【小白开发】安卓射击游戏开发——Canvas工具的应用

这学期期中的时候,我被朋友介绍一个老师,这个老师在开发一款射击游戏,让我帮他做界面。秉持着有任务就接下,边学边做的原则,我开始了为期一个月的开发。下面是我对这个项目的总结(资源已上传,可以去我上传的资源查看下载)资源下载(虽然用安卓来实现这个比较鸡肋)
好了,开始介绍。
首先第一步就是布局,布局其实没什么好说的了,我这里的布局其实最基本的就是分了两个ImageView用来当作主画布和游戏时计分表的画布,在加上一个Chronometer用来计时,里面的VideoView是一个视频播放器,用于打中后靶子倒下视频的实现,这里先暂不考虑,毕竟这是需要一个专门的动画,但是我用了一个小段的视频去实验了,是可以实现的,不多说了,直接上代码和效果图吧

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.mypaint.MainActivity">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/bg"
        android:background="@drawable/photo3">

    </ImageView>

    <Chronometer
        android:id="@+id/chronometer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:textSize="20dp"
        android:textColor="#FFFFFF">

    </Chronometer>

<!--    <VideoView-->
<!--        android:id="@+id/bazi2"-->
<!--        android:layout_marginLeft="140px"-->
<!--        android:layout_marginTop="140px"-->
<!--        android:layout_height="300px"-->
<!--        android:layout_width="300px">-->

<!--    </VideoView>-->

    <ImageView
        android:id="@+id/view"
        android:background="#3A3A3A"
        android:layout_alignParentRight="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    </ImageView>

</RelativeLayout>

开始界面
游戏界面
结束界面
布局差不多了,接下来就是最主要的功能实现。最开始的设想是通过蓝牙连接,将设备连入电视等大屏幕,在用专门的射击工具来获取点坐标,到那时由于在开发过程中,老师那边的硬件有点问题,所以我就改成了用点击来获取坐标。
在游戏的开始界面,通过点击任意处进入游戏,然后显示靶子以及计分表,并开始计时。通过点击屏幕来达成射击效果,每打出一枪就画出分值以及击中区域。规定每十枪作为一轮,打完十枪之后就进行清除靶子以及计分表上的数据,并统计总分数以及总时间,算出总得分率。

在这里插入图片描述
那么综上所述,功能实现的第一步就是应该考虑靶子以及点。我这里的靶子是比较常见的圆靶,分四个区域:C区2分,B区5分,A区8分,靶心10分。而且为了营造出靶近靶远的效果,就必须去调整靶子的大小。但是这样的话这么多个靶子,如果每个都要去进行逻辑一样只是具体参数不一样的判断,就会造成我们的代码冗长且杂乱。所以我们就必须去自定义两个类:靶子类和点类。
其中靶子对应的代码如下:

public class Target {
    private float ca,cb,cr;//靶子圆心坐标以及半径
    public Target ( float ca, float cb,float cr)
    {
        this.setCa(ca);
        this.setCb(cb);
        this.setCr(cr);
    }

    public void setCa(float ca){
        this.ca = ca;
    }

    public void setCb(float cb) {
        this.cb = cb ;
    }

    public void setCr(float cr){
        this.cr = cr ;
    }

/** **/
    public float getCa(){
        return this.ca ;
    }
    public float getCb(){
        return this.cb;
    }

    public float getCr(){
        return this.cr ;
    }

/** 
 * 
 * 在画布上画上靶子
 * 
 * **/

    public Bitmap draw(Canvas canvas, Paint paint,Target target,Bitmap alterBitmap){

        paint.setColor(Color.WHITE);
        canvas.drawCircle(target.getCa(),target.getCb(),target.getCr(),paint);
        paint.setColor(Color.BLUE);
        canvas.drawCircle(target.getCa(),target.getCb(), (float) (target.getCr()*0.8),paint);
        paint.setColor(Color.WHITE);
        canvas.drawCircle(target.getCa(),target.getCb(), (float) (target.getCr()*0.5),paint);
        paint.setColor(Color.BLUE);
        canvas.drawCircle(target.getCa(),target.getCb(), (float) (target.getCr()*0.1),paint);

        return alterBitmap;
    }

}

点的话其实不当作一个类也是可以的,只是为了方便和可读性。代码如下:

public class Point {
    private int grade,num;//点对应的分数以及标号
    private String where;//点对应的区域

    public Point ( int grade,int num,String where)
    {
        this.setGrade(grade);
        this.setNum(num);
        this.setWhere(where);
    }

    public void setGrade(int grade){
        this.grade = grade;
    }

    public void setNum(int num) {
        this.num = num ;
    }

    public void setWhere(String where){
        this.where = where ;
    }

    /** **/
    public int getGrade(){
        return this.grade ;
    }
    public int getNum(){
        return this.num;
    }

    public String getWhere(){
        return this.where;
    }

    /** **/

}

好,做到这准备工作就差不多了。接下来开始画画!主要代码如下,先对射击次数进行判断,如果还没有进行射击,则应该是显示的开始界面。当开始射击也就是选择开始之后,这个时候number还是等于0,开始画上靶子以及计分表并开始计时,同时通过MediaPlayer进行枪击声的同步播放以及枪击点的标记,并调用 is_hit 函数进行判断是否击中,代码如下:

      if ( number == 0 ){
            //刚开始玩游戏
            paint.setColor(Color.WHITE);
            paint.setTextSize(120);
            canvas.drawText("点击此处开始游戏吧",bitmap.getWidth()/2,bitmap.getHeight()/2,paint);
            canvas.drawBitmap(alterBitmap, new Matrix(), paint);
            imageView.setImageBitmap(alterBitmap);
        }

        imageView.setOnTouchListener(new View.OnTouchListener() {
            int startX;
            int startY;

            @Override
            public boolean onTouch(View v, MotionEvent event) {

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        if (number == 0) {
                            canvas.drawColor(0, PorterDuff.Mode.CLEAR); //清空画布
                            showTable();//画计分表
                            //画靶子一
                            target.draw(canvas, paint, target, alterBitmap);

                            //画靶子二
                            target1.draw(canvas, paint, target1, alterBitmap);

                            //画靶子三
                            target2.draw(canvas, paint, target2, alterBitmap);

                            // 先将背景画上
                            canvas.drawBitmap(alterBitmap, new Matrix(), paint);
                            imageView.setImageBitmap(alterBitmap);

                            //开始计时
                            chronometer.setBase(SystemClock.elapsedRealtime());
                            chronometer.start();
                        }

                        //创建MediaPlayer,设置数据源
                        mediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.voice);

                        //设置声音流类型
                        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

                        mediaPlayer.start();

                        // 获取手按下时的坐标
                        startX = (int) event.getX();
                        startY = (int) event.getY();

                        if (number != 0){
                            //number=1时为点击开始游戏射出的一枪,不显示红点
                            paint.setColor(Color.RED);
                            canvas.drawCircle(startX, startY, 15, paint);
                            imageView.setImageBitmap(alterBitmap);
                            //判断是否打中,多个靶时只会击到一个靶,所以如果把这个判断放到is_hit函数里面会有误判的情况
                            if (is_hit(startX, startY, target) == false && is_hit(startX, startY, target1) == false && is_hit(startX, startY, target2) == false ) {
                                where = "未击中靶子";
                                message(0, where);
                            }
                        }else number++;
                        break;
//
//                   case MotionEvent.ACTION_UP:
//                       canvas.drawColor(0, PorterDuff.Mode.CLEAR); //清空画布
//                       break;

                }

                return true;
            }
        });

与此同时is_hit函数的代码如下:

   /**
     * 判断是否击中靶子 分为4个计分区域 分别为2,5,8,10分
     * 1.方形靶:判断左上角与右下角
     * 2.圆形靶:判断击中点与圆心的距离
     * 3.不规则靶
     **/
    public boolean is_hit(int x, int y, Target cTarget) {

        boolean is = true;
        double e = Math.pow(x - cTarget.getCa(), 2);  //点与圆心的距离
        double f = Math.pow(y - cTarget.getCb(), 2);

        if (e + f > Math.pow(cTarget.getCr(), 2)) {
            is = false; //如果大于半径的平方,则判断为未击中靶
        } else {
            //正方形(长方形)时,分别比较这个点与左上角与左下角的关系,有多少个区域就比较多少次,不需要嵌套
            //圆形时,比较点与圆心的距离
            double k = Math.pow(0.8 * cTarget.getCr(), 2);
            double l = Math.pow(0.5 * cTarget.getCr(), 2);  //两个区域
            mark = 2;
            if (e + f <= k) {
                mark += 3;
                if (e + f <= l) {

                    mark += 3;
                    if (e + f <= Math.pow(0.1 * cTarget.getCr(), 2)) {
                        mark = 10;
                        where = "击中靶心";
                        message(mark, where);
                    } else {
                        where = "击中A区";
                        message(mark, where);
                    }
                } else {
                    where = "击中B区";
                    message(mark, where);
                }

            } else {
                where = "击中C区";
                message(mark, where);
            }
        }
        return is;
    }

通过message函数来进行分数的绘制,每一次刷新界面其实也就是将计分表重新绘制了一遍,至于为什么选择以Canvas的形式来展示计分表,是因为老师说这样避免了分辨率导致表格变形的问题。
函数代码如下:

   /**
     * 刷新表格信息
     * 将击中分数存储起来
     * 每射击一次,总分数,以及得分率会刷新(重新建表);计分表也会随之刷新
     * 总时间是一直变化
     **/
    ArrayList<Point> list = new ArrayList<Point>();
    private Point point;
    public void message(int mark, String where) {

        number += 1;
        //规定只能射十发
        if( number == 11 ){
            chronometer.stop(); //停止计时
            canvas.drawColor(0, PorterDuff.Mode.CLEAR); //清空画布

            double totalss = 0;
            String time = chronometer.getText().toString();
            String[] split = time.split(":");
            String string3 = split[0];
            double min = Integer.parseInt(string3);
            double Mins =min*60;
            double  SS =Integer.parseInt(split[1]);
            totalss = Mins+SS;
            double k = all_mark/totalss;//得分率

            DecimalFormat    df   = new DecimalFormat("######0.00");//取后两位小数
            paint.setColor(Color.WHITE);
            paint.setTextAlign(Paint.Align.CENTER);
            canvas.drawText("耗时为"+time,bitmap.getWidth()/2,bitmap.getHeight()/2-100,paint);
            canvas.drawText("总分数为:"+all_mark+"分",bitmap.getWidth()/2,bitmap.getHeight()/2+100,paint);
            canvas.drawText("得分率为:"+df.format(k)+"%",bitmap.getWidth()/2,bitmap.getHeight()/2+300,paint);

            imageView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch (event.getAction()){
                        case MotionEvent.ACTION_DOWN:
                            chronometer.setBase(SystemClock.elapsedRealtime());
                            number = 0;
                            all_mark = 0;
                            list.clear();
                            showDraw();
                            showTable();
                            break;
                    }
                    return true;
                }
            });
        }
        //number=1时为点击开始游戏射出的一枪,不记录
        if (number != 1){
            point = new Point(mark, number-1, where);
            list.add(point);
            all_mark += mark;
            showGrade(c,number-1);
        }
    }

}

这就是几个最主要的功能函数,其实这个游戏从头到尾就只有一个界面,这些变化都是通过Canvas来实现的(将Canvas运用到极致哈哈哈)
感觉实现起来还是比较好实现的,主要的问题就是**点击的点在屏幕显示的时候偏差比较大,还没有得到解决。**如果大家有想法可以一起来讨论解决。
**如果有帮助的话,给我一个赞吧~**下面是效果展示:
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值