用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);
                }
*/
        }

}

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

原文地址:https://www.cnblogs.com/tomboy/p/2685484.html