本文共 9406 字,大约阅读时间需要 31 分钟。
第一章 背景和开发框架介绍
第二章 Node树和场景制作 第三章 UI、地图和关卡文本制作 第四章 摇杆、按键和角色动画制作 第五章 敌人和AI制作 第六章 角色和敌人行为互动脚本制作 第七章 游戏打包、发布和调试请下载上一章代码:https://download.csdn.net/download/sinat_29014637/17854872?spm=1001.2014.3001.5503。本章节上上一章代码基础上开发
原理:通过cc.sys.localstorage来设定参数作为设定值(HTML平台清空缓存会被清理),jsb操作json在h5平台不受支持,所以我们项目暂时使用前者。
既然是全局的代码,我们放到canvasjs.js上
//1.显示和隐藏setting showOrHideSetting:function(e){ var setl = cc.find('Canvas').getChildByName('settingLayout'); if(setl){ setl.active = setl.active? false:true; } },//2.显示配置内容 loadSetting:function(e){ var setl = cc.find('Canvas').getChildByName('settingLayout'); var pname = cc.sys.localStorage.getItem('pname'); if(setl && pname){ setl.getChildByName('EditBox1').getComponent(cc.EditBox).string = pname; setl.getChildByName('EditBox1').getChildByName('LABEL').string = 'pname'; //voice //graphic //action } var uinode = cc.find('Canvas').getChildByName('UI'); var userinfo = uinode.getChildByName('playerinfo'); if(userinfo){ userinfo.getChildByName('pname').getComponent(cc.Label).string = pname? pname:'notnamed';},//3.修改配置内容 setSetting:function(e){ var setl = cc.find('Canvas').getChildByName('settingLayout'); var value = setl.getChildByName('EditBox1').getComponent(cc.EditBox).string ; var keys = setl.getChildByName('EditBox1').getChildByName('LABEL').string; cc.sys.localStorage.setItem(keys,value); },
onLoad(){ this.loadSetting();//加载setting,显示在右上角头像和三围 }
现在开始做物品和装备栏,上一章我们建立了equipment节点:头身体和四肢+3个技能栏组成和scrollview,因为我们日常都是拖动物品到装备栏,故建议把equipment节点放到scrollview子节点下,方便在同一个js上写触摸拖动、碰撞、碰撞后填入栏位的功能。
LOAD和set读写items的代码公用性较强,可以写到canvasjs.js
(1)考虑scrollview导致node层级较多,这里把itemSrollView修改一下,把view节点改为grid,添加layout组件: (2)添加后在grid节点添加子节点发现都会自动按照网格排列。所以清空grid的子对象,使用代码读取json和localstorage来添加相应的物品。 这里遇到一个问题,cc.sys.loalstorage是异步读取的,所以读取json时候重复读spriteframe图片,会发现读完spriteframe后,前面已经读取完,故只有最后一个json记录才有spritefram,这里先记录下来后面找找原因,知道解决方法的朋友也请留言告诉我一声。 虽然json遇到问题,项目还是要进行,那就替代方案走起:1)js直接定义一个array变量记录初始的item
const TOOLS = [ { id:'W1',//唯一值:type+序号 sname:'mainMap',//场景 type:'W',//类型 W-weapon I-ITEMS E-EQUIPMENT S-SKILL技能 name: '工具套装', // 名称 desc: '这是一个工具盒,撬门破解无所不能', // 点击显示介绍 owner:'player',//所在位置(拥有者) eff:'at',//效果 at=attact hp=+-hp mp=+-mp lp=+-lp值 value:'10',//配合eff,eff的值,支持+-号 rotation:'0',//方向 icon: 'icons/png-0007' // 动态加载路径 }, { id:'I1',//唯一值:type+序号 sname:'mainMap',//场景 type:'I',//类型 W-weapon I-ITEMS E-EQUIPMENT name: '放大镜', desc: '就是个放大镜,啥东西放上去都能变大', owner:'player',//所在位置(拥有者) eff:'at',//效果 at=attact hp=+-hp mp=+-mp lp=+-lp值 value:'10',//配合eff,eff的值,支持+-号 rotation:'0',//方向 icon: 'icons/png-0017' }, { id:'W2',//唯一值:type+序号 sname:'mainMap',//场景 type:'W',//类型 W-weapon I-ITEMS E-EQUIPMENT name: '剪刀', desc: '这是剪刀,当做武器不知道行不行', owner:'player',//所在位置(拥有者) eff:'at',//效果 at=attact hp=+-hp mp=+-mp lp=+-lp值 value:'20',//配合eff,eff的值,支持+-号 rotation:'0',//方向 icon: 'icons/png-0031' }, { id:'W3',//唯一值:type+序号 sname:'mainMap',//场景 type:'W',//类型 W-weapon I-ITEMS E-EQUIPMENT name: '卷轴', desc: '这是卷轴', owner:'player',//所在位置(拥有者) eff:'at',//效果 at=attact hp=+-hp mp=+-mp lp=+-lp值 value:'20',//配合eff,eff的值,支持+-号 rotation:'0',//方向 icon: 'icons/卷轴' },];export { TOOLS}; // import {TOOLS} from './array'; //通过这个函数调用js
2)新建一个itemstab.js来编写加载spriteframe动态icon的代码
// update (dt) {}, setSpriteFrame:function(iconurl){ var self = this; cc.loader.loadRes(iconurl,cc.SpriteFrame,function(err,res){ self.node.getComponent(cc.Sprite).spriteFrame = res; }); },
3)定义一个物品的prefab方便实例化复制然后切换不同的icon来代码不同物品
a.在grid节点——新建节点——sprite,然后添加一个label b.给item添加js组件 c.拖动item节点到资源管理器新建的prefab文件夹(任意都行,方便管理新建了prefab文件夹) 这样就完成了预制体的制作,后面通过cc.instance()来不断实例化和复制,重复利用4)在canvasjs.js中编写代码,要加载icon图片时候,直接调用itemstab.js就可以同步加载,解决异步问题
//items和equipte相关代码 loadItems: function (e, customerData) { var itemsview = cc.find('Canvas').getChildByName('UI').getChildByName('itemScrollView'); if (!itemsview) { cc.log('找不到itemScrollView'); return; } //customerData:i类-formal items E类-equipment W类-weapon //是否有缓存,有则取缓存的档案,没有取json的初始档案 var items = cc.sys.localStorage.getItem('items'); if (!items) { //from json var url = 'json/main';//resources/json/.. // cc.loader.loadRes(url, cc.RawAsset, function (err, res) {//异步跟下面异步获取spriteframe有冲突 // if (!err) { // var rs = res.json.items;//json的obj:mapobj items acts for (var i = 0; i < TOOLS.length; i++) { if (TOOLS[i].owner == 'player') { var obj; obj = cc.instantiate(this.item); //(new cc.Node());//this.floor无效 if (obj) { obj.name = TOOLS[i].id;//TOOLS[i].name; obj.angle = - TOOLS[i].rotation;//方向 // obj.addComponent(cc.Sprite); // obj.parent = itemsview.getChildByName('grid'); //父本 sv-view-content-item // obj.setPosition(cc.v2(rs[i].posx, rs[i].posy)); // cc.loader.loadRes(rs[i].icon,cc.SpriteFrame,function(err,res){ // obj.getComponent(cc.Sprite).spriteFrame = res; // }); var itemjs = obj.getComponent('itemstab'); var icons = TOOLS[i].icon; itemjs.setSpriteFrame(icons);//直接调用item的方法 itemsview.getChildByName('grid').addChild(obj); } } } // } else { cc.log(err); } //异步 往往会执行后面之后再回调函数 // }); } else { //from localstorage } }, setItems: function (e) { },
最终效果:比较“斋”,目的达到就好,后面再找时间美化下
item拖动可以通过touch_move的方法实现,移动时候鼠标变为图片,停止时候执行排序,排列顺序呢目前是按照grad layout标准方法排列:新的放到最后,旧的不变
onLoad() { var self = this; this.initpos; this.endpos; this.node.on(cc.Node.EventType.TOUCH_START, function (e) { this.initpos = e.getLocation; }.bind(this), this); this.node.on(cc.Node.EventType.TOUCH_MOVE, function (e) { var dis = e.getDelta(); // cc.log(dis.x +':'+ dis.y); if(self.node.parent.parent.name == 'equipment'){ return;} var dy = self.node.position.y + dis.y; var dx = self.node.position.x + dis.x; var isv = cc.find('Canvas').getChildByName('UI').getChildByName('itemScrollView'); // cc.log(self.node.parent);//需要添加不等于装备 self.node.parent = isv;//grid 里面是不能随便拖动的,转到别的父本 self.node.setPosition(cc.v2(dx, dy)); }.bind(this), this); this.node.on(cc.Node.EventType.TOUCH_END, function (e) { }.bind(this), this); this.node.on(cc.Node.EventType.TOUCH_CANCEL, function (e) { // self.node.removeFromParent();//移走 //没有移到其他栏目则回来原来grid if (self.node.parent == cc.find('Canvas').getChildByName('UI').getChildByName('itemScrollView')) { self.node.parent = cc.find('Canvas').getChildByName('UI').getChildByName('itemScrollView').getChildByName('grid'); } }.bind(this), this); },
而考虑交互事件较多、冒泡事件传递较复杂,我们需要在itemstab.js脚本文件来编写items的拖拉筛选等代码
原理剖析:首先我们是拖动item,故应该在每个item启用touch方法;拖动后放到装备栏或者使用按钮,有两种方法实现:a.判断坐标是否落入 b.直接使用碰撞,碰撞后从物品栏去掉,加载到具体装备栏。我选择b资源不用重复加载,换一个副本即可。另外我们需要把装备类item节点命名为E+名称,这样就可以简单判断。(1)开启碰撞
onLoad() { //开启碰撞检测 var collider = cc.director.getCollisionManager(); collider.enabled = true; // collider.enabledDebugDraw = true; //debug // collider.enabledDrawBoundingBox = true; ...
检查碰撞组:UI之间还是要碰撞(也可以单独给item分配新的组)
(2)检测碰撞时候改变副本为具体的equipment栏
//碰撞检测 onCollisionEnter:function(other,self){ // cc.log('ot:'+other.node.name); // cc.log('se:'+self.node.name.substr(0,1)); if(self.node.name.substr(0,1) != 'W' && self.node.name.substr(0,1) != 'S' && self.node.name.substr(0,1) != 'E'){ return;} var i = 0; switch(other.node.name){ //对应equipment节点所有的装备栏 case 'head':i =1;break; case 'body':i =2;break; case 'lhand':i =3;break; case 'rhand':i =4;break; case 'lfoot':i =5;break; case 'rfoot':i =6;break; case 'skill1':i =7;break; case 'skill2':i =8;break; case 'skill3':i =9;break; // case '':break; // case '':break; // case '':break; } if(i!= 0 && other.node.children.length<1){ self.node.parent = other.node ; // cc.log(other.node.name); self.node.setPosition(cc.v2(0,0)); } },
有点复杂,花了较长时间只做了item栏和装备栏的互动,后面还要做数据运算等,地图和关卡文本放到(二)文章处理吧。 这里提供物品栏制作的文章供参考,非常感谢博主对我们这些菜鸟的指引: https://blog.csdn.net/La_vie_est_belle/article/details/105147837