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

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景