Cocos2d游戏开发学习记录——3.实现僵尸跳舞demo

2023-11-28 18:40

本文主要是介绍Cocos2d游戏开发学习记录——3.实现僵尸跳舞demo,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1.案例介绍
  • 2.地图制作
  • 3.实践一
  • 4.粒子系统
  • 5.实践二
  • 6.游戏暂停
  • 7.实践三
  • 8.总结

1.案例介绍

让我们结合之前学到的东西,实现一个小demo。

该demo的描述如下:

在一个九曲十八弯的小路上,一个僵尸冒着大雪前行。最后雪停了,僵尸高兴的跳起了骑马舞。

分析需求

  • 在一个九曲十八弯的小路上

    地图制作(使用Tiled制作)

  • 一个僵尸冒着大雪前行

    粒子系统

  • 最后雪停了

    粒子系统的控制

  • 僵尸高兴的跳起了骑马舞

    声音引擎

2.地图制作

地图素材如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YuY24Qy-1577980144404)(G:\cocos2.x视频\植物大战僵尸资料\植物大战僵尸\地图制作\bk1.jpg)]

下载Tiled软件,教程可上网搜索,这里将地图制作成如下图所示即可,注意:

  • 注意修改相关属性,尽量使用英文名
  • 将地图文件和图片一起放到项目中的assets目录下,修改地图文件的<img>标签中的source属性,改成与地图图片名相同即可

在这里插入图片描述

3.实践一

  1. 新建DemoLayer,继承CCLayer,并编写构造方法,用于实现该demo,代码如下:
package com.example.cocos2ddemo;import org.cocos2d.layers.CCLayer;/*** 实现demo的图层*/
public class DemoLayer extends CCLayer {public DemoLayer() {}
}
  1. 修改MainActivity,使用DemoLayer来作为新的图层,代码如下:
package com.example.cocos2ddemo;import android.app.Activity;
import android.os.Bundle;import org.cocos2d.layers.CCScene;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.opengl.CCGLSurfaceView;public class MainActivity extends Activity {/*** 导演*/CCDirector director = CCDirector.sharedDirector();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//调用顺序:视图(CCGLSurfaceView) -》 导演(CCDirector) -》 场景(CCScene) -》 图层(CCLayer) -》 精灵(CCSprite) -》 动作(CCMove)// 获取视图CCGLSurfaceView view = new CCGLSurfaceView(this); // 创建一个SurfaceView,类似导演眼前的小屏幕setContentView(view);// 获取导演的单例对象director.attachInView(view); // 开启绘制线程的方法director.setDisplayFPS(true); // 显示帧率,表示每秒刷新页面的次数。一般当帧率大于30帧时,基本上人眼看起来比较流畅,帧率和手机性能与程序性能有关director.setAnimationInterval(1/60f); // 设置最高帧率位60director.setDeviceOrientation(CCDirector.kCCDeviceOrientationLandscapeLeft); // 设置屏幕方式为横屏显示director.setScreenSize(480,320); // 设置分辨率,用于屏幕适配,会基于不同大小的屏幕等比例缩放,设置我们开发时候的分辨率// 获取场景对象CCScene scene = CCScene.node();// 获取图层对象//FirstLayer layer = new FirstLayer();//ActionLayer layer = new ActionLayer();DemoLayer layer = new DemoLayer();// 配置环境scene.addChild(layer); // 给场景添加图层director.runWithScene(scene); // 导演运行场景}@Overrideprotected void onResume() {super.onResume();director.resume(); // 游戏继续}@Overrideprotected void onPause() {super.onPause();director.pause(); // 游戏暂停}@Overrideprotected void onDestroy() {super.onDestroy();director.end(); // 游戏结束}
}
  1. 修改DemoLayer,首先获取地图,并且获取地图上那些节点的坐标,并且添加行走动画播放方法walk()(可在之前的博客中copy过来)和僵尸移动方法moveNext(暂时只行走到下一个点),注意一些细节信息的配置,代码如下:
package com.example.cocos2ddemo;import org.cocos2d.actions.base.CCRepeatForever;
import org.cocos2d.actions.interval.CCAnimate;
import org.cocos2d.actions.interval.CCMoveTo;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCTMXObjectGroup;
import org.cocos2d.layers.CCTMXTiledMap;
import org.cocos2d.nodes.CCAnimation;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.nodes.CCSpriteFrame;
import org.cocos2d.types.CGPoint;import java.util.ArrayList;
import java.util.HashMap;/*** 实现demo的图层*/
public class DemoLayer extends CCLayer {/*** 坐标点的集合*/private ArrayList<CGPoint> mPoints;/*** 僵尸*/private CCSprite sprite;public DemoLayer() {sprite = CCSprite.sprite("z_1_01.png");sprite.setFlipX(true);sprite.setScale(0.5);sprite.setAnchorPoint(0.5f,0); // 设置锚点为僵尸的两脚之间loadMap();}/*** 加载地图*/private void loadMap(){CCTMXTiledMap map = CCTMXTiledMap.tiledMap("map.tmx"); // 加载地图mPoints = new ArrayList<>(); // 初始化所有的坐标点CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroupArrayList<HashMap<String, String>> objects = road.objects; // 所有坐标点的集合for (HashMap<String, String> hashMap : objects) {Integer x = Integer.parseInt(hashMap.get("x"));Integer y = Integer.parseInt(hashMap.get("y"));mPoints.add(ccp(x,y));}this.addChild(map);sprite.setPosition(mPoints.get(0)); // 设置僵尸的初始位置this.addChild(sprite);walk(); // 播放僵尸行走的动画moveNext();// 开始行走}private void moveNext(){CCMoveTo move = CCMoveTo.action(2,mPoints.get(1));sprite.runAction(move);}/*** 图层(僵尸)行走*/private void walk(){// 初始化7帧图片ArrayList<CCSpriteFrame> frames = new ArrayList<>();String format = "z_1_%02d.png"; // %02d表示两位数字,如果是个位,用0去补位(01,02);如果是十位,则不用补位(10,11)// 初始化7帧图片for (int i = 1; i <= 7 ; i++) {frames.add(CCSprite.sprite(String.format(format,i)).displayedFrame());}CCAnimation animation = CCAnimation.animation("walk",.2f,frames); // 第二个参数表示每一帧显示时间CCAnimate animate = CCAnimate.action(animation);CCRepeatForever repeat = CCRepeatForever.action(animate); // 表示动画永远循环,若不循环则会报出空指针异常sprite.runAction(repeat);}
}
  1. 修改DemoLayer,完善僵尸行走的方法,使其可以走到每一个获取到的节点上,注意三个地方:
    • 为了递归调用moveNext方法,首先使用了index来计量所有节点的数量,然后再调用CCSequence来封装了CCCallFunc方法,这个方法是一个反射方法,可以以递归的形式来反复调用moveNext方法
    • 为了顺利使用CCCallFunc方法来进行反射调用,需要将moveNext()方法的访问修饰符改为public
    • 为了使僵尸行走在每一段路上时都是均衡的速度,调用了CGPointUtil.distance来计算两个节点的距离,再设定僵尸的速度,使用 距离/速度 就可以得到僵尸走每段路所平均需要的时间
    • 代码如下:
package com.example.cocos2ddemo;import org.cocos2d.actions.base.CCRepeatForever;
import org.cocos2d.actions.instant.CCCallFunc;
import org.cocos2d.actions.interval.CCAnimate;
import org.cocos2d.actions.interval.CCMoveTo;
import org.cocos2d.actions.interval.CCSequence;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCTMXObjectGroup;
import org.cocos2d.layers.CCTMXTiledMap;
import org.cocos2d.nodes.CCAnimation;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.nodes.CCSpriteFrame;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.util.CGPointUtil;import java.util.ArrayList;
import java.util.HashMap;/*** 实现demo的图层*/
public class DemoLayer extends CCLayer {/*** 坐标点的集合*/private ArrayList<CGPoint> mPoints;/*** 僵尸*/private CCSprite sprite;/*** 僵尸当前的位置*/private int index;/*** 僵尸移动的速度*/private int speed = 50;public DemoLayer() {sprite = CCSprite.sprite("z_1_01.png");sprite.setFlipX(true);sprite.setScale(0.5);sprite.setAnchorPoint(0.5f,0); // 设置锚点为僵尸的两脚之间loadMap();}/*** 加载地图*/private void loadMap(){CCTMXTiledMap map = CCTMXTiledMap.tiledMap("map.tmx"); // 加载地图mPoints = new ArrayList<>(); // 初始化所有的坐标点CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroupArrayList<HashMap<String, String>> objects = road.objects; // 所有坐标点的集合for (HashMap<String, String> hashMap : objects) {Integer x = Integer.parseInt(hashMap.get("x"));Integer y = Integer.parseInt(hashMap.get("y"));mPoints.add(ccp(x,y));}this.addChild(map);sprite.setPosition(mPoints.get(0)); // 设置僵尸的初始位置this.addChild(sprite);walk(); // 播放僵尸行走的动画moveNext();// 开始行走}/*** 如果要通过反射(CCCallFunc.action)调用此方法,必须将访问修改符设置为public*/public void moveNext(){index++;if (index < mPoints.size()){CCMoveTo move = CCMoveTo.action(CGPointUtil.distance(mPoints.get(index - 1),mPoints.get(index)) / speed,mPoints.get(index)); // 通过CGPointUtil.distance计算两个坐标点的距离,再除以速度,就可以得到每一段路所花费的时间,即匀速前进CCSequence sequence = CCSequence.actions(move,CCCallFunc.action(this,"moveNext"));// 通过反射调用方法,实现类似于递归的循环sprite.runAction(sequence);}else {System.out.println("僵尸走到终点了......");}}/*** 图层(僵尸)行走*/private void walk(){// 初始化7帧图片ArrayList<CCSpriteFrame> frames = new ArrayList<>();String format = "z_1_%02d.png"; // %02d表示两位数字,如果是个位,用0去补位(01,02);如果是十位,则不用补位(10,11)// 初始化7帧图片for (int i = 1; i <= 7 ; i++) {frames.add(CCSprite.sprite(String.format(format,i)).displayedFrame());}CCAnimation animation = CCAnimation.animation("walk",.2f,frames); // 第二个参数表示每一帧显示时间CCAnimate animate = CCAnimate.action(animation);CCRepeatForever repeat = CCRepeatForever.action(animate); // 表示动画永远循环,若不循环则会报出空指针异常sprite.runAction(repeat);}
}
  1. 修改DemoLayer,因为地图在手机屏幕上显示不全,所以需要修改相应信息,并且重写ccTouchesMoved()方法,实现拖拽屏幕时可以查看整张地图。注意:
    • 需要打开CCLayer的可触控事件开关,因为默认是关闭的
    • 让僵尸与图层进行绑定,不然拖拽地图时僵尸不跟着地图移动(将this.addChild(sprite)修改成map.addChild(sprite))
    • 代码如下:
package com.example.cocos2ddemo;import android.view.MotionEvent;import org.cocos2d.actions.base.CCRepeatForever;
import org.cocos2d.actions.instant.CCCallFunc;
import org.cocos2d.actions.interval.CCAnimate;
import org.cocos2d.actions.interval.CCMoveTo;
import org.cocos2d.actions.interval.CCSequence;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCTMXObjectGroup;
import org.cocos2d.layers.CCTMXTiledMap;
import org.cocos2d.nodes.CCAnimation;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.nodes.CCSpriteFrame;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.util.CGPointUtil;import java.util.ArrayList;
import java.util.HashMap;/*** 实现demo的图层*/
public class DemoLayer extends CCLayer {/*** 地图*/private CCTMXTiledMap map;/*** 坐标点的集合*/private ArrayList<CGPoint> mPoints;/*** 僵尸*/private CCSprite sprite;/*** 僵尸当前的位置*/private int index;/*** 僵尸移动的速度*/private int speed = 50;public DemoLayer() {sprite = CCSprite.sprite("z_1_01.png");sprite.setFlipX(true);sprite.setScale(0.5);sprite.setAnchorPoint(0.5f,0); // 设置锚点为僵尸的两脚之间loadMap();// 打开触摸事件开关setIsTouchEnabled(true);}/*** 加载地图*/private void loadMap(){map = CCTMXTiledMap.tiledMap("map.tmx"); // 加载地图map.setAnchorPoint(0.5f,0.5f); // 为了保证地图可以正常触摸,将地图锚点设置为中心位置,默认为0,0map.setPosition(ccp(map.getContentSize().width/2,map.getContentSize().height/2)); // 设置位置为地图的中心点mPoints = new ArrayList<>(); // 初始化所有的坐标点CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroupArrayList<HashMap<String, String>> objects = road.objects; // 所有坐标点的集合for (HashMap<String, String> hashMap : objects) {Integer x = Integer.parseInt(hashMap.get("x"));Integer y = Integer.parseInt(hashMap.get("y"));mPoints.add(ccp(x,y));}this.addChild(map);sprite.setPosition(mPoints.get(0)); // 设置僵尸的初始位置map.addChild(sprite);walk(); // 播放僵尸行走的动画moveNext();// 开始行走}/*** 如果要通过反射(CCCallFunc.action)调用此方法,必须将访问修改符设置为public*/public void moveNext(){index++;if (index < mPoints.size()){CCMoveTo move = CCMoveTo.action(CGPointUtil.distance(mPoints.get(index - 1),mPoints.get(index)) / speed,mPoints.get(index)); // 通过CGPointUtil.distance计算两个坐标点的距离,再除以速度,就可以得到每一段路所花费的时间,即匀速前进CCSequence sequence = CCSequence.actions(move,CCCallFunc.action(this,"moveNext"));// 通过反射调用方法,实现类似于递归的循环sprite.runAction(sequence);}else {System.out.println("僵尸走到终点了......");}}/*** 图层(僵尸)行走*/private void walk(){// 初始化7帧图片ArrayList<CCSpriteFrame> frames = new ArrayList<>();String format = "z_1_%02d.png"; // %02d表示两位数字,如果是个位,用0去补位(01,02);如果是十位,则不用补位(10,11)// 初始化7帧图片for (int i = 1; i <= 7 ; i++) {frames.add(CCSprite.sprite(String.format(format,i)).displayedFrame());}CCAnimation animation = CCAnimation.animation("walk",.2f,frames); // 第二个参数表示每一帧显示时间CCAnimate animate = CCAnimate.action(animation);CCRepeatForever repeat = CCRepeatForever.action(animate); // 表示动画永远循环,若不循环则会报出空指针异常sprite.runAction(repeat);}/*** 响应触摸移动事件* @param event* @return*/@Overridepublic boolean ccTouchesMoved(MotionEvent event) {map.touchMove(event,map); // 地图移动return super.ccTouchesMoved(event);}
}

4.粒子系统

粒子系统表示三维计算机图形学中模拟一些特定的模糊现象的技术,而这些现象用其它传统的渲染技术难以实现的真实感的 game physics。经常使用粒子系统模拟的现象有火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉效果等等。

为了实现下雪的效果,需要使用到CCParticleSystem这个类来实现。

在进行实践前,导入以下资源到assets目录下:

在这里插入图片描述

5.实践二

  1. 修改DemoLayer,添加particleSystem()方法,用于让场景中出现下雪效果,代码如下:
package com.example.cocos2ddemo;import android.view.MotionEvent;import org.cocos2d.actions.base.CCRepeatForever;
import org.cocos2d.actions.instant.CCCallFunc;
import org.cocos2d.actions.interval.CCAnimate;
import org.cocos2d.actions.interval.CCMoveTo;
import org.cocos2d.actions.interval.CCSequence;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCTMXObjectGroup;
import org.cocos2d.layers.CCTMXTiledMap;
import org.cocos2d.nodes.CCAnimation;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.nodes.CCSpriteFrame;
import org.cocos2d.nodes.CCTextureCache;
import org.cocos2d.particlesystem.CCParticleSnow;
import org.cocos2d.particlesystem.CCParticleSystem;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.util.CGPointUtil;import java.util.ArrayList;
import java.util.HashMap;/*** 实现demo的图层*/
public class DemoLayer extends CCLayer {/*** 地图*/private CCTMXTiledMap map;/*** 坐标点的集合*/private ArrayList<CGPoint> mPoints;/*** 僵尸*/private CCSprite sprite;/*** 僵尸当前的位置*/private int index;/*** 僵尸移动的速度*/private int speed = 50;/*** 粒子系统*/private CCParticleSystem system;public DemoLayer() {sprite = CCSprite.sprite("z_1_01.png");sprite.setFlipX(true);sprite.setScale(0.5);sprite.setAnchorPoint(0.5f,0); // 设置锚点为僵尸的两脚之间loadMap();// 打开触摸事件开关setIsTouchEnabled(true);}/*** 加载地图*/private void loadMap(){map = CCTMXTiledMap.tiledMap("map.tmx"); // 加载地图map.setAnchorPoint(0.5f,0.5f); // 为了保证地图可以正常触摸,将地图锚点设置为中心位置,默认为0,0map.setPosition(ccp(map.getContentSize().width/2,map.getContentSize().height/2)); // 设置位置为地图的中心点mPoints = new ArrayList<>(); // 初始化所有的坐标点CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroupArrayList<HashMap<String, String>> objects = road.objects; // 所有坐标点的集合for (HashMap<String, String> hashMap : objects) {Integer x = Integer.parseInt(hashMap.get("x"));Integer y = Integer.parseInt(hashMap.get("y"));mPoints.add(ccp(x,y));}this.addChild(map);sprite.setPosition(mPoints.get(0)); // 设置僵尸的初始位置map.addChild(sprite); // 把僵尸添加到地图//CCFollow follow = CCFollow.action(sprite); //参数表示跟随的对象//map.runAction(follow); // 地图跟随僵尸移动,如果实现地图跟随效果,需要把地点的锚点和位置改为默认值,不要手动设置walk(); // 播放僵尸行走的动画moveNext();// 开始行走//particleSystem(); // 粒子系统——下雪,报出空指针异常,不知道该怎么解决}/*** 粒子系统*/private void particleSystem(){system= CCParticleSnow.node(); // 下雪//CCParticleSystem system = CCParticleExplosion.node();//system.setScale(2); // 设置雪花大小//system.setSpeed(10); // 设置雪花速度system.setTexture(CCTextureCache.sharedTextureCache().addImage("snow.png")); // 设置雪花图片this.addChild(system, 1);}/*** 如果要通过反射(CCCallFunc.action)调用此方法,必须将访问修改符设置为public*/public void moveNext(){index++;if (index < mPoints.size()){CCMoveTo move = CCMoveTo.action(CGPointUtil.distance(mPoints.get(index - 1),mPoints.get(index)) / speed,mPoints.get(index)); // 通过CGPointUtil.distance计算两个坐标点的距离,再除以速度,就可以得到每一段路所花费的时间,即匀速前进CCSequence sequence = CCSequence.actions(move,CCCallFunc.action(this,"moveNext"));// 通过反射调用方法,实现类似于递归的循环sprite.runAction(sequence);}else {System.out.println("僵尸走到终点了......");system.stopSystem(); // 停止粒子系统}}/*** 图层(僵尸)行走*/private void walk(){// 初始化7帧图片ArrayList<CCSpriteFrame> frames = new ArrayList<>();String format = "z_1_%02d.png"; // %02d表示两位数字,如果是个位,用0去补位(01,02);如果是十位,则不用补位(10,11)// 初始化7帧图片for (int i = 1; i <= 7 ; i++) {frames.add(CCSprite.sprite(String.format(format,i)).displayedFrame());}CCAnimation animation = CCAnimation.animation("walk",.2f,frames); // 第二个参数表示每一帧显示时间CCAnimate animate = CCAnimate.action(animation);CCRepeatForever repeat = CCRepeatForever.action(animate); // 表示动画永远循环,若不循环则会报出空指针异常sprite.runAction(repeat);}/*** 响应触摸移动事件* @param event* @return*/@Overridepublic boolean ccTouchesMoved(MotionEvent event) {map.touchMove(event,map); // 地图移动return super.ccTouchesMoved(event);}
}

PS:这里作者在试验粒子系统的时候system.setTexture(CCTextureCache.sharedTextureCache().addImage("snow.png"));这行代码报出了空指针异常,然后模拟器闪退。但作者检查了几遍后仍没有发现问题,在assets目录下确实有snow.png这张图片。若有读者对这块有了解的话希望读者可以帮忙修改一下此处的Bug,感激不尽!

  1. 修改DemoLayer,添加骑马舞方法dance()。注意:声音文件要放在res/raw目录下,而不是assets目录下,代码如下:
package com.example.cocos2ddemo;import android.view.MotionEvent;import org.cocos2d.actions.base.CCRepeatForever;
import org.cocos2d.actions.instant.CCCallFunc;
import org.cocos2d.actions.interval.CCAnimate;
import org.cocos2d.actions.interval.CCJumpBy;
import org.cocos2d.actions.interval.CCMoveTo;
import org.cocos2d.actions.interval.CCRotateBy;
import org.cocos2d.actions.interval.CCSequence;
import org.cocos2d.actions.interval.CCSpawn;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCTMXObjectGroup;
import org.cocos2d.layers.CCTMXTiledMap;
import org.cocos2d.nodes.CCAnimation;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.nodes.CCSpriteFrame;
import org.cocos2d.nodes.CCTextureCache;
import org.cocos2d.particlesystem.CCParticleSnow;
import org.cocos2d.particlesystem.CCParticleSystem;
import org.cocos2d.sound.SoundEngine;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.util.CGPointUtil;import java.util.ArrayList;
import java.util.HashMap;/*** 实现demo的图层*/
public class DemoLayer extends CCLayer {/*** 地图*/private CCTMXTiledMap map;/*** 坐标点的集合*/private ArrayList<CGPoint> mPoints;/*** 僵尸*/private CCSprite sprite;/*** 僵尸当前的位置*/private int index;/*** 僵尸移动的速度*/private int speed = 50;/*** 粒子系统*/private CCParticleSystem system;public DemoLayer() {sprite = CCSprite.sprite("z_1_01.png");sprite.setFlipX(true);sprite.setScale(0.5);sprite.setAnchorPoint(0.5f,0); // 设置锚点为僵尸的两脚之间loadMap();// 打开触摸事件开关setIsTouchEnabled(true);}/*** 加载地图*/private void loadMap(){map = CCTMXTiledMap.tiledMap("map.tmx"); // 加载地图map.setAnchorPoint(0.5f,0.5f); // 为了保证地图可以正常触摸,将地图锚点设置为中心位置,默认为0,0map.setPosition(ccp(map.getContentSize().width/2,map.getContentSize().height/2)); // 设置位置为地图的中心点mPoints = new ArrayList<>(); // 初始化所有的坐标点CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroupArrayList<HashMap<String, String>> objects = road.objects; // 所有坐标点的集合for (HashMap<String, String> hashMap : objects) {Integer x = Integer.parseInt(hashMap.get("x"));Integer y = Integer.parseInt(hashMap.get("y"));mPoints.add(ccp(x,y));}this.addChild(map);sprite.setPosition(mPoints.get(0)); // 设置僵尸的初始位置map.addChild(sprite); // 把僵尸添加到地图//CCFollow follow = CCFollow.action(sprite); //参数表示跟随的对象//map.runAction(follow); // 地图跟随僵尸移动,如果实现地图跟随效果,需要把地点的锚点和位置改为默认值,不要手动设置walk(); // 播放僵尸行走的动画moveNext();// 开始行走//particleSystem(); // 粒子系统——下雪,报出空指针异常,不知道该怎么解决}/*** 粒子系统*/private void particleSystem(){system= CCParticleSnow.node(); // 下雪//CCParticleSystem system = CCParticleExplosion.node();//system.setScale(2); // 设置雪花大小//system.setSpeed(10); // 设置雪花速度system.setTexture(CCTextureCache.sharedTextureCache().addImage("snow.png")); // 设置雪花图片this.addChild(system, 1);}/*** 如果要通过反射(CCCallFunc.action)调用此方法,必须将访问修改符设置为public*/public void moveNext(){index++;if (index < mPoints.size()){CCMoveTo move = CCMoveTo.action(CGPointUtil.distance(mPoints.get(index - 1),mPoints.get(index)) / speed,mPoints.get(index)); // 通过CGPointUtil.distance计算两个坐标点的距离,再除以速度,就可以得到每一段路所花费的时间,即匀速前进CCSequence sequence = CCSequence.actions(move,CCCallFunc.action(this,"moveNext"));// 通过反射调用方法,实现类似于递归的循环sprite.runAction(sequence);}else {System.out.println("僵尸走到终点了......");system.stopSystem(); // 停止粒子系统dance();}}/*** 图层(僵尸)跳舞*/private void dance(){sprite.setAnchorPoint(0.5f,0.5f);CCJumpBy jump = CCJumpBy.action(1,ccp(-20,10),20,3);CCRotateBy rotate = CCRotateBy.action(1,360);CCSpawn spawn = CCSpawn.actions(jump,rotate);CCSequence sequence = CCSequence.actions(spawn,spawn.reverse());CCRepeatForever repeat = CCRepeatForever.action(sequence);sprite.runAction(repeat);SoundEngine engine = SoundEngine.sharedEngine();engine.playSound(CCDirector.theApp,R.raw.psy,true);}/*** 图层(僵尸)行走*/private void walk(){// 初始化7帧图片ArrayList<CCSpriteFrame> frames = new ArrayList<>();String format = "z_1_%02d.png"; // %02d表示两位数字,如果是个位,用0去补位(01,02);如果是十位,则不用补位(10,11)// 初始化7帧图片for (int i = 1; i <= 7 ; i++) {frames.add(CCSprite.sprite(String.format(format,i)).displayedFrame());}CCAnimation animation = CCAnimation.animation("walk",.2f,frames); // 第二个参数表示每一帧显示时间CCAnimate animate = CCAnimate.action(animation);CCRepeatForever repeat = CCRepeatForever.action(animate); // 表示动画永远循环,若不循环则会报出空指针异常sprite.runAction(repeat);}/*** 响应触摸移动事件* @param event* @return*/@Overridepublic boolean ccTouchesMoved(MotionEvent event) {map.touchMove(event,map); // 地图移动return super.ccTouchesMoved(event);}
}

6.游戏暂停

一般来说,游戏都会提供一个按钮,用于让玩家在游玩时随时随地地暂停。接下来的实践就是要完成这一点。为了方便起见,这里要达到的效果是点击屏幕的任意位置都会停下。

暂停时,会出现以下标识:

在这里插入图片描述

7.实践三

  1. 修改MainActivity,在onResume()方法和onPause()方法中分别添加让音乐恢复的逻辑和让音乐停止的逻辑,代码如下:
package com.example.cocos2ddemo;import android.app.Activity;
import android.os.Bundle;import org.cocos2d.layers.CCScene;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.opengl.CCGLSurfaceView;
import org.cocos2d.sound.SoundEngine;public class MainActivity extends Activity {/*** 导演*/CCDirector director = CCDirector.sharedDirector();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//调用顺序:视图(CCGLSurfaceView) -》 导演(CCDirector) -》 场景(CCScene) -》 图层(CCLayer) -》 精灵(CCSprite) -》 动作(CCMove)// 获取视图CCGLSurfaceView view = new CCGLSurfaceView(this); // 创建一个SurfaceView,类似导演眼前的小屏幕,必须传this,底层要强转成ActvitysetContentView(view);// 获取导演的单例对象director.attachInView(view); // 开启绘制线程的方法director.setDisplayFPS(true); // 显示帧率,表示每秒刷新页面的次数。一般当帧率大于30帧时,基本上人眼看起来比较流畅,帧率和手机性能与程序性能有关director.setAnimationInterval(1/60f); // 设置最高帧率位60director.setDeviceOrientation(CCDirector.kCCDeviceOrientationLandscapeLeft); // 设置屏幕方式为横屏显示director.setScreenSize(480,320); // 设置分辨率,用于屏幕适配,会基于不同大小的屏幕等比例缩放,设置我们开发时候的分辨率// 获取场景对象CCScene scene = CCScene.node();// 获取图层对象//FirstLayer layer = new FirstLayer();//ActionLayer layer = new ActionLayer();DemoLayer layer = new DemoLayer();// 配置环境scene.addChild(layer); // 给场景添加图层director.runWithScene(scene); // 导演运行场景}@Overrideprotected void onResume() {super.onResume();director.resume(); // 游戏继续SoundEngine.sharedEngine().resumeSound(); // 音乐继续}@Overrideprotected void onPause() {super.onPause();director.pause(); // 游戏暂停SoundEngine.sharedEngine().pauseSound(); // 音乐暂停}@Overrideprotected void onDestroy() {super.onDestroy();director.end(); // 游戏结束}
}
  1. 修改DemoLayer,重写ccTouchesBegan()方法,添加游戏暂停的逻辑,并且新增内部类PauseLayer,用于在点击屏幕的任意处时,出现一个心脏图标,来提示游戏暂停,然后再次点击图标时,游戏继续。代码如下:
package com.example.cocos2ddemo;import android.view.MotionEvent;import org.cocos2d.actions.base.CCRepeatForever;
import org.cocos2d.actions.instant.CCCallFunc;
import org.cocos2d.actions.interval.CCAnimate;
import org.cocos2d.actions.interval.CCJumpBy;
import org.cocos2d.actions.interval.CCMoveTo;
import org.cocos2d.actions.interval.CCRotateBy;
import org.cocos2d.actions.interval.CCSequence;
import org.cocos2d.actions.interval.CCSpawn;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCTMXObjectGroup;
import org.cocos2d.layers.CCTMXTiledMap;
import org.cocos2d.nodes.CCAnimation;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.nodes.CCSpriteFrame;
import org.cocos2d.nodes.CCTextureCache;
import org.cocos2d.particlesystem.CCParticleSnow;
import org.cocos2d.particlesystem.CCParticleSystem;
import org.cocos2d.sound.SoundEngine;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.CGRect;
import org.cocos2d.types.CGSize;
import org.cocos2d.types.util.CGPointUtil;import java.util.ArrayList;
import java.util.HashMap;/*** 实现demo的图层*/
public class DemoLayer extends CCLayer {/*** 地图*/private CCTMXTiledMap map;/*** 坐标点的集合*/private ArrayList<CGPoint> mPoints;/*** 僵尸*/private CCSprite sprite;/*** 僵尸当前的位置*/private int index;/*** 僵尸移动的速度*/private int speed = 50;/*** 粒子系统*/private CCParticleSystem system;public DemoLayer() {sprite = CCSprite.sprite("z_1_01.png");sprite.setFlipX(true);sprite.setScale(0.5);sprite.setAnchorPoint(0.5f,0); // 设置锚点为僵尸的两脚之间loadMap();// 打开触摸事件开关setIsTouchEnabled(true);}/*** 加载地图*/private void loadMap(){map = CCTMXTiledMap.tiledMap("map.tmx"); // 加载地图map.setAnchorPoint(0.5f,0.5f); // 为了保证地图可以正常触摸,将地图锚点设置为中心位置,默认为0,0map.setPosition(ccp(map.getContentSize().width/2,map.getContentSize().height/2)); // 设置位置为地图的中心点mPoints = new ArrayList<>(); // 初始化所有的坐标点CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroupArrayList<HashMap<String, String>> objects = road.objects; // 所有坐标点的集合for (HashMap<String, String> hashMap : objects) {Integer x = Integer.parseInt(hashMap.get("x"));Integer y = Integer.parseInt(hashMap.get("y"));mPoints.add(ccp(x,y));}this.addChild(map);sprite.setPosition(mPoints.get(0)); // 设置僵尸的初始位置map.addChild(sprite); // 把僵尸添加到地图//CCFollow follow = CCFollow.action(sprite); //参数表示跟随的对象//map.runAction(follow); // 地图跟随僵尸移动,如果实现地图跟随效果,需要把地点的锚点和位置改为默认值,不要手动设置walk(); // 播放僵尸行走的动画moveNext();// 开始行走//particleSystem(); // 粒子系统——下雪,报出空指针异常,不知道该怎么解决}/*** 粒子系统*/private void particleSystem(){system= CCParticleSnow.node(); // 下雪//CCParticleSystem system = CCParticleExplosion.node();//system.setScale(2); // 设置雪花大小//system.setSpeed(10); // 设置雪花速度system.setTexture(CCTextureCache.sharedTextureCache().addImage("snow.png")); // 设置雪花图片this.addChild(system, 1);}/*** 如果要通过反射(CCCallFunc.action)调用此方法,必须将访问修改符设置为public*/public void moveNext(){index++;if (index < mPoints.size()){CCMoveTo move = CCMoveTo.action(CGPointUtil.distance(mPoints.get(index - 1),mPoints.get(index)) / speed,mPoints.get(index)); // 通过CGPointUtil.distance计算两个坐标点的距离,再除以速度,就可以得到每一段路所花费的时间,即匀速前进CCSequence sequence = CCSequence.actions(move,CCCallFunc.action(this,"moveNext"));// 通过反射调用方法,实现类似于递归的循环sprite.runAction(sequence);}else {System.out.println("僵尸走到终点了......");dance();system.stopSystem(); // 停止粒子系统}}/*** 图层(僵尸)跳舞*/private void dance(){sprite.setAnchorPoint(0.5f,0.5f);CCJumpBy jump = CCJumpBy.action(1,ccp(-20,10),20,3);CCRotateBy rotate = CCRotateBy.action(1,360);CCSpawn spawn = CCSpawn.actions(jump,rotate);CCSequence sequence = CCSequence.actions(spawn,spawn.reverse());CCRepeatForever repeat = CCRepeatForever.action(sequence);sprite.runAction(repeat);SoundEngine engine = SoundEngine.sharedEngine();engine.playSound(CCDirector.theApp,R.raw.psy,true);}/*** 图层(僵尸)行走*/private void walk(){// 初始化7帧图片ArrayList<CCSpriteFrame> frames = new ArrayList<>();String format = "z_1_%02d.png"; // %02d表示两位数字,如果是个位,用0去补位(01,02);如果是十位,则不用补位(10,11)// 初始化7帧图片for (int i = 1; i <= 7 ; i++) {frames.add(CCSprite.sprite(String.format(format,i)).displayedFrame());}CCAnimation animation = CCAnimation.animation("walk",.2f,frames); // 第二个参数表示每一帧显示时间CCAnimate animate = CCAnimate.action(animation);CCRepeatForever repeat = CCRepeatForever.action(animate); // 表示动画永远循环,若不循环则会报出空指针异常sprite.runAction(repeat);}/*** 响应触摸移动事件* @param event* @return*/@Overridepublic boolean ccTouchesMoved(MotionEvent event) {map.touchMove(event,map); // 地图移动return super.ccTouchesMoved(event);}/*** 点击屏幕,游戏暂停* @param event* @return*/@Overridepublic boolean ccTouchesBegan(MotionEvent event) {this.onExit(); // 游戏暂停this.getParent().addChild(new PauseLayer()); // 给父控件添加一个暂停的图层return super.ccTouchesBegan(event);}/*** 暂停图层*/class PauseLayer extends CCLayer{/*** 心脏图标*/private CCSprite heart;public PauseLayer() {heart = CCSprite.sprite("heart.png");CGSize winSize = CCDirector.sharedDirector().winSize();heart.setPosition(winSize.width / 2,winSize.height / 2);this.addChild(heart);setIsTouchEnabled(true); // 打开点击事件}/*** 监听心脏图标的方法* @param event* @return*/@Overridepublic boolean ccTouchesBegan(MotionEvent event) {CGPoint point = convertTouchToNodeSpace(event);// 判断心脏图标是否被点击if (CGRect.containsPoint(heart.getBoundingBox(),point)){DemoLayer.this.onEnter(); // 游戏继续this.removeSelf(); // 删除暂停的图层}return super.ccTouchesBegan(event);}}
}

8.总结

到这里,这个小demo就基本完成了,下篇博客将正式介绍移动端《植物大战僵尸》的制作。
在这里插入图片描述
在这里插入图片描述

这篇关于Cocos2d游戏开发学习记录——3.实现僵尸跳舞demo的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/430095

相关文章

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

Python MySQL如何通过Binlog获取变更记录恢复数据

《PythonMySQL如何通过Binlog获取变更记录恢复数据》本文介绍了如何使用Python和pymysqlreplication库通过MySQL的二进制日志(Binlog)获取数据库的变更记录... 目录python mysql通过Binlog获取变更记录恢复数据1.安装pymysqlreplicat

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

Python xmltodict实现简化XML数据处理

《Pythonxmltodict实现简化XML数据处理》Python社区为提供了xmltodict库,它专为简化XML与Python数据结构的转换而设计,本文主要来为大家介绍一下如何使用xmltod... 目录一、引言二、XMLtodict介绍设计理念适用场景三、功能参数与属性1、parse函数2、unpa

C#实现获得某个枚举的所有名称

《C#实现获得某个枚举的所有名称》这篇文章主要为大家详细介绍了C#如何实现获得某个枚举的所有名称,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... C#中获得某个枚举的所有名称using System;using System.Collections.Generic;usi