zoukankan      html  css  js  c++  java
  • 用SurfaceView制作简单的android游戏 : 重力小球(3)制作障碍物以及使其旋转

    接下来将要制作下图的障碍物

    添加障碍物的类Barricade.java (继承task)

    View Code
    public class Barricade extends Task {
    
        public enum eType { // 设置障碍物类型
            OUT, // 碰到就lose
            GOAL // 碰到就win
        }
    
        protected PointF _center = new PointF(0, 0); // 图形的中心点
        protected PointF _pt[]; // 图形的顶点
        protected Paint _paint = new Paint(); // paint
        protected eType _type; // 障碍物的类型
        protected float _rotaSpeed = 0; // 旋转速度
    
        // 构造函数, type=类型, n=顶点数, conf=设置信息
        public Barricade(int n, BConf conf) {
            if (conf != null) {
                _rotaSpeed = conf.speed; // 旋转速度
                _type = conf.type; // 物体的类型
            }
            switch (_type) {
            case OUT:
                _paint.setColor(Color.RED);
                break;
            case GOAL:
                _paint.setColor(Color.GREEN);
                break;
            }
            // 顶点数组生成, 以此构造出各种多边形障碍物
            _pt = new PointF[n]; 
            for (int i = 0; i < n; i++) {
                _pt[i] = new PointF();
            }
        }
    
        // 更新
        public boolean onUpdate() {
            if (_rotaSpeed != 0) {
                 // 顶点数组围绕中心点旋转
                DiagramCalcr.RotateDiagram(_pt, _center, _rotaSpeed);
            }
            return true;
        }
    
        // 小球与障碍物的碰撞检测判定, 
        public eHitCode isHit(final Circle cir, Vec vec) {
            //当各顶点形成的线段跟小球反生碰撞的时候
            if (DiagramCalcr.isHit(_pt, cir, vec) == true) {
                switch (_type) {
                case OUT:
                    return Def.eHitCode.OUT;
                case GOAL:
                    return Def.eHitCode.GOAL;
                }
            }
            return Def.eHitCode.NO;
        }
    
        // 画出物体
        public void onDraw(Canvas c) {
            // 防止顶点数量为1以下
            if (_pt.length < 1) { 
                return;
            }
            //path为轨迹, 可以用于描绘用户碰触屏幕所产生的轨迹
            Path path = new Path();
            path.moveTo(_pt[0].x, _pt[0].y); // 初始化path的位置
            for (int i = 0; i < _pt.length; i++) {
                path.lineTo(_pt[i].x, _pt[i].y); // 向顶点位置引线
            }
            c.drawPath(path, _paint); // 画出引线
        }
    
    }

    Def.java (碰撞的信息属性, 分别为nothing, lose, win)

    public class Def {
            public enum eHitCode{
                    NO,
                    OUT,
                    GOAL,
            }
    }

    BConf.java (障碍物的设置信息参数, 包括速度, 类型)

    View Code
    public class BConf {
        public float speed = (float) Math.PI / 180;
        public Barricade.eType type = Barricade.eType.OUT;
    
        public BConf(Barricade.eType atype) {
            type = atype;
        }
    
        public BConf(float aspeed) {
            speed = aspeed;
        }
    }

    Line.java (各顶点之间形成的线段,线段的起点座标以及线段的水平大小跟垂直大小)

    View Code
    public class Line {
        public float _x, _y; //初始座标
        public float _sx, _sy; //线段的长度
    
        public Line(float x1, float y1, float x2, float y2) {
            _x = x1;
            _y = y1;
            _sx = x2 - x1;
            _sy = y2 - y1;
        }
    
        public Line() {
            _x = _y = _sx = _sy = 0;
        }
    }

    DiagramCalcr.java (很重要的类, 用以计算障碍物旋转以及碰撞检测的, 计算障碍物旋转的公式可以参照上图)

    View Code
    public class DiagramCalcr {
        // 顶点群以center为中心,进行角度为ang的旋转
        public static void RotateDiagram(PointF pt[], final PointF center,
                final float ang) {
            for (int i = 0; i < pt.length; i++) {
                RotatePt(pt[i], center, ang);
            }
        }
    
        // 顶点的旋转公式
        public static void RotatePt(PointF rotaPt, final PointF origPt,
                final float ang) {
            float cx = origPt.x;
            float cy = origPt.y;
            float x = rotaPt.x;
            float y = rotaPt.y;
            rotaPt.x = (float) (cx + Math.cos(ang) * (x - cx) - Math.sin(ang)
                    * (y - cy));
            rotaPt.y = (float) (cy + Math.sin(ang) * (x - cx) + Math.cos(ang)
                    * (y - cy));
        }
    
        // 碰撞检测判定方法
        public static boolean isHit(PointF pt[], final Circle cir, Vec vec) {
            if (pt.length < 2) {
                return false;
            }
            int len = pt.length;
            for (int i = 1; i <= len; i++) {
                //用取余数的方法使0-1,1-2,2-3,3-0得以循环
                Line line = new Line(pt[i - 1].x, pt[i - 1].y, pt[i % len].x, pt[i
                        % len].y);
                //检测到碰撞的话把该线段的方向存储在vec里面
                if (isHitLC(line, cir) == true) {
                    vec._x = pt[i % len].x - pt[i - 1].x;
                    vec._y = pt[i % len].y - pt[i - 1].y;
                    return true;
                }
            }
            return false;
        }
    
        // 线段与小圆球的碰撞检测函数
        //同一坐标系内,(任意两点的距离)的平方 = (x1-x2)的平方 + (y1-y2)的平方
        //圆和直线的位置关系有3种:相交,相切,相离
        public static boolean isHitLC(Line L,Circle C) {
            //条件1
            if ((L._sx * (C._x - L._x) + L._sy * (C._y - L._y)) <= 0) {
                return (C._r * C._r >= (C._x - L._x) * (C._x - L._x)
                        + (C._y - L._y) * (C._y - L._y));
                //条件2
            } else if (((-L._sx) * (C._x - (L._x + L._sx)) + (-L._sy)
                    * (C._y - (L._y + L._sy))) <= 0) {
                return (C._r * C._r >= (C._x - (L._x + L._sx))
                        * (C._x - (L._x + L._sx)) + (C._y - (L._y + L._sy))
                        * (C._y - (L._y + L._sy)));
            } else {
                //条件3
                //e为线段的长度
                float e = (float) Math.sqrt(L._sx * L._sx + L._sy * L._sy);
                //c2为(圆心到线段起点的距离)的平方
                float c2 = (C._x - L._x) * (C._x - L._x) + (C._y - L._y)
                        * (C._y - L._y);
                float b = (C._x - L._x) * (L._sx / e) + (C._y - L._y) * (L._sy / e);
                return (C._r * C._r >= c2 - b * b);
            }
        }
    }

    如下图所示, S和E分别表示坐标系中某直线L的起点和终点, L1为经过起点S并垂直于L的直线, L2为经过终点E并垂直于L的直线

    代码中的条件1是指圆心在下图蓝色范围时, 条件2是指圆心在绿色范围时, 条件3为圆心在红色范围内的情况

    其中条件1和条件2的返回值用了(计算坐标系内任意两点的距离的公式)

    对条件3的返回值的求证计算如下图(当圆心在红色范围内的时候, 由圆心往直线上的某一点引线的时候总能垂直于直线)

    终于把最复杂的部分布置好了,接下来就可以轻松地往里面添加各种障碍物了

    试试添加矩形的障碍物吧,添加一个新类名为BarricadeSquare.java (继承Barricade)

    View Code
    //正方形障碍物, 原点座标, 宽, 高
    public class BarricadeSquare extends Barricade {
    
        public BarricadeSquare(float x, float y, float w, float h, BConf conf) {
            super(4, conf);
            _pt[0].x = x;
            _pt[0].y = y;
            _pt[1].x = x + w;
            _pt[1].y = y;
            _pt[2].x = x + w;
            _pt[2].y = y + h;
            _pt[3].x = x;
            _pt[3].y = y + h;
            _center.x = x + w / 2;
            _center.y = y + h / 2;
        }
    
    }

    最后把GameMgr.java作一次大更新,代码如下, 用/**/包围起来的代码为更新了的代码

    因为本机的分辨率只有480X320, 所以障碍物位置需要根据真机分辨率自行调整

    View Code
    public class GameMgr {
    
    //用来装task的容器
            private LinkedList<Task> _taskList = new LinkedList<Task>();
    
    /*
    // 圆周率
        private static final float PI = (float) Math.PI;
    
    // 用来装矩形障碍物的容器
        private ArrayList<Barricade> _barrList = new ArrayList<Barricade>();
    
    // 添加玩家
        private Player _player;
    */
    
    //添加task        
            GameMgr(){
    /*
    // 障碍物生成
            barricadeAdd();
    
    // 添加障碍物到tasklist
            for (Barricade bar : _barrList) {
                _taskList.add(bar);
            }
    */
    
                    _player = new Player();
                    _taskList.add(_player);
                    _taskList.add( new FpsController() );
            }
    
    /*
        public void barricadeAdd() {
            // 生成边框障碍物
            //square参数:起始座标, 宽, 高
            _barrList.add(new BarricadeSquare(0, 0, 310, 10, new BConf(0)));
            _barrList.add(new BarricadeSquare(0, 0, 10, 480, new BConf(0)));
            _barrList.add(new BarricadeSquare(310, 0, 10, 480, new BConf(0)));
            _barrList.add(new BarricadeSquare(0, 470, 310, 10, new BConf(0)));
    
            // 顺时针旋转棒子
            _barrList.add(new BarricadeSquare(20, 200, 280, 10, new BConf(+PI / 180)));
            // 逆时针旋转棒子
            _barrList.add(new BarricadeSquare(20, 200, 280, 10, new BConf(-PI / 180)));
        }
    */
            
    
    //逐一更新tasklist里面的任务
            public boolean onUpdate() {
                    for(int i=0; i<_taskList.size(); i++){
    //更新失败的话就把task移除
                            if(_taskList.get(i).onUpdate() == false){
                                    _taskList.remove(i);            
                                    i--;
                            }
                    }
                    return true;
            }
    
    //逐一描画
            public void onDraw(Canvas c) {
    //把屏幕涂白,相当于清屏的作用
                    c.drawColor(Color.WHITE); 
    /*      
                    for (Task task : _taskList) {
                            task.onDraw(c);
                    }
    */
            }
    
    }

    到真机上测试的话,如无意外能得下图

  • 相关阅读:
    Andriod 部署Cocos2d-x项目到Eclipse中
    Andriod 在MAC上搭建开发环境--连接真机测试
    XCode5 破解 免证书连接真机调试
    NSURLConnection 的神奇之处
    NSOperationQueue、NSOperation理解
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/tomboy/p/2685484.html
Copyright © 2011-2022 走看看