device-2016-12-28-162117.png今天通过关卡模拟学习绘制敌机以及敌机出场的动画,我随意写了一些简单的逻辑算法,差不多26种模式,其实也是主要使用<a href="http://www.jianshu.com/p/1114eca3393b">DrawEnemy</a>来绘制敌机出场逻辑。
1.创建一个管理关卡的抽象类
public abstract class BaseLevel {
private Context context;
public BaseLevel(Context context,Object... objects) {
this.context = context;
initialize(objects);
}
public abstract void initialize(Object... objects);
public abstract Map<Integer,Integer> enemyLevelArray(int enemyArrayIndex);
public Context getContext() {
return context;
}
}```
######2.建立游戏的第一关(整个游戏我也只写了一关),创建Level1类继承上面的抽象类,有两个方法解释一下。
<b>initialize()</b>关卡初始化,比如当前关卡需要用到的各种资源。
<b>enemyLevelArray(int enemyArrayIndex)</b>每次循环都会来调用此方法获取当前需要出场的敌机,不能一次性加入画布中,不然会很卡,甚至奔溃(oom内存溢出)。
初始化的方法没有什么特点需要介绍,主要说说获取敌机出场的思路。
/**
* 获取每一波分配好的敌机集合
* @param enemyArrayIndex
* @return
*/
@Override
public Map<Integer,Integer> enemyLevelArray(int enemyArrayIndex) {
Map<Integer,Integer> tempMap = new HashMap<>();
switch (enemyArrayIndex){
case 0:
tempMap.put(DrawEnemy.TYPE_E,3);
tempMap.put(DrawEnemy.TYPE_A,4);
break;
case 1:
tempMap.put(DrawEnemy.TYPE_E,3);
tempMap.put(DrawEnemy.TYPE_D,4);
break;
case 2:
tempMap.put(DrawEnemy.TYPE_E,3);
tempMap.put(DrawEnemy.TYPE_B,4);
break;
case 3:
tempMap.put(DrawEnemy.TYPE_E,3);
tempMap.put(DrawEnemy.TYPE_C,4);
break;
case 4:
tempMap.put(DrawEnemy.TYPE_E,3);
tempMap.put(DrawEnemy.TYPE_F,4);
break;
case 5:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_G,3);
break;
case 6:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_H,4);
break;
case 7:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_I,4);
break;
case 8:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_A,2);
tempMap.put(DrawEnemy.TYPE_J,4);
break;
case 9:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_D,2);
tempMap.put(DrawEnemy.TYPE_K,4);
break;
case 10:
tempMap.put(DrawEnemy.TYPE_L,4);
break;
case 11:
tempMap.put(DrawEnemy.TYPE_M,4);
break;
case 12:
tempMap.put(DrawEnemy.TYPE_G,4);
break;
case 13:
tempMap.put(DrawEnemy.TYPE_E,4);
break;
case 14:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_N,4);
break;
case 15:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_O,4);
break;
case 16:
tempMap.put(DrawEnemy.TYPE_E,4);
break;
case 17:
tempMap.put(DrawEnemy.TYPE_P,4);
break;
case 18:
tempMap.put(DrawEnemy.TYPE_Q,4);
break;
case 19:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_R,4);
break;
case 20:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_S,4);
break;
case 21:
tempMap.put(DrawEnemy.TYPE_T,1);
tempMap.put(DrawEnemy.TYPE_U,1);
break;
case 22:
tempMap.put(DrawEnemy.TYPE_A,4);
tempMap.put(DrawEnemy.TYPE_V,4);
break;
case 23:
tempMap.put(DrawEnemy.TYPE_D,4);
tempMap.put(DrawEnemy.TYPE_W,4);
break;
case 24:
tempMap.put(DrawEnemy.TYPE_X,4);
break;
case 25:
tempMap.put(DrawEnemy.TYPE_Y,1);
break;
case 26:
tempMap.put(DrawEnemy.TYPE_Z,1);
break;
case 27:
tempMap.put(DrawEnemy.TYPE_T,1);
tempMap.put(DrawEnemy.TYPE_U,1);
break;
case 28:
tempMap.put(DrawEnemy.TYPE_E,4);
tempMap.put(DrawEnemy.TYPE_Y,4);
break;
case 29:
tempMap.put(DrawEnemy.TYPE_H,4);
tempMap.put(DrawEnemy.TYPE_I,4);
break;
case 30:
tempMap.put(DrawEnemy.TYPE_J,4);
tempMap.put(DrawEnemy.TYPE_K,4);
break;
case 31:
tempMap.put(DrawEnemy.TYPE_L,4);
tempMap.put(DrawEnemy.TYPE_M,4);
break;
case 32:
tempMap.put(DrawEnemy.TYPE_B,4);
tempMap.put(DrawEnemy.TYPE_C,4);
break;
case 33:
tempMap.put(DrawEnemy.TYPE_L,4);
tempMap.put(DrawEnemy.TYPE_M,4);
break;
case 34:
tempMap.put(DrawEnemy.TYPE_P,5);
break;
case 35:
tempMap.put(DrawEnemy.TYPE_Q,5);
break;
case 36:
tempMap.put(DrawEnemy.TYPE_N,5);
break;
case 37:
tempMap.put(DrawEnemy.TYPE_O,5);
break;
case 38:
tempMap.put(DrawEnemy.TYPE_N,5);
tempMap.put(DrawEnemy.TYPE_O,5);
break;
case 39:
tempMap.put(DrawEnemy.TYPE_B,5);
tempMap.put(DrawEnemy.TYPE_C,5);
break;
case 40:
tempMap.put(DrawEnemy.TYPE_J,5);
tempMap.put(DrawEnemy.TYPE_K,5);
tempMap.put(DrawEnemy.TYPE_H,5);
tempMap.put(DrawEnemy.TYPE_I,5);
break;
}
return tempMap;
}```
上面的方法我一共模拟了41次的敌机出场,方法的里面的数字判断0到40就是从第一次到最后的按顺序出场的。每一个case下的内容可以把他们都看成一个组,每组敌机加入画布的时间间隔通过createEnemyTime来设置
/** * 控制敌机加入的时间间隔 */
private int mCountEnemy;
private int createEnemyTime=100;
每一个组是一个Map,Map的key放敌机的类型,value放这个敌机类型(key)需要出场的数量;每一组需要出场几种不同类型的敌机就通过put多种类型的敌机。
tempMap.put(DrawEnemy.TYPE_J,5);
tempMap.put(DrawEnemy.TYPE_K,5);
tempMap.put(DrawEnemy.TYPE_H,5);
tempMap.put(DrawEnemy.TYPE_I,5);
3.出场的敌机准备好以后,迭代Map把所有的敌机装入一个list集合。
Paste_Image.png游戏总是需要结束的或者进行到最后,所以我们也需要设置一个该关卡敌机最多出现多少波,现在可能体现用处不大,但是后面涉及到大BOSS的时候会用到,所以先写出来备用。
/** * 当前关卡最多出现多少波敌机 */
private int maxEnemy=40;
4.回到GameView类中使用调用关卡类。
- 定义关卡相关的变量
- 新增一个方法来处理敌机出场逻辑。
/**
* 分配敌机资源
*/
public void distribution(){
if(mLevelArray!=null){
mLevelArray.clear();
}
boolean senior = seniorEnemy();
if(senior) return;
//不是高级敌机出场就去获取普通敌机
mLevelArray = mLevel.enemyLevelArray(mLevel.getEnemyArrayIndex());
}```
这里有一个seniorEnemy的方法,也是新增一个方法,该方法主要是表示高级敌机出场的时候,其他普通敌机暂时不需要加入,也就是说这时候画布中只有主角和高级敌机在PK,其他敌机等待高级敌机被消亡在继续添加。
/**
* 高级敌机出场
*/
private boolean seniorEnemy(){
for (DrawEnemy en:mLevel.getEnemyList()) {
//当高级将领出现 其余战机 处于待命状态,并且降低背景循环运行的速度,一个视觉差的感觉
if(en.getEnemyType()==DrawEnemy.TYPE_T||en.getEnemyType()==DrawEnemy.TYPE_U){
mDrawBackground.setSpeedBY(4);
return true;
}else if(en.getEnemyType()==DrawEnemy.TYPE_Y){
mDrawBackground.setSpeedBY(4);
return true;
}
}
mDrawBackground.setSpeedBY(10);
return false;
}```
其中mDrawBackground.setSpeedBY(10)是设置当进入高级敌机Pk的时候变更背景的移动速度来增加游戏视觉效果和游戏气氛。
5.新增onDrawEnemy方法用于绘制敌机和处理敌机移动以及动画效果。
/**
* 绘制敌机
* 判断敌机是否失效
*/
private void onDrawEnemy(){
mLevel.addEnemy(mLevelArray);//添加敌机
List<DrawEnemy> drawEnemies = mLevel.getEnemyList();
for (int i=0;i<drawEnemies.size();i++) {
DrawEnemy en = drawEnemies.get(i);
if(en.isDead()){
drawEnemies.remove(en);
}else{
en.updateGame();
en.onDraw(mCanvas);
if((en.getEnemyType()==DrawEnemy.TYPE_V ||en.getEnemyType()==DrawEnemy.TYPE_W)&&en.isEnemyStopTop() ||en.getEnemyType()==DrawEnemy.TYPE_J ||en.getEnemyType()==DrawEnemy.TYPE_K){//追踪主角的战机
en.getAngleRotate(mPlayer.getPlayerX(), mPlayer.getPlayerY(),true);
}else if(en.getEnemyType()==DrawEnemy.TYPE_A||en.getEnemyType()==DrawEnemy.TYPE_D||en.getEnemyType()==DrawEnemy.TYPE_E||en.getEnemyType()==DrawEnemy.TYPE_P||en.getEnemyType()==DrawEnemy.TYPE_Q||en.getEnemyType()==DrawEnemy.TYPE_T||en.getEnemyType()==DrawEnemy.TYPE_U){
en.getAngleRotate(mPlayer.getPlayerX(), mPlayer.getPlayerY(),false);
}
}
}
}```
其中用到了DrawEnemy类中的getAngleRotate方法,该方法的用处<a href="http://www.jianshu.com/p/1114eca3393b">上一篇</a>已经说过,这里就不作解释了。
######6.在GameView中的onGameDraw方法调用onDrawEnemy方法
![Paste_Image.png](https://img.haomeiwen.com/i3982371/3f213a0ac148eb7c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
关卡类的东西较多,其实大部分都是针对敌机出场的逻辑判断,在出场顺序和算法的地方有点绕,最开始我用的二维数组实现的,写完后连我自己都有点晕,所以改成Map比较好理解一些,但是可能拓展性不够强,希望在后期能够找到更好的优化方式,我们还是主要学些canvas为主,算法不是我的强项。
![enemy.gif](https://img.haomeiwen.com/i3982371/3dc7e966f5f96f7c.gif?imageMogr2/auto-orient/strip)
<a href="https://github.com/tangyxgit/GameCanvas">源码</a>已经通过git更新,下一篇给敌机配备武器。
<a href="http://www.jianshu.com/p/1114eca3393b">上一篇</a> <a href="http://www.jianshu.com/p/b4c2d19a4f9f">下一篇</a>
网友评论