# 超能陆战队
注:美术资源来源于网络
作者:
* 所用到的 IDE :
- Cocos Creator v2.0.10,主要用于视觉还原、主逻辑开发、跨端调试和编译。个人觉得这是超给力的游戏开发工具,把 component 的机制直接可视化,集成各种物理引擎、粒子引擎、UI 组件等功能,大大节省了游戏UI以及部分特效逻辑的研发成本。但小游戏版本的编译速度还是很慢啊。
- Visual Stuido Code,用于代码开发。这好像是最近最流行的代码撰写工具了,免费且稳定啊!
- Google Chrome,web版游戏调试。有些时候一些性能调优可以放在 chrome 上面,它有非常专业的调试工具。
游戏的主要玩法:
摇杆控制人物躲避怪物并且用子弹或者技能杀死他们
* 实现功能:
- 摇杆控制人物移动
- 长按子弹键一直发射子弹
- 限制人物移动区域
- 骨骼动画的切换
- 人物机甲的子弹反弹
- 选择关卡
- 弹夹掉落
- 炸弹怪索敌移动和爆炸
- 蜜蜂怪尾刺功能
- 蜘蛛怪移动和攻击
- 锯齿怪移动和碰撞判断
- 魔龙怪两种攻击方式
- 机器人boss攻击
- 冰冻道具功能实现
- 护盾道具功能实现
- 轰炸道具功能实现
- 奖励箱功能实现
- 欢呼通告功能实现
- 角色怪物视角朝向功能实现
是烧麦啊 实现模块
1.摇杆功能的实现
先在摇杆底图上挂载一个脚本,在他的面板属性上添加一个节点,在设置他的最远距离限制,然后将我们的摇杆拖到这个面板属性上的节点上
cc.Class({
extends: cc.Component,
properties: {
Rocker: cc.Node,
Max_r: 60,
},
start() {
this.qishi = cc.v2(0, 0)
this.Rocker.on(cc.Node.EventType.TOUCH_START, function (e) {
var w_pos = e.getLocation(); //先获取触摸节点的坐标
var pos = this.node.convertToNodeSpaceAR(w_pos); //转换为在 父节点下的坐标
if (pos.mag() > 60) {
pos.normalizeSelf();
pos = cc.v2(pos.x * 60, pos.y * 60);
}
this.Rocker.setPosition(pos);
cc.director.emit("遥杆", pos.x, pos.y)
}, this);
这是摇杆的代码,先设置一个起始位置,就是(0,0)
先注册一个触摸事件,在触摸时和移动时都先获取触摸节点的坐标,然后转换为父节点下的坐标,如果距离超过了限制距离 就将它归一化在乘60 如果没超过限制距离的话就直接赋值给他的position然后发射“摇杆”事件将参数传去给人物,人物就可以根据摇杆的方向去移动了
手指抬起时,位置复位到起始位置。
2.长按发射键的实现
长按子弹键发射子弹的功能实现我使用的是图片代替Button,然后在精灵上注册一个触摸事件,当你按下时就发射子弹事件,就能做到一直连发子弹,而如果使用Button的话只能一发一发的发射子弹下面是我实现的代码
this.qiang1.on(cc.Node.EventType.TOUCH_START, function () {
cc.director.emit("开火1");
this.qiang1.scale = cc.v2(0.9, 0.9);
this.schedule(this.shoot1, 0.2);
}, this)
当你按下子弹建的同时要缩放图片,这里是接受开火事件的代码块,先获取子弹的Label组件,然后读取他的值是否为0 当子弹数量为0时return出去不执行下面代码,如果不为零则获取主角身上的动画组件,让他播放相对应的枪械动画,并且设置动画播放模式为一直循环
cc.director.on("开火1", function () {
let zidan1 = this.attack1.getComponent(cc.Label)
if (zidan1.string == 0) {
return;
}
var anim = this.node.getComponent(cc.Animation);
let animState = anim.play('Pler clip');
animState.wrapMode = cc.WrapMode.Loop;
anim.playAdditive('zidan1');
this.yidalipao(this.obj1, 0);
this.danjiasc();
3.人物限制移动区域
人物限制的方法我是在摇杆移动赋值给人物前每帧进行判断,判断人物是否超出边界,如果左边到达边界则只加上下移动的值只需要做if的判断就行了
超能战队
4.人物的骨骼动画切换
先获取人物的骨骼动画组件然后给他注册骨骼动画播放完一次后的监听事件
setCpmpleteListener()下面是具体代码
onLoad() {
var ske = this.node.getComponent(sp.Skeleton);
this.ske = ske;
},
start() {
let all = cc.find("Canvas");
let acc = cc.find("Canvas/yaogan");
all.pauseSystemEvents(true);
this.ske.setAnimation(0, "ruchang", false);
this.ske.setCompleteListener((trackEntry) => {
if (trackEntry.animation.name == "ruchang") {
this.ske.setAnimation(0, "zhongjian", false);
} else if (trackEntry.animation.name == "zhongjian") {
this.ske.setAnimation(0, "jiewei", false);
} else if (trackEntry.animation.name == "jiewei") {
acc.resumeSystemEvents(true);//恢复所有触摸点击事件
this.scheduleOnce(() => {
this.node.destroy();
cc.director.emit("hudun1");
cc.director.emit("nuosijj");
}, 0.5)
}
})
},
获取当前骨骼的动画的名字,然后进行骨骼动画的排序播放,我是设定了播放时屏蔽了所有的触摸点击事件,然后播放完后恢复,并且发射护盾,以及诺斯机甲的事件,别的地方监听后作出护盾和子弹导弹的发射
5.人物机甲导弹触壁反弹
人物机甲的子弹反弹功能实现,先在诺斯机甲上设立几个空节点作为她的发射点和计算向量的点
然后通过我们设立的2个空节点获取一条直线
let fashedian = this.toWorldPosition(this.node.getChildByName("2")); //发射点
let paoguan = this.toWorldPosition(this.node.getChildByName("1")) //炮管 2个点得到一条直线计算弧度角度和方向
let fashedian1 = this.toWorldPosition(this.node.getChildByName("4")); //发射点
let it = cc.instantiate(this.jijiazidan);//实例化子弹
it.parent = cc.find("Canvas");//父节点
this.shuzu.push(it)
if(this.zy == true ){
it.position = fashedian1
}else{
it.position = fashedian; //导弹出生点为发射点
}
let isx = fashedian.x -paoguan.x;
let isy = fashedian.y - paoguan.y;
然后生成子弹的预制体。并且放到一个空数组中
然后调用接口转换得到角度,根据子弹的角度去判断他是往什么方向上去运行
然后就能得到他相撞后的那个位置(就举一个方向为例子)
let lastAngle = cc.misc.degreesToRadians(-it.rotation); //将之前赋值过得it的roattion转换得到幅度
let tanValue = Math.tan(lastAngle);
let py = -cc.winSize.height / 2;
//已知的一个点的x坐标 -已知的一个点的y坐标-相撞后的y坐标(因为是上边界所以可以得到Y坐标 就是屏幕高度除以2)在调用Math.tan方法传入这条直线的幅度
let px = it.x - (it.y - py) / tanValue;
return cc.v2(px, py);
得到位置后直接调用moveTo方法让子弹移动到那里就行,这里要注意子弹反弹时的旋转角度
这里是反弹的具体代码
this.startPos = it.position;
this.endPos = dest
let distance = this.startPos.sub(this.endPos).mag() / 500
//顺序动作先执行移动移动执行完后在调用自身
var seq = cc.sequence(
cc.moveTo(distance, dest),
cc.callFunc(function () {
if (it.curDir == "leftUp") {
if (it.rotation < 0) {
it.rotation = -180 - it.rotation;
} else {
it.rotation = - it.rotation;
}
} else if (it.curDir == "rightDown") {
if (it.rotation < 0) {
it.rotation = - it.rotation;
} else {
it.rotation = 180 - it.rotation;
}
}else if(it.curDir == "leftDown"){
if (it.rotation < 0) {
it.rotation = - it.rotation;
} else {
it.rotation = 180 - it.rotation;
}
}else{
if (it.rotation < 0) {
it.rotation = -180- it.rotation;
} else {
it.rotation = - it.rotation;
}
}
this.movingAction(it);
}, this)
);
it.runAction(seq);
反弹后我是根据子弹的旋转角度和他现在要飞行的方向来计算他现在的角度应该是多少
举个例子现在他反弹后飞行的角度是左上角,只有2种情况是会反弹左上角的,
一种是碰到右边墙壁此时子弹的角度是负的,要往左上角只需要将它当前角度转正就可以了,
还有一种情况是碰到下面的墙壁往左上角弹,此时子弹没有反弹时的角度是正的,180减去她的角度在转换为负数就可以得到反弹后的角度
然后递归调用函数直到子弹发生碰撞或者变身时间结束
超能战队
6.选择关卡的功能实现
使用的是Scrollview组件设置好大小,然后使用图片拼接
cc.director.on("zuobiao",function(x){
this.node.getComponent(cc.ScrollView).scrollToPercentHorizontal((x.x-50)/3200);
},this);
进入场景时将人物小头像的左边通过事件的方式传到函数里,然后调用Scrollview组件的scrollToPercentHorizontal方法就可以设置你
组件移动的百分比,就能达到下面的效果,进入选关场景时人物头像在中间不用从最左侧开始滑
7.弹夹掉落
弹夹的生成我是使用得预制体的方式去实现的,然后给弹夹添加一个自动义动画让它旋转起来,
而弹夹掉落的曲线我用的方法是贝塞尔曲线,下面是具体的代码。
let a = Math.floor(Math.random() * 200) // 随机生成是为了让它的曲线稍有不同不至于很单调的一条曲线
let b = Math.floor(Math.random() * 50) + 350;
let d = Math.floor(Math.random() * 10)
if (d % 4 === 0) { //是为了弹夹弹飞的方向不同
let itc = cc.bezierTo(1.2, [
cc.v2(xsj.x + a, xsj.y + 300),
cc.v2(xsj.x + a + 50, xsj.y - b),
cc.v2(xsj.x + a, xsj.y - b),
]);
it.runAction(itc)
} else {
let cad = cc.bezierTo(1.2, [
cc.v2(xsj.x - a, xsj.y + 300),
cc.v2(xsj.x - a - 50, xsj.y - b),
cc.v2(xsj.x - a, xsj.y - b),
]);
it.runAction(cad)
}
this.scheduleOnce(() => {
it.destroy();
}, 1) //延迟一秒后删除
未生畏死 实现模块:
- AI生成
随机在半个地图外生成AI,然后让AI移动到地图内。
bornPosition() {
//出生位置
let it = cc.find("Canvas/AiPanrent");//怪物父节点 置(0,0);
this.node.parent = it;
let posX = Math.random() * (cc.winSize.width + this.node.width) * 2 - (cc.winSize.width + this.node.width);//x,y
let posY = Math.random() * (cc.winSize.height + this.node.height) * 2 - (cc.winSize.height