让我们结合之前学到的东西,实现一个小demo。
该demo的描述如下:
在一个九曲十八弯的小路上,一个僵尸冒着大雪前行。最后雪停了,僵尸高兴的跳起了骑马舞。
分析需求:
在一个九曲十八弯的小路上
地图制作(使用Tiled制作)
一个僵尸冒着大雪前行
粒子系统
最后雪停了
粒子系统的控制
僵尸高兴的跳起了骑马舞
声音引擎
地图素材如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YuY24Qy-1577980144404)(G:\cocos2.x视频\植物大战僵尸资料\植物大战僵尸\地图制作\bk1.jpg)]
下载Tiled软件,教程可上网搜索,这里将地图制作成如下图所示即可,注意:
![]()
标签中的source属性,改成与地图图片名相同即可package com.example.cocos2ddemo;
import org.cocos2d.layers.CCLayer;
/**
* 实现demo的图层
*/
public class DemoLayer extends CCLayer {
public 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();
@Override
protected 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); // 设置最高帧率位60
director.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); // 导演运行场景
}
@Override
protected void onResume() {
super.onResume();
director.resume(); // 游戏继续
}
@Override
protected void onPause() {
super.onPause();
director.pause(); // 游戏暂停
}
@Override
protected void onDestroy() {
super.onDestroy();
director.end(); // 游戏结束
}
}
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"); // 根据名字,找到objectGroup
ArrayList<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);
}
}
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"); // 根据名字,找到objectGroup
ArrayList<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);
}
}
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,0
map.setPosition(ccp(map.getContentSize().width/2,map.getContentSize().height/2)); // 设置位置为地图的中心点
mPoints = new ArrayList<>(); // 初始化所有的坐标点
CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroup
ArrayList<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
*/
@Override
public boolean ccTouchesMoved(MotionEvent event) {
map.touchMove(event,map); // 地图移动
return super.ccTouchesMoved(event);
}
}
粒子系统表示三维计算机图形学中模拟一些特定的模糊现象的技术,而这些现象用其它传统的渲染技术难以实现的真实感的 game physics。经常使用粒子系统模拟的现象有火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉效果等等。
为了实现下雪的效果,需要使用到CCParticleSystem这个类来实现。
在进行实践前,导入以下资源到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.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,0
map.setPosition(ccp(map.getContentSize().width/2,map.getContentSize().height/2)); // 设置位置为地图的中心点
mPoints = new ArrayList<>(); // 初始化所有的坐标点
CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroup
ArrayList<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
*/
@Override
public boolean ccTouchesMoved(MotionEvent event) {
map.touchMove(event,map); // 地图移动
return super.ccTouchesMoved(event);
}
}
PS:这里作者在试验粒子系统的时候system.setTexture(CCTextureCache.sharedTextureCache().addImage("snow.png"));
这行代码报出了空指针异常,然后模拟器闪退。但作者检查了几遍后仍没有发现问题,在assets目录下确实有snow.png这张图片。若有读者对这块有了解的话希望读者可以帮忙修改一下此处的Bug,感激不尽!
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,0
map.setPosition(ccp(map.getContentSize().width/2,map.getContentSize().height/2)); // 设置位置为地图的中心点
mPoints = new ArrayList<>(); // 初始化所有的坐标点
CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroup
ArrayList<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
*/
@Override
public boolean ccTouchesMoved(MotionEvent event) {
map.touchMove(event,map); // 地图移动
return super.ccTouchesMoved(event);
}
}
一般来说,游戏都会提供一个按钮,用于让玩家在游玩时随时随地地暂停。接下来的实践就是要完成这一点。为了方便起见,这里要达到的效果是点击屏幕的任意位置都会停下。
暂停时,会出现以下标识:
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();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//调用顺序:视图(CCGLSurfaceView) -》 导演(CCDirector) -》 场景(CCScene) -》 图层(CCLayer) -》 精灵(CCSprite) -》 动作(CCMove)
// 获取视图
CCGLSurfaceView view = new CCGLSurfaceView(this); // 创建一个SurfaceView,类似导演眼前的小屏幕,必须传this,底层要强转成Actvity
setContentView(view);
// 获取导演的单例对象
director.attachInView(view); // 开启绘制线程的方法
director.setDisplayFPS(true); // 显示帧率,表示每秒刷新页面的次数。一般当帧率大于30帧时,基本上人眼看起来比较流畅,帧率和手机性能与程序性能有关
director.setAnimationInterval(1/60f); // 设置最高帧率位60
director.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); // 导演运行场景
}
@Override
protected void onResume() {
super.onResume();
director.resume(); // 游戏继续
SoundEngine.sharedEngine().resumeSound(); // 音乐继续
}
@Override
protected void onPause() {
super.onPause();
director.pause(); // 游戏暂停
SoundEngine.sharedEngine().pauseSound(); // 音乐暂停
}
@Override
protected void onDestroy() {
super.onDestroy();
director.end(); // 游戏结束
}
}
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,0
map.setPosition(ccp(map.getContentSize().width/2,map.getContentSize().height/2)); // 设置位置为地图的中心点
mPoints = new ArrayList<>(); // 初始化所有的坐标点
CCTMXObjectGroup road = map.objectGroupNamed("road"); // 根据名字,找到objectGroup
ArrayList<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
*/
@Override
public boolean ccTouchesMoved(MotionEvent event) {
map.touchMove(event,map); // 地图移动
return super.ccTouchesMoved(event);
}
/**
* 点击屏幕,游戏暂停
* @param event
* @return
*/
@Override
public 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
*/
@Override
public boolean ccTouchesBegan(MotionEvent event) {
CGPoint point = convertTouchToNodeSpace(event);
// 判断心脏图标是否被点击
if (CGRect.containsPoint(heart.getBoundingBox(),point)){
DemoLayer.this.onEnter(); // 游戏继续
this.removeSelf(); // 删除暂停的图层
}
return super.ccTouchesBegan(event);
}
}
}