给宝宝做一个cocos免费游戏
第一章 背景和开发框架介绍
第二章 Node树和场景制作
第三章 UI、地图和关卡文本制作
第四章 摇杆、按键和角色动画制作
第五章 敌人和AI制作
第六章 角色和敌人行为互动脚本制作
第七章 游戏打包、发布和调试
cocos角色和敌人行为互动脚本制作
前言
前面错漏百出的介绍了怎么制作地图、ai和角色,这一节顺水推舟做他们的交互。有朋友私信说啥时候可以补充下剧情跟美工,关键技术在了但是画面看起来货不对板。压力山大,有空先,后面娓娓道来,美工小姐姐抱娃去了只能雌雄同体了
道个歉,忙了一段时间,游戏项目搁置了很久。
一、角色跟地图交互
主要因为我们没有使用tiedmap制作地图,非要作死另辟蹊径,掉进了小溪里。所以要做地图阻隔功能、剧情触发、掉血处理等。酸爽求虐的感觉
(一)阻隔的实现
1.地图对象——碰到了就切线移动吧,这样实现可以的。别忘了还有一个组件叫做物理引擎,必须用上做阻隔
现在把之前地图prefab对象添加上物理引擎。
勾选刚体Bullet,type选择Static固定的(毕竟是墙壁等)
其他默认即可
当然,第一步是在加载地图时候记得开启物理引擎
在这里插入代码片
2.角色对象:
(1)关键位置如身体手脚可能接触的组件都添加碰撞体(也是物理组件)
注意:角色由多个物理接触点如武器 手脚等,如果不添加JOIN类物理组件,物理开启了滞后,不会跟随主node行动
join类物理组件请参考:https://blog.csdn.net/qq_43287088/article/details/107948878
当然也可以使用最简单的方法,animation动作制作时候,除了angle还要记录position,这样可以偷工减料。
(二)完善地图触发
对门、*、箱子等添加代码,实现阻隔和通行切换、加载内容等,考虑互动性,加载aijs.js并把响应代码写在上面
调整下canvasjs.js的loadMaoObj和Main.json
二、角色和AI互动
相关代码都写在ajjs.js文件上,通过reacttype判断
switch(this.reacttype){
case 'box': break;
case 'guid': break;
case 'dialog':break;
case 'player':break;
case 'enemy': break;
case 'story': break;
case 'loader':break;
}
1.load读出tab
//这里直接在ajjs.js中累加属性(jstick中已经加载菜单)
loadEquipment:function(owner){
switch(this.reacttype){
case 'box': break;
//json-items:position = owner
case 'guid': break;
case 'dialog':break;
case 'player': //角色的装备属性
//ui-tab
var eq = cc.find('Canvas').getChildByName('UI').getChildByName('itemScrollView').getChildByName('equipment');
if(eq){
var eqs = eq.children;
if(eqs.length > 0){
for(var i=0;i<eqs.length;i++){
var ip = eqs[i].children[0]?eqs[i].children[0].getComponent('itemstab'):null;
if(!ip){return;}
this.feeldistance = Math.round(this.feeldistance) + Math.round(ip.feeldistance);
this.actdistance = Math.round(this.actdistance) + Math.round(ip.actdistance);
this.movespeed = Math.round(this.movespeed) + Math.round(ip.movespeed);
this.hp = Math.round(this.hp) + Math.round(ip.hp);
this.lp = Math.round(this.lp) + Math.round(ip.lp);
this.mp = Math.round(this.mp) + Math.round(ip.mp);
this.at = Math.round(this.at) + Math.round(ip.at);
this.skill1 = ip.skill1?ip.skill1:this.skill1 ;//cd?按照按键是否active
this.skill2 = ip.skill2?ip.skill2:this.skill2 ;
this.skill3 = ip.skill3?ip.skill3:this.skill3 ;
this.skill4 = ip.skill4?ip.skill4:this.skill4 ;
// this.skills.push( ip.skill1?ip.skill1:this.skill1 );
}
}
}
break;
case 'enemy'://敌人的装备属性
//json-items:position = owner
if(TOOLS){
for(var i=0;i<TOOLS.length;i++){
if(TOOLS[i].owner == this.node.name){
if(TOOLS[i].type=='S'){ //往下走,放到空的skill栏
if(this.skill1 ==''){this.skill1 = TOOLS[i].name;}
else if(this.skill2==''){this.skill2 = TOOLS[i].name;}
else if(this.skill3 ==''){this.skill3 = TOOLS[i].name;}
else if(this.skill4 ==''){this.skill4 = TOOLS[i].name;}
else{}//都不为空,不执行
var tmp = 'name:'+TOOLS[i].name+',eff:'+TOOLS[i].eff+',at:'+TOOLS[i].value;
this.skills.push(tmp);
}
else{//消耗品也累加到属性,后面优化时候考虑使用
switch(TOOLS[i].eff){
case 'fd':this.feeldistance = Math.round(this.feeldistance) + Math.round(TOOLS[i].value);break;
case 'ad':this.actdistance = Math.round(this.actdistance) + Math.round(TOOLS[i].value);break;
case 'ms':this.movespeed = Math.round(this.movespeed) + Math.round(TOOLS[i].value);break;
case 'hp':this.hp = Math.round(this.hp) + Math.round(TOOLS[i].value);break;
case 'lp':this.lp = Math.round(this.lp) + Math.round(TOOLS[i].value);break;
case 'mp':this.mp = Math.round(this.mp) + Math.round(TOOLS[i].vaue);break;
case 'at': this.at = Math.round(this.at) + Math.round(TOOLS[i].value);break;
}
}
}
}
}
break;
case 'story':break;
case 'loader': break;
}
},
2.技能CD实现
这里直接使用random方式释放技能,当然可把技能load到array中,自己循环一下什么距离使用有mp的技能
takeActions:function(e){
var an = this.node.getComponent(cc.Animation);
var anstatus = this.node.getComponent(cc.Animation).AnimationStatus;
// if(an){an.play('att_ailhit');};
var sk = this.sk?this.sk:[];
if(sk.length>0){
var r = Math.floor(Math.random()*sk.length+1); //随机无cd
an.play(sk[r].name);
//减血方面的打击运算,都放在碰撞上
}
//check free
//important level
},
3.伤害和扣血
在物理碰撞中实现,如果是weapon+对手,则减血
//2.物理碰撞
onBeginContact(contact, selfCollider, otherCollider) {
// cc.log("onBeginContact"); //碰撞触发事件
switch(this.reacttype){
case 'box':
//need key?
break;
case 'door':
//need key?
break;
case 'guid':
if( otherCollider.node.group != 'players'){break;}
var lv = this.node.getComponent('aijs').lv ;//get lv text
// if(!lv){break;}
this.node.getChildByName('RichText').active = true;
break;
case 'dialog':break;
case 'player':
if(otherCollider.node.group == 'weapon' && otherCollider.node.parent.group == 'enemies'){
var pai = otherCollider.node.parent.parent.parent.getComponent('aijs')?otherCollider.node.parent.parent.parent.getComponent('aijs'):otherCollider.node.parent.parent.parent.parent.getComponent('aijs');
this.hp -= pai.at;
}
break;
case 'enemy':
//1.action back:弹性系数
//2.hp mp lp changing
if(otherCollider.node.group == 'weapon' && otherCollider.node.parent.group == 'players'){
var player = cc.find('Canvas').getChildByName('mapNode').getChildByName('player');
var pai = player.getComponent('aijs')?player.getComponent('aijs'):player.getChildByName('body').getComponent('aijs');
this.hp -= pai.at;
}
break;
case 'story':break;
case 'loader'://加载
var lv = this.node.getComponent('aijs').lv ;//get lv text
cc.loadScene(lv);
break;
}
},
4.打倒和消灭
goDieAndDistroy:function(e){
if(!this.node){return;}
var an = this.node.getComponent(cc.Animation);
an.play('dead');
// this.schedule(function () {
this.node.parent.destroy();
// }, 0.2, 1,3);
},
5 .最终在UPDATE方法实现简单AI
update (dt) {
if(!this.node){return;}
if(this.hp == 0){
this.goDieAndDistroy();
if(this.reacttype=='player'){
var cvs = cc.find('Canvas');
cvs.getChildByName('mapNode').acitve = false;
cvs.getChildByName('UI').active = false; //g over
cvs.getChildByName('RichText').active =true ;//可以动画
}
}//执行一次
else{
//通过距离判断
switch(this.reacttype){
case 'guid': break;
case 'dialog':break;
case 'player':break;
case 'enemy':
var player = this.node.parent.getChildByName('player');
var tx = this.node.x - player.position.x;
var pdis = this.node.position.sub(player.position);//有方向,判断<>0是无法判断向量的
// if(this.actdistance < pdis.mag() < this.feeldistance){
// if(this.node.x > player.x){ this.node.scaleX = -1 * Math.abs(this.node.scaleX );}
// else{this.node.scaleX = Math.abs(this.node.scaleX )}//turnaround
// if(tx < 0) {this.node.x += this.movespeed;}
// else if(tx > 0){this.node.x -= this.movespeed;} //1d x轴运算即可
this.playAnimation('walk');
// }
if(this.actdistance >= pdis.mag()){
//take actions
this.takeActions();
}
if(pdis.mag() > this.feeldistance){
cc.tween(this.node).to(2,{position:this.initpos}); //原始位置
};
//障碍物
break;
case 'story':break;
}
}
},
三、AI和地图
1.阻隔
使用物理引擎实现,故不需要做特殊的代码处理,原理不重复说明,记得勾选不穿透和触发方法即可
2.路线
在aijs.js的update写入逻辑:
没有打扰时候执行巡逻路线
进入距离执行追赶路线
离开后执行返回原始位置路线
总结
先到这里,后面阐述美化剧本、打包发布等内容。