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

相关文章

Django开发时如何避免频繁发送短信验证码(python图文代码)

《Django开发时如何避免频繁发送短信验证码(python图文代码)》Django开发时,为防止频繁发送验证码,后端需用Redis限制请求频率,结合管道技术提升效率,通过生产者消费者模式解耦业务逻辑... 目录避免频繁发送 验证码1. www.chinasem.cn避免频繁发送 验证码逻辑分析2. 避免频繁

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali