接下来将要制作下图的障碍物
添加障碍物的类Barricade.java (继承task)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 (障碍物的设置信息参数, 包括速度, 类型)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 (各顶点之间形成的线段,线段的起点座标以及线段的水平大小跟垂直大小)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 (很重要的类, 用以计算障碍物旋转以及碰撞检测的, 计算障碍物旋转的公式可以参照上图)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//正方形障碍物, 原点座标, 宽, 高 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, 所以障碍物位置需要根据真机分辨率自行调整
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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); } */ } }
到真机上测试的话,如无意外能得下图