参考CocosEditor管网提供的,运黄金完整源码:https://github.com/makeapp/cocoseditor-java-goldship
1.点击按钮弹窗代码
@Bind("pausedBtn")
public Button pausedBtn;
@Bind("pausedBtn")
@Action(Action.ActionType.WidgetTouchUp)
public void onPausedClicked(Ref ref) {
Logger.log("paused");
setTouchEnabled(false);
pausedBtn.setTouchEnabled(false);
NodeReader.create().showNode(owner, "layouts/pause.cce");
Director.getInstance().pause();
}
1.1 游戏结束自动弹窗
//游戏结束后弹出的窗口
//游戏结束窗口弹出后,暂停按钮不能点击
//屏幕触摸失效,游戏暂停
private void showGameLayer() {
final ControllerMessage controller = (ControllerMessage)
NodeReader.create().showNode(owner, "layout/GameOver.cce");
controller.score = Integer.parseInt(score.getString());
setTouchEnabled(false);
pauseButton.setTouchEnabled(false);
this.playGame = false;
// FunctionFactory.callFunction("ad", "show Interstitial");
}
2.java项目内部,全局调用自定义函数
调用
@Bind("sharing")
@Action(Action.ActionType.WidgetTouchUp)
public void onShareTouched(Ref ref) {
FunctionFactory.callFunction("weixin", "sendSession appData 运黄金 我得了" + score + "个黄金,你也来试试手气.");
}
注册
FunctionFactory.callFunction("ad", "show Banner Top " + width + " " + height);
// FunctionFactory.callFunction("ad", "show Banner Top");
FunctionFactory.registerFunction("onApplicationDidEnterBackground", new Function() {
@Override
public Object apply(Object o) {
onStop();
return null;
}
});
3.碰撞检测
设置Scene的参数为true。
包括创建监听器GroundAndGoldContact和注册监听器addPhysicsContactListner()
//给陆地加入碰撞监听器以及参数
//陆地本身的位掩码setCategoryBitmask(十六进制)
//与陆地所要接触的对象的位掩码setContactTestBitmask(十六进制)
private void setGroundCollsition() {
PhysicsBody gBody = groundSprite.getPhysicsBody();
gBody.setCategoryBitmask(0x08);
gBody.setContactTestBitmask(0x02);
groundSprite.addPhysicsContactListener(new GroundAndGoldContact());
}
/**
* 创建陆地和金子碰撞监听类
* 实现物理相交监听这个类里面的方法
* 首先,注册要使用的方法,用的的方法都要先注册
* 如:regContactBegin();
* *
*/
public class GroundAndGoldContact
extends PhysicsContactListener {
{
regContactBegin();
}
@Override
public boolean onContactBegin(PhysicsContact contact) {
Node goldNode = contact.getNode(1000);
Node groundNode = contact.getNode(5555);
if (goldNode != null && groundNode != null) {
Vec2 loc = goldNode.getPosition();
goldExplode(loc);
goldNode.removeFromParent();
golds.remove(goldNode);
ggCount++;
}
return false;
}
}
4.爆炸效果
//金子爆炸,金子掉落到地上爆炸碎了
private void goldExplode(Vec2 loc) {
final Sprite goldSprite = NodeReader.create().readSprite("layout/GoldExplode.cce");
if (loc != null && goldSprite != null) {
goldSprite.setPosition(loc);//爆炸位置
owner.addChild(goldSprite);
}
if (goldSprite != null) {
goldSprite.runAction(Sequence.create(DelayTime.create(0.3f), new CallFunc() {
@Override
public void execute() {
super.execute();
goldSprite.removeFromParent();
}
}));
}
AudioEngine.getInstance().playEffect("audio/fallgd.mp3");
}
5.创建复杂Sprite
(包括多个图片,多个Action轨迹,根据指定数目创建新刚体)
已经给不清楚的地方作了注释。。
/**
* 创建矿车设置它的tag(获取需要通过它),设置矿车物理碰撞监听器以及物理参数
* 矿车本身的位掩码setCategoryBitmask(十六进制)
* 与矿车发生相交的对象的位掩码setContactTestBitmask(十六进制)
* 矿车移动过程:从右下角--->左下角--->左上角--->右上角--->右下角
* 移动过程是无限循环过程,移动的过程中接受金子,并将金子倒入矿槽
* *
*/
public synchronized void createTramcar() {
if (this.currentTime - this.lastCreateCarTime >= 5) {
carCount += 1;
NodeReader reader = NodeReader.create();
final Node tramcarNode = reader.readNode("layout/Tramcar.cce");
tramcarNode.setPosition(new Vec2(675, 180));
tramcarNode.setTag(0);
tramcarNode.setZOrder(10);
//车身刚体
PhysicsBody physicsBody2 = tramcarNode.getPhysicsBody();
physicsBody2.setCategoryBitmask(0x08);
physicsBody2.setContactTestBitmask(0x02);
tramcarNode.addPhysicsContactListener(new TramcarAndGoldContact());
owner.addChild(tramcarNode);
final Node cheshen = tramcarNode.getChildByTag(100);
//矿车运行线路
tramcarNode.runAction(RepeatForever.create(Sequence.create(
MoveTo.create(WHEEL_TIME, new Vec2(50, 180)),
new CallFunc() {
@Override
public void execute() {
super.execute();
goldChangeTag = 0;
}
},
Sequence.create(MoveTo.create(WHEEL_TIME, new Vec2(50, 1100))),
//2.5f表示运行到水平中间位置,就执行倒金子到金子池的动作。
Sequence.create(MoveTo.create(2.5f, new Vec2(360, 1100)), new CallFunc() {
@Override
public void execute() {
super.execute();
//获取该小车接收多少个金子,并根据金子数,在for循环中,创建gold个精灵
int gold = tramcarNode.getTag();
tramcarNode.setTag(0);
cheshen.removeAllChildren();
tramcarNode.getPhysicsBody().setEnable(true);
if (gold > 0 && gold != 1001) {
for (int a = 0; a < gold; a++) {
//创建gold Sprite,然后添加到场景Scene——这是倒金子的动作。
NodeReader reader = NodeReader.create();
String goldName = "Gold" + ((int) Math.floor(Math.random() * 4) + 1) + ".cce";
Sprite sprite = reader.readSprite("layout/" + goldName);
sprite.setTag(1000);
sprite.setZOrder(5);
sprite.setPosition(new Vec2(a * 15 + 300, 1050));
//黄金刚体
PhysicsBody physicsBody1 = sprite.getPhysicsBody();
physicsBody1.setCategoryBitmask(0x02);
physicsBody1.setContactTestBitmask(0x08);
owner.addChild(sprite);
}
}
}
}, Sequence.create(MoveTo.create(2.5f, new Vec2(675, 1100)))),
MoveTo.create(WHEEL_TIME, new Vec2(675, 180)))));
this.lastCreateCarTime = this.currentTime;
}
}
6.精灵Sprite的获取办法
6.1通过tag
@Bind(tag = 1002)
public Sprite board;//挡板
6.2通过name
6.3通过NodeReader
直接根据cce文件的布局create,并可通过强制类型转化,获取Controller类。
参考1.1中的
final ControllerMessage controller = (ControllerMessage)NodeReader.create().showNode(owner, "layout/GameOver.cce");
把数据传递给该Node,然后把背景设置为不可触碰,点击事件会被controller捕捉到。再看看代码全貌
//游戏结束后弹出的窗口
//游戏结束窗口弹出后,暂停按钮不能点击
//屏幕触摸失效,游戏暂停
private void showGameLayer() {
final ControllerMessage controller = (ControllerMessage)
NodeReader.create().showNode(owner, "layout/GameOver.cce");
controller.score = Integer.parseInt(score.getString());
setTouchEnabled(false);
pauseButton.setTouchEnabled(false);
this.playGame = false;
// FunctionFactory.callFunction("ad", "show Interstitial");
}
7.声音播放停止
AudioEngine.getInstance().playBackgroundMusic("audio/bg.mp3", true);
AudioEngine.getInstance().playEffect("audio/click.mp3");
AudioEngine.getInstance().pauseBackgroundMusic();
8.创建大量Sprite
其中golds是一个数组
List<Sprite> golds = new ArrayList<Sprite>();
//创建一定数量的金子,设置它的tag(后面获取通过它)和物理碰撞监听器参数
//本身的位掩码setCategoryBitmask(十六进制)
//相交时对象的位掩码setContactTestBitmask(十六进制)
//相交时对象的碰撞位掩码setCollisionBitmask(十六进制)
private void showAllGold() {
NodeReader reader = NodeReader.create();
int goldCountHorizon = 7;
int goldCountVertical = 7;
for (int j = 0; j < goldCountHorizon; j++) {//横排
for (int k = 0; k < goldCountVertical; k++) {//竖排
String goldName = "Gold" + ((int) Math.floor(Math.random() * 4) + 1) + ".cce";
Sprite sprite = reader.readSprite("layout/" + goldName);
sprite.setTag(1000);
sprite.setPosition(new Vec2(k * 40 + (size.getWidth() / 3 + 20), size.getHeight() - 350 - j * 20));
PhysicsBody physicsBody1 = sprite.getPhysicsBody();
physicsBody1.setCategoryBitmask(0x02);
physicsBody1.setContactTestBitmask(0x08);
owner.addChild(sprite);
golds.add(sprite);
}
}
AudioEngine.getInstance().playEffect("audio/down.mp3");
formerCount = goldCountHorizon * goldCountVertical;//原有金子总数
}
9.暂停时对所有Sprites刚体的操作。
还是通过第8点的数组获取。
public void onStop() {
for (Sprite sprite : golds) {
PhysicsBody physicsBody = sprite.getPhysicsBody();
if (physicsBody != null) {
physicsBody.setEnable(false);
}
}
}
public void onRestart() {
for (Sprite sprite : golds) {
PhysicsBody physicsBody1 = sprite.getPhysicsBody();
physicsBody1.setEnable(true);
}
}
10.动画效果。
在assets目录下建立一个animates文件夹(小心:不要把这个文件夹放到Texture那里了,根目录是assets),然后自定义一个loadingAnimate.cce文件,把keyFrame放进Animation控件中。
参考http://blog.cocoseditor.com/?p=928
//loading 3s后删除
loadingSprite.runAction(NodeReader.create().readAnimation("animates/loadingAnimate.cce"));
owner.scheduleOnce(new Scheduler.SchedulerCallback() {
@Override
public void onUpdate(float delta) {
super.onUpdate(delta);
if (loadingSprite != null) {
loadingSprite.removeFromParent();
isLoadingFinish = true;
}
}
}, 3);
//涂鸦怪物跳跃动画
Vec2 dsPos = doodleSprite.getPosition();
doodleSprite.runAction(RepeatForever.create(Sequence.create(
MoveTo.create(0.5f, new Vec2(dsPos.getX(), dsPos.getY() + 150)),
MoveTo.create(0.4f, new Vec2(dsPos.getX(), dsPos.getY())),
new CallFunc() {
@Override
public void execute() {
super.execute();
AudioEngine.getInstance().playEffect("sounds/bs_jump.mp3");
}
}
)));
组合动作,可以用Spawn,但是有的动作不能组合,例如下面的,用Spawn的话,Action类就不会启动了。但runAction是可以合并的,下面的例子我让鸟飞的时候还能拍打翅膀。
@Override
public void onEnter() {
super.onEnter();
org.ccj.d2.action.Action actionFlip = NodeReader.create().readAnimation("animates/1.cce");
waitingSprite.runAction(actionFlip);
Vec2 position = waitingSprite.getPosition();
Sequence actionUpDown = Sequence.create(
MoveTo.create(0.5f, new Vec2(position.getX(), position.getY() + 50)),
MoveTo.create(0.5f, new Vec2(position.getX(), position.getY()))
);
waitingSprite.runAction(RepeatForever.create(
// Spawn.create(
// actionFlip
actionUpDown
// )
));
}
11.允许点击事件
默认是不允许的,后来看了源码才发现。下面是允许的代码,必须先enable
@Override
public void onEnter() {
super.onEnter();
setTouchEnabled(true);
setTouchMode(Touch.MODE_ONE_BY_ONE);
this.createParticle("lv01", new Vec2(360, 400));
AudioEngine.getInstance().playBackgroundMusic("sounds/bgmusic.mp3", true);
}
下面是响应代码
@Override
public boolean onTouchBegan(Touch touch, Event event) {
Vec2 pos = touch.getLocation();
if (pausedBtn.getBoundingBox().containsPoint(pos)) {
return false;
}
this.moveMonster(pos);
return true;
}
12.延迟时间
12.1 DelayTime.create(0.3f)
当Action用,如下
pipeBar1.runAction(RepeatForever.create(Sequence.create(
MoveTo.create(1.5f,new Vec2(-160,0)),
MoveTo.create(0, new Vec2(720, 0)),
DelayTime.create(0.5f)
)));
12.2 DelayTIme只能放在最后的参数位置上
有时候,希望能放在最前,怎么实现呢?如下例子,先在屏幕外移动,3秒后,瞬间拉回到边缘。这就相当于DelayTime了。在屏幕内的等待,也可以用类似办法,移动1像素看看。
MoveTo.create(3.0f,new Vec2(screenWidth+100,0));
MoveTo.create(0,new Vec(screenWidth,0));
13.Sequence.create()的注意事项
为了实现flappy bird的柱子等间隔出现,今天花好大力气去测试。
发现Sequence.create()函数里,貌似是赋值了Action,但是,这些Action只赋值一次。我后面通过CallFunc修改了i[0]的值,企图改变sprite的等待时间,发现不行。它会在整个循环中一直都保持为初始化时候的值{waitTime*2}。
final float[] i = {waitTime * 2};
pipeBar3.runAction(RepeatForever.create(Sequence.create(
MoveTo.create(i[0],new Vec2(designWidth+50,0)),
MoveTo.create(0,new Vec2(designWidth,0)),
MoveTo.create(timeTotal,new Vec2(-pipeWidth,0)),
MoveTo.create(0, new Vec2(designWidth, 0)),
new CallFunc(){
@Override
public void execute() {
System.out.println("pipe3 end");
System.out.println("i = "+i[0]);
i[0] =0f;
super.execute();
} ;
}
)));
14.CallFunc与外部参数交互
如上面第13点的函数,i如果是数组,是可以和外部交互数据的,但是如果是普通变量,就不行。
后来发现,用全局函数是可以的。
boolean mFinishFlyFlag = true; public void onTouchBegan(){ ActionInterval rotateUp = Sequence.create( RotateBy.create(0.3f, -60) , RotateBy.create(0.2f, 60) , new CallFunc(){ @Override public void execute() { mFinishFlyFlag = true; super.execute(); } } );........}
15.定时创建一个Sprite
参考了Demo中的ComponetsTestScene,每次onUpdate统计elapseTime,如果超过1秒,就创建一个怪物。
float _addTargetTime;
float _elapsedTime;
public void onEnter()
{
super.onEnter();
this._addTargetTime = 1;
_targets = new ArrayList();
_projectiles = new ArrayList();
Node owner = this.getOwner();
AudioEngine.getInstance().playBackgroundMusic("Sound/background-music-aac.wav", true);
ComAttribute attribute = ComAttribute.cast(owner.getComponent("CCComAttribute"));
//积分
attribute.setInt("KillCount", 0);
}
public void onUpdate(float dt)
{
super.onUpdate(dt);
this._elapsedTime += dt;
if (this._elapsedTime > this._addTargetTime) {
this.addTarget();
this._elapsedTime = 0.0f;
}
}
还可以用schedule,定时处理事务,但使用这个,就不能使用Controller的onUpdate()了(没验证过),除非你知道怎么处理。还有个SchedulerListener接口,已经有了Callback,暂时没发现这个接口有什么用。
public void onCreate()
{
super.onCreate();
this.schedule(new Scheduler.SchedulerCallback()
{
public void onUpdate(float delta)
{
super.onUpdate(delta);
accum += delta;
Logger.log("Time: " + accum);
if (accum > 3) {
unschedule(this);
Logger.log("scheduler removed");
}
}
}, 0.5f);
this.schedule(new Scheduler.SchedulerCallback()
{
public void onUpdate(float delta)
{
super.onUpdate(delta);
Logger.log("This scheduler should not be removed");
}
}, 0.5f);
}