【Cocos Creator 实战教程(1)】——人机对战五子棋(节点事件相关)

lili

发布日期: 2018-11-21 20:32:10 浏览量: 3178
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

一、涉及知识点

  1. 场景切换
  2. 按钮事件监听
  3. 节点事件监听
  4. 节点数组
  5. 循环中闭包的应用
  6. 动态更换sprite图片
  7. 定时器
  8. 预制资源

二、步骤

2.1 准备工作

首先,我们要新建一个空白工程,并在资源管理器中新建几个文件夹

在这些文件夹中,我们用来存放不同的资源,其中

  • Scene用来存放场景,我们可以把场景看作一个关卡,当关卡切换时,场景就切换了

  • Script用来存放脚本文件

  • Texture用来存放显示的资源,例如音频,图片

  • Prefab用来存放预制资源,接下来我会详细的介绍

接下来,我们在Scene文件夹中新建一个场景(右击文件夹->新建->Scene),命名为Menu,接着导入背景图片(直接拖拽即可)。最后调整图片大小使图片铺满背景,效果如图。

2.2 按钮监听与场景切换

接下来我们来学习此次实战的第一个难点,按钮监听与场景切换。

首先,创建一个Button节点,并删除label,放在人机博弈的按钮上,并在属性上调成透明样式。

接下来,新建一个Game场景,并添加一个棋盘节点,并把锚点设为(0,0)。

这里,讲一下锚点的作用。

anchor point 究竟是怎么回事? 之所以造成不容易理解的是因为我们平时看待一个图片是以图片的中心点这一个维度来决定图片的位置的。而在cocos2d中决定一个图片的位置是由两个维度一个是 position 另外一个是anchor point。只要我们搞清楚他们的关系,自然就迎刃而解。
默认情况下,anchor point在图片的中心位置(0.5, 0.5),取值在0到1之间的好处就是,锚点不会和具体物体的大小耦合,也即不用关注物件大小,而应取其对应比率,如果把锚点改成(0,0),则进行放置位置时,以图片左下角作为起始点。
也就是说,把position设置成(x,y)时,画到屏幕上需要知道:到底图片上的哪个点放在屏幕的(x,y)上,而anchor point就是这个放置的点,anchor point是可以超过图片边界的,比如下例中的(-1,-1),表示从超出图片左下角一个宽和一个高的地方放置到屏幕的(0,0)位置(向右上偏移10个点才开始到图片的左下角,可以认为向右上偏移了10个点的空白区域)

他们的关系是这样的(假设actualPosition.x,actualPosition.y是真实图片位置的中点):
actualPosition.x = position.x + width*(0.5 - anchor_point.x);
acturalPosition.y = position.y + height*(0.5 - anchor_point.y)
actualPosition 是sprite实际上在屏幕显示的位置, poistion是 程序设置的, achor_point也是程序设置的。

然后,我们需要新建一个脚本,Menu.js,并添加开始游戏方法。

  1. cc.Class({
  2. extends: cc.Component,
  3. startGame:function(){
  4. cc.director.loadScene('Game');//这里便是运用导演类进行场景切换的代码
  5. }
  6. });

这里提示以下,编辑脚本是需要下载插件的,我选择了VScode,还是很好用的。

最后我们将其添加为Menu场景的Canvas的组件(添加组件->脚本组件->menu),并在BtnP2C节点上添加按钮监听响应。

这样,按钮监听就完成了。现在我们在Menu场景里点击一下人机按钮就会跳转到游戏场景了。

2.3 预制资源

预制资源一般是在场景里面创建独立的子界面或子窗口,即预制资源是存放在资源中,并不是节点中,例如本节课中的棋子。

现在我们就来学习一下如何制作预制资源。

再将black节点改名为Chess拖入下面Prefab文件夹使其成为预制资源。

这其中,SpriteFrame 是核心渲染组件 Sprite 所使用的资源,设置或替换 Sprite 组件中的 spriteFrame 属性,就可以切换显示的图像,将其去掉防止图片被预加载,即棋子只是一个有大小的节点不显示图片也就没有颜色。

2.4 结束场景

直接在Game场景上制作结束场景。

2.5 游戏脚本制作

人机对战算法参考了这里https://blog.csdn.net/onezeros/article/details/5542379

具体步骤便是初始化棋盘上225个棋子节点,并为每个节点添加事件,点击后动态显示棋子图片。

代码如下:

  1. cc.Class({
  2. extends: cc.Component,
  3. properties: {
  4. overSprite:{
  5. default:null,
  6. type:cc.Sprite,
  7. },
  8. overLabel:{
  9. default:null,
  10. type:cc.Label
  11. },
  12. chessPrefab:{//棋子的预制资源
  13. default:null,
  14. type:cc.Prefab
  15. },
  16. chessList:{//棋子节点的集合,用一维数组表示二维位置
  17. default: [],
  18. type: [cc.node]
  19. },
  20. whiteSpriteFrame:{//白棋的图片
  21. default:null,
  22. type:cc.SpriteFrame
  23. },
  24. blackSpriteFrame:{//黑棋的图片
  25. default:null,
  26. type:cc.SpriteFrame
  27. },
  28. touchChess:{//每一回合落下的棋子
  29. default:null,
  30. type:cc.Node,
  31. visible:false//属性窗口不显示
  32. },
  33. gameState:'white',
  34. fiveGroup:[],//五元组
  35. fiveGroupScore:[]//五元组分数
  36. },
  37. //重新开始
  38. startGame(){
  39. cc.director.loadScene("Game");
  40. },
  41. //返回菜单
  42. toMenu(){
  43. cc.director.loadScene("Menu");
  44. },
  45. onLoad: function () {
  46. this.overSprite.node.x = 10000;//让结束画面位于屏幕外
  47. var self = this;
  48. //初始化棋盘上225个棋子节点,并为每个节点添加事件
  49. for(var y = 0;y<15;y++){
  50. for(var x = 0;x < 15;x++){
  51. var newNode = cc.instantiate(this.chessPrefab);//复制Chess预制资源
  52. this.node.addChild(newNode);
  53. newNode.setPosition(cc.p(x*40+20,y*40+20));//根据棋盘和棋子大小计算使每个棋子节点位于指定位置
  54. newNode.tag = y*15+x;//根据每个节点的tag就可以算出其二维坐标
  55. newNode.on(cc.Node.EventType.TOUCH_END,function(event){
  56. self.touchChess = this;
  57. if(self.gameState === 'black' && this.getComponent(cc.Sprite).spriteFrame === null){
  58. this.getComponent(cc.Sprite).spriteFrame = self.blackSpriteFrame;//下子后添加棋子图片使棋子显示
  59. self.judgeOver();
  60. if(self.gameState == 'white'){
  61. self.scheduleOnce(function(){self.ai()},1);//延迟一秒电脑下棋
  62. }
  63. }
  64. });
  65. this.chessList.push(newNode);
  66. }
  67. }
  68. //开局白棋(电脑)在棋盘中央下一子
  69. this.chessList[112].getComponent(cc.Sprite).spriteFrame = this.whiteSpriteFrame;
  70. this.gameState = 'black';
  71. //添加五元数组
  72. //横向
  73. for(var y=0;y<15;y++){
  74. for(var x=0;x<11;x++){
  75. this.fiveGroup.push([y*15+x,y*15+x+1,y*15+x+2,y*15+x+3,y*15+x+4]);
  76. }
  77. }
  78. //纵向
  79. for(var x=0;x<15;x++){
  80. for(var y=0;y<11;y++){
  81. this.fiveGroup.push([y*15+x,(y+1)*15+x,(y+2)*15+x,(y+3)*15+x,(y+4)*15+x]);
  82. }
  83. }
  84. //右上斜向
  85. for(var b=-10;b<=10;b++){
  86. for(var x=0;x<11;x++){
  87. if(b+x<0||b+x>10){
  88. continue;
  89. }else{
  90. this.fiveGroup.push([(b+x)*15+x,(b+x+1)*15+x+1,(b+x+2)*15+x+2,(b+x+3)*15+x+3,(b+x+4)*15+x+4]);
  91. }
  92. }
  93. }
  94. //右下斜向
  95. for(var b=4;b<=24;b++){
  96. for(var y=0;y<11;y++){
  97. if(b-y<4||b-y>14){
  98. continue;
  99. }else{
  100. this.fiveGroup.push([y*15+b-y,(y+1)*15+b-y-1,(y+2)*15+b-y-2,(y+3)*15+b-y-3,(y+4)*15+b-y-4]);
  101. }
  102. }
  103. }
  104. },
  105. //电脑下棋逻辑
  106. ai:function(){
  107. //评分
  108. for(var i=0;i<this.fiveGroup.length;i++){
  109. var b=0;//五元组里黑棋的个数
  110. var w=0;//五元组里白棋的个数
  111. for(var j=0;j<5;j++){
  112. this.getComponent(cc.Sprite).spriteFrame
  113. if(this.chessList[this.fiveGroup[i][j]].getComponent(cc.Sprite).spriteFrame == this.blackSpriteFrame){
  114. b++;
  115. }else if(this.chessList[this.fiveGroup[i][j]].getComponent(cc.Sprite).spriteFrame == this.whiteSpriteFrame){
  116. w++;
  117. }
  118. }
  119. if(b+w==0){
  120. this.fiveGroupScore[i] = 7;
  121. }else if(b>0&&w>0){
  122. this.fiveGroupScore[i] = 0;
  123. }else if(b==0&&w==1){
  124. this.fiveGroupScore[i] = 35;
  125. }else if(b==0&&w==2){
  126. this.fiveGroupScore[i] = 800;
  127. }else if(b==0&&w==3){
  128. this.fiveGroupScore[i] = 15000;
  129. }else if(b==0&&w==4){
  130. this.fiveGroupScore[i] = 800000;
  131. }else if(w==0&&b==1){
  132. this.fiveGroupScore[i] = 15;
  133. }else if(w==0&&b==2){
  134. this.fiveGroupScore[i] = 400;
  135. }else if(w==0&&b==3){
  136. this.fiveGroupScore[i] = 1800;
  137. }else if(w==0&&b==4){
  138. this.fiveGroupScore[i] = 100000;
  139. }
  140. }
  141. //找最高分的五元组
  142. var hScore=0;
  143. var mPosition=0;
  144. for(var i=0;i<this.fiveGroupScore.length;i++){
  145. if(this.fiveGroupScore[i]>hScore){
  146. hScore = this.fiveGroupScore[i];
  147. mPosition = (function(x){//js闭包
  148. return x;
  149. })(i);
  150. }
  151. }
  152. //在最高分的五元组里找到最优下子位置
  153. var flag1 = false;//无子
  154. var flag2 = false;//有子
  155. var nPosition = 0;
  156. for(var i=0;i<5;i++){
  157. if(!flag1&&this.chessList[this.fiveGroup[mPosition][i]].getComponent(cc.Sprite).spriteFrame == null){
  158. nPosition = (function(x){return x})(i);
  159. }
  160. if(!flag2&&this.chessList[this.fiveGroup[mPosition][i]].getComponent(cc.Sprite).spriteFrame != null){
  161. flag1 = true;
  162. flag2 = true;
  163. }
  164. if(flag2&&this.chessList[this.fiveGroup[mPosition][i]].getComponent(cc.Sprite).spriteFrame == null){
  165. nPosition = (function(x){return x})(i);
  166. break;
  167. }
  168. }
  169. //在最最优位置下子
  170. this.chessList[this.fiveGroup[mPosition][nPosition]].getComponent(cc.Sprite).spriteFrame = this.whiteSpriteFrame;
  171. this.touchChess = this.chessList[this.fiveGroup[mPosition][nPosition]];
  172. this.judgeOver();
  173. },
  174. judgeOver:function(){
  175. var x0 = this.touchChess.tag % 15;
  176. var y0 = parseInt(this.touchChess.tag / 15);
  177. //判断横向
  178. var fiveCount = 0;
  179. for(var x = 0;x < 15;x++){
  180. if((this.chessList[y0*15+x].getComponent(cc.Sprite)).spriteFrame === this.touchChess.getComponent(cc.Sprite).spriteFrame){
  181. fiveCount++;
  182. if(fiveCount==5){
  183. if(this.gameState === 'black'){
  184. this.overLabel.string = "你赢了";
  185. this.overSprite.node.x = 0;
  186. }else{
  187. this.overLabel.string = "你输了";
  188. this.overSprite.node.x = 0;
  189. }
  190. this.gameState = 'over';
  191. return;
  192. }
  193. }else{
  194. fiveCount=0;
  195. }
  196. }
  197. //判断纵向
  198. fiveCount = 0;
  199. for(var y = 0;y < 15;y++){
  200. if((this.chessList[y*15+x0].getComponent(cc.Sprite)).spriteFrame === this.touchChess.getComponent(cc.Sprite).spriteFrame){
  201. fiveCount++;
  202. if(fiveCount==5){
  203. if(this.gameState === 'black'){
  204. this.overLabel.string = "你赢了";
  205. this.overSprite.node.x = 0;
  206. }else{
  207. this.overLabel.string = "你输了";
  208. this.overSprite.node.x = 0;
  209. }
  210. this.gameState = 'over';
  211. return;
  212. }
  213. }else{
  214. fiveCount=0;
  215. }
  216. }
  217. //判断右上斜向
  218. var f = y0 - x0;
  219. fiveCount = 0;
  220. for(var x = 0;x < 15;x++){
  221. if(f+x < 0 || f+x > 14){
  222. continue;
  223. }
  224. if((this.chessList[(f+x)*15+x].getComponent(cc.Sprite)).spriteFrame === this.touchChess.getComponent(cc.Sprite).spriteFrame){
  225. fiveCount++;
  226. if(fiveCount==5){
  227. if(this.gameState === 'black'){
  228. this.overLabel.string = "你赢了";
  229. this.overSprite.node.x = 0;
  230. }else{
  231. this.overLabel.string = "你输了";
  232. this.overSprite.node.x = 0;
  233. }
  234. this.gameState = 'over';
  235. return;
  236. }
  237. }else{
  238. fiveCount=0;
  239. }
  240. }
  241. //判断右下斜向
  242. f = y0 + x0;
  243. fiveCount = 0;
  244. for(var x = 0;x < 15;x++){
  245. if(f-x < 0 || f-x > 14){
  246. continue;
  247. }
  248. if((this.chessList[(f-x)*15+x].getComponent(cc.Sprite)).spriteFrame === this.touchChess.getComponent(cc.Sprite).spriteFrame){
  249. fiveCount++;
  250. if(fiveCount==5){
  251. if(this.gameState === 'black'){
  252. this.overLabel.string = "你赢了";
  253. this.overSprite.node.x = 0;
  254. }else{
  255. this.overLabel.string = "你输了";
  256. this.overSprite.node.x = 0;
  257. }
  258. this.gameState = 'over';
  259. return;
  260. }
  261. }else{
  262. fiveCount=0;
  263. }
  264. }
  265. //没有输赢交换下子顺序
  266. if(this.gameState === 'black'){
  267. this.gameState = 'white';
  268. }else{
  269. this.gameState = 'black';
  270. }
  271. }
  272. });

这里便用到了节点监听,节点数组,定时器和动态更换sprite图片。

新建Game脚本添加到ChessBoard节点下

3 注意

课程到这里就结束了,本课程部分资源来源于网络。

第一次写技术分享,如果大家有什么好的建议和问题请在下方留言区留言。

上传的附件 cloud_download cocos creator第一课.zip ( 729.91kb, 112次下载 )

keyboard_arrow_left上一篇 : PC微信逆向分析のWeTool内部探秘 深度学习 20、CNN : 下一篇keyboard_arrow_right



nomatter
2018-11-25 22:14:22
666~ cocos creator的系列教程
xiaozheng
2018-12-10 08:48:17
nice 给力
BoyMeetsGirl
2019-03-05 09:50:29
kevinl
2019-06-28 12:05:30
文件不全,有很多错误啊
兔子
2019-09-01 09:11:06
bangbangda
飘邈仙云
2019-09-06 10:41:27
不错学习了。
东邪黄药师
2019-09-16 14:17:51
if(self.gameState === 'black'&& this.getComponent(cc.Sprite).spriteFrame === null) 这段代码在调试时候出错,错误原因:Uncaught TypeError: Cannot read property 'getComponent' of undefined,求指教

eject