经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 游戏设计 » 查看文章
Cocos Creator经典游戏制作之:信使
来源:cnblogs  作者:优梦创客  时间:2019/8/16 10:36:44  对本文有异议

游戏原图


游戏介绍

  • 《信使(The Messenger)》是一款横版过关冒险游戏,此游戏在IGN上获得了8.0分的不错成绩,IGN编辑MITCHELL SALTZMAN认为《信使》拥有很多想带给玩家的内容。它是一款2D动作平台游戏,拥有华丽的美学,从8位风格无缝过渡到16位,游戏拥有一些搞笑的幽默场景,巧妙的对白,以及一个最佳的复古"芯片风”音乐。
  • 《信使(The Messenger)》无论从哪一方面来看都是受到了FC时代《忍者龙剑传》的影响,这款游戏的发行商Devolver Digital还邀请到了FC《忍者龙剑传》初代的制作人吉沢秀雄进行体验。

    游戏规则介绍

  • 游戏的操作与FC上的忍龙基本一致,左右移动、跳、落(从平台上落下)、攻击

    游戏开发过程

    搭建游戏UI界面

  • 在assets上创建一个文件夹名为Scence,这个文件夹用来存放场景,新建一个场景Scence改名为Game
  • 在Canvas上建一个Background节点,在这个节点上插入Sprite(图片精灵)组件,把背景图片拖进去,调整大小,这个就是游戏的背景
  • 在Canvas上新建节点Ground、Platform、Wall、Pitfall、Save、Lame,分别用来放置地面、平台、墙、陷阱、保存点、灯

    创建主角

  • 将一张忍者的图片拉至Canvas节点下,并重命名为Player

    制作动画特效

  • 在资源管理器的assets文件夹下创建一个子文件夹Animation,用来存放各种动画
  • 选择Player,在动画编辑器中点击按钮《添加Animation组件》为Player节点添加动画组件
  • 在点击按钮《新建Clip文件》为Player创建动画
  • 单击动画编辑器左上角的书写图标按钮开始编辑动画
  • 将主角的往右跑的4张图片组合成主角右跑动画(序列帧动画)
  • 再创建主角左跑动画、攻击动画、爬墙动画······

    写脚本

    Player脚本

    创建Player脚本
  • 在资源管理器的assets文件夹下创建一个子文件夹Script,用来存放各种脚本
  • 在资源管理器的Script文件夹下创建一个JavaScript脚本,重命名为Player
  • 将脚本Player挂在Player对象上

    编辑Player脚本
    定义玩家属性
  1. properties: {
  2. camera: { //摄像机
  3. type: cc.Node, //属性类型
  4. default: null, //默认值
  5. },
  6. jumpHeight: 200, //跳跃高度
  7. playerState: 2, //玩家状态 0站立 1跳 2落 3爬墙
  8. isRun: false, //是否跑动
  9. playerDirection: 1, //玩家方向 0左 1右
  10. mayJump: true, //能否跳跃
  11. speed: 0, //速度
  12. hp: 5, //生命值
  13. time: 0, //时间
  14. shinState: 0, //攀爬状态 0为不动 1为上爬 2为下滑
  15. isAttack: false, //是攻击状态
  16. whetherSpringback: true, //是否回弹
  17. },
给玩家创建各种能力(函数)
  • 攻击
  1. attack() {
  2. this.isAttack = true; //是攻击状态
  3. if (this.playerDirection == 0) { //如果玩家方向为左
  4. this.getComponent(cc.Animation).play("主角左攻动画");
  5. } else { //否则
  6. this.getComponent(cc.Animation).play("主角右攻动画");
  7. }
  8. this.node.getComponent(cc.BoxCollider).size = cc.size(140, 87); //获取主角的碰撞器,并设置包围盒大小(扩大包围盒,因为攻击时人物的包围盒要把刀包围进去)
  9. },
  • 左移
  1. leftMove() {
  2. this.node.x -= 10
  3. },
  • 右移
  1. rightMove() {
  2. this.node.x += 10;
  3. },
  • 攀爬,触碰墙壁进入爬墙状态后可使用爬墙方法
  1. shin() {
  2. if (this.shinState == 1) { //如果人物攀爬状态为上爬
  3. this.node.y += 3;
  4. } else if (this.shinState == 2) { //否则,就是人物状态为下滑
  5. this.node.y -= 9;
  6. }
  7. },
  • 跳跃,在平台、地面、空中时可执行
  1. jumpAction: null,
  2. jump() {
  3. this.jumpAction = cc.moveBy(0.5, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut());
  4. this.node.runAction(this.jumpAction);
  5. },
  • 爬墙跳,此方法只有攀爬在墙壁上时可使用
  1. jumpDistance: null,
  2. wallJump: null,
  3. WallJump() {
  4. if (this.playerDirection == 0) {
  5. this.jumpDistance = 200;
  6. } else {
  7. this.jumpDistance = -200;
  8. }
  9. this.wallJump = cc.moveBy(0.5, cc.v2(this.jumpDistance, this.jumpHeight)).easing(cc.easeCubicActionOut());
  10. this.node.runAction(this.wallJump);
  11. },
  • 翻跳上地面,当与墙壁结束碰撞时执行,用于结束攀爬进入站立状态
  1. WallGroundJump() {
  2. if (this.playerDirection == 0) {
  3. this.jumpDistance = -100;
  4. this.getComponent(cc.Animation).play("主角左跳动画");
  5. } else {
  6. this.jumpDistance = 100;
  7. this.getComponent(cc.Animation).play("主角右跳动画");
  8. }
  9. var WallJump = cc.moveBy(0.25, cc.v2(this.jumpDistance, this.jumpHeight / 2));
  10. this.node.runAction(WallJump);
  11. },
  • 落下,这是一个被动方法,只要玩家在空中且没有上升力,此方法就一直执行
  1. drop(speed) {
  2. this.node.y += speed;
  3. },
主角的碰撞处理
  • 碰撞开始
  1. onCollisionEnter: function (other, self) { //处理碰撞的方法
  2. if (other.node.group == "Ground" || other.node.group == "Platform") { //如果玩家碰到的是节点分组中的地面,或平台
  3. this.otherObject = other; //获取另一个对象(玩家碰撞的对象)
  4. if (this.speed < -30) { //如果速度过快
  5. this.hp = 0; //摔死
  6. }
  7. if (self.node.getComponent("Player").playerState == 2 && other.node.y < self.node.y) { //如果玩家状态为下落
  8. self.node.getComponent("Player").playerState = 0; //玩家状态变为站立
  9. self.node.y = other.node.y + other.node.height / 2 + 42; //回弹,防止人物脚陷入地面
  10. if (this.isRun) { //如果是跑动状态
  11. if (this.playerDirection == 0) { //如果玩家方向为左
  12. this.getComponent(cc.Animation).play("主角左跑动画");
  13. } else { //否则,就是玩家方向为右
  14. this.getComponent(cc.Animation).play("主角右跑动画");
  15. }
  16. } else { //否则就是玩家不是跑动状态
  17. if (this.playerDirection == 0) { //如果玩家方向为左
  18. this.getComponent(cc.Animation).play("主角左立动画");
  19. } else { //否则,就是玩家方向为右
  20. this.getComponent(cc.Animation).play("主角右立动画");
  21. }
  22. }
  23. } else if (self.node.getComponent("Player").shinState == 2) {
  24. self.node.getComponent("Player").playerState = 0; //玩家状态变为站立
  25. if (self.node.getComponent("Player").playerDirection == 0) {
  26. self.node.x += 20;
  27. this.getComponent(cc.Animation).play("主角左立动画");
  28. } else {
  29. self.node.x -= 20;
  30. this.getComponent(cc.Animation).play("主角右立动画");
  31. }
  32. }
  33. } else if (other.node.group == "Wall") { //如果玩家碰到的是节点分组中的墙壁
  34. if (this.isAttack) { //如果是攻击状态
  35. this.isRun = false; //停止跑动
  36. if (this.playerDirection == 0) { //如果人物方向为左
  37. this.node.x += 50; //攻击到墙壁往右回弹
  38. } else { //否则(人物方向为右)
  39. this.node.x -= 50; //攻击到墙壁往左回弹
  40. }
  41. } else {
  42. if (self.node.getComponent("Player").playerState == 0) {
  43. self.node.y += 20;
  44. }
  45. this.node.stopAction(this.wallJump);
  46. self.node.getComponent("Player").playerState = 3; //玩家状态变为爬墙
  47. this.isRun = false;
  48. this.shinState = 0; //攀爬状态为不动
  49. if (this.playerDirection == 0) { //如果人物方向为左
  50. self.node.x = other.node.x + other.node.width / 2 + 25; //回弹,防止人物陷入墙壁
  51. this.getComponent(cc.Animation).play("主角左贴动画");
  52. } else { //否则,就是人物方向为右
  53. self.node.x = other.node.x - other.node.width / 2 - 25; //回弹,防止人物陷入墙壁
  54. this.getComponent(cc.Animation).play("主角右贴动画");
  55. }
  56. }
  57. } else if (other.node.group == "Pitfall") { //如果玩家碰到的是节点分组中的陷阱
  58. this.hp = 0;
  59. this.time = 0;
  60. } else if (other.node.group == "Save") { //如果玩家碰到的是节点分组中的保存点
  61. this.playerX = this.node.x;
  62. this.playerY = this.node.y;
  63. }
  64. },
  • 碰撞过程中
  1. onCollisionStay: function (other, self) {
  2. if (other.node.group == "Lamp") {
  3. if (this.isAttack) {
  4. this.mayJump = true;
  5. this.node.getComponent(cc.BoxCollider).size = cc.size(55, 87); //获取主角的碰撞器,并设置包围盒大小(缩小包围盒,因为攻击到灯后结束攻击人物收刀后碰撞范围变小)
  6. this.isAttack = false;
  7. this.time = 0;
  8. }
  9. }
  10. },
  • 碰撞结束时
  1. onCollisionExit: function (other, self) {
  2. if (this.hp > 0) {
  3. if (other.node.group == "Ground" || other.node.group == "Platform") { //如果玩家与地面或平台碰撞结束
  4. if (self.node.getComponent("Player").playerState == 0) { //如果玩家状态为站立
  5. self.node.getComponent("Player").playerState = 2; //玩家状态变为下落
  6. if (this.playerDirection == 0) { //如果玩家方向为左
  7. this.getComponent(cc.Animation).play("主角左落动画");
  8. } else { //否则,就是玩家方向为右
  9. this.getComponent(cc.Animation).play("主角右落动画");
  10. }
  11. }
  12. } else if (other.node.group == "Wall" && self.node.getComponent("Player").playerState == 3) {
  13. this.playerState = 1; //玩家状态设为跳
  14. this.WallGroundJump(); //玩家执行翻跳上地面
  15. this.speed = 100; //刚起跳时速度快
  16. this.mayJump = false; //能跳设为false
  17. }
  18. }
  19. },
在update中刷新玩家
  1. update(dt) {
  2. if (this.hp == 0) { //如果玩家生命值为0,就是死了
  3. if (this.time == 0) {
  4. this.getComponent(cc.Animation).play("主角爆炸动画");
  5. }
  6. if (this.time < 0.4) {
  7. this.time += dt;
  8. } else if (this.time >= 0.4) {
  9. this.node.x = this.playerX;
  10. this.node.y = this.playerY + 30;
  11. this.speed = 0;
  12. this.playerState = 2;
  13. this.time = 0;
  14. this.isRun = false;
  15. this.hp = 5;
  16. }
  17. } else { //否则,就是活着
  18. //this.camera.x = this.node.x;
  19. //if (this.camera.x > this.node.x + 100) {
  20. this.camera.x = this.node.x //+ 100;
  21. //} else if (this.camera.x < this.node.x - 100) {
  22. this.camera.x = this.node.x //- 100;
  23. //}
  24. if (this.camera.y > this.node.y + 100) {
  25. this.camera.y = this.node.y + 100;
  26. } else if (this.camera.y < this.node.y - 100) {
  27. this.camera.y = this.node.y - 100;
  28. if (this.isAttack) { //是否是攻击状态
  29. this.time += dt; //计时
  30. if (this.time > 0.3) { //当时间大于0.3秒
  31. this.node.getComponent(cc.BoxCollider).size = cc.size(55, 87); //获取主角的碰撞器,并设置包围盒大小(缩小包围盒,因为攻击结束人物收刀后碰撞范围变小)
  32. this.isAttack = false; //将攻击状态变为false
  33. this.time = 0; //时间归零
  34. if (this.playerState == 0) { //如果玩家是站立状态
  35. if (this.isRun) { //如果玩家是跑动的的
  36. if (this.playerDirection == 0) { //如果玩家方向为左
  37. this.getComponent(cc.Animation).play("主角左跑动画");
  38. } else { //否则(就是玩家方向为右)
  39. this.getComponent(cc.Animation).play("主角右跑动画");
  40. }
  41. } else { //否则(就是站在不动)
  42. if (this.playerDirection == 0) { //如果玩家方向为左
  43. this.getComponent(cc.Animation).play("主角左立动画");
  44. } else { //否则(就是玩家方向为右)
  45. this.getComponent(cc.Animation).play("主角右立动画");
  46. }
  47. }
  48. } else { //否则(就是在空中)
  49. if (this.playerDirection == 0) { //如果玩家方向为左
  50. this.getComponent(cc.Animation).play("主角左落动画");
  51. } else { //否则(就是玩家方向为右)
  52. this.getComponent(cc.Animation).play("主角右落动画");
  53. }
  54. }
  55. }
  56. if (this.isRun) { //如果能跑动
  57. if (this.playerDirection == 0) { //如果玩家方向是左
  58. this.leftMove(); //向左跑
  59. } else { //否则
  60. this.rightMove(); //向右跑
  61. }
  62. if (this.playerState == 0) { //如果玩家状态为站立
  63. this.speed = 0; //速度归零
  64. this.mayJump = true; //能跳跃
  65. } else if (this.playerState == 1) { //如果玩家状态为跳
  66. this.speed -= dt * 400; //速度越来越慢
  67. if (this.speed <= 0) { //如果速度减少到小于等于0
  68. this.speed = 0; //速度归零
  69. this.node.stopAction(this.jumpAction); //停止跳
  70. this.playerState = 2; //玩家状态变为下落
  71. }
  72. } else if (this.playerState == 2) { //如果玩家状态为下落
  73. this.speed -= dt * 30; //下落状态下速度随时间变得越来越快
  74. this.drop(this.speed); //执行下落
  75. } else if (this.playerState == 3) { //如果玩家状态为爬墙
  76. this.speed = 0; //速度归零
  77. this.mayJump = true; //能跳跃
  78. this.shin(); //攀爬
  79. }
  80. }
  81. },

Game脚本

创建Game脚本
  • 在资源管理器的Script文件夹下创建一个JavaScript脚本,重命名为Game
  • 将脚本Game挂在Canvas上

    编辑Game脚本
    在onLoad中开启碰撞监听、监听系统事件
  1. onLoad() {
  2. var manager = cc.director.getCollisionManager(); //获取碰撞组件
  3. manager.enabled = true; //开启碰撞监听
  4. //manager.enabledDebugDraw = true; //开启碰撞组件Debug
  5. this.playerJS = this.Player.getComponent("Player"), //获取玩家脚本
  6. //系统事件,当键被按下时调用keyDown回调函数处理
  7. cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.keyDown, this);
  8. //系统事件,当键弹起时调用keyUp回调函数处理
  9. cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.keyUp, this);
  10. },
创建keyDown于keyUp方法用来控制玩家
  • keyDown方法
  1. playerJS: null, //玩家脚本
  2. keyDown(event) {
  3. if (this.playerJS.hp > 0) { //如果玩家hp大于0,就是玩家活着
  4. switch (event.keyCode) {
  5. case cc.macro.KEY.a:
  6. if (this.playerJS.isRun == false) { //如果能跑动是错误的
  7. if (this.playerJS.playerState != 3) { //如果玩家不为爬墙
  8. this.playerJS.isRun = true; //能跑动设为true
  9. this.playerJS.playerDirection = 0; //玩家方向设为左
  10. if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
  11. this.Player.getComponent(cc.Animation).play("主角左跑动画");
  12. }
  13. } else if (this.playerJS.playerDirection == 1) {
  14. this.playerJS.playerState = 1; //玩家状态设为跳
  15. this.playerJS.WallJump(); //玩家执行爬墙跳
  16. this.playerJS.playerDirection = 0; //玩家方向设为左
  17. this.playerJS.speed = 200; //刚起跳时速度快
  18. this.playerJS.mayJump = false; //能跳设为false
  19. this.Player.getComponent(cc.Animation).play("主角左跳动画");
  20. }
  21. }
  22. break;
  23. case cc.macro.KEY.d:
  24. if (this.playerJS.isRun == false) { //如果能跑动是错误的
  25. if (this.playerJS.playerState != 3) { //如果玩家不为爬墙
  26. this.playerJS.isRun = true; //能跑动设为true
  27. this.playerJS.playerDirection = 1; //玩家方向设为右
  28. if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
  29. this.Player.getComponent(cc.Animation).play("主角右跑动画");
  30. }
  31. } else if (this.playerJS.playerDirection == 0) {
  32. this.playerJS.playerState = 1; //玩家状态设为跳
  33. this.playerJS.WallJump(); //玩家执行爬墙跳
  34. this.playerJS.playerDirection = 1; //玩家方向设为右
  35. this.playerJS.speed = 200; //刚起跳时速度快
  36. this.playerJS.mayJump = false; //能跳设为false
  37. this.Player.getComponent(cc.Animation).play("主角右跳动画");
  38. }
  39. }
  40. break;
  41. case cc.macro.KEY.w:
  42. if (this.playerJS.playerState != 3) { //如果玩家状态不为爬墙
  43. if (this.playerJS.mayJump) { //如果能跳
  44. this.playerJS.playerState = 1; //玩家状态设为跳
  45. this.playerJS.jump(); //玩家执行跳
  46. this.playerJS.speed = 200; //刚起跳时速度快
  47. this.playerJS.mayJump = false; //能跳设为false
  48. if (this.playerJS.playerDirection == 0) { //如果玩家方向为左
  49. this.Player.getComponent(cc.Animation).play("主角左跳动画");
  50. } else { //否则,就是玩家方向为右
  51. this.Player.getComponent(cc.Animation).play("主角右跳动画");
  52. }
  53. }
  54. } else { //否则,就是玩家为爬墙状态
  55. this.playerJS.shinState = 1; //攀爬状态设为上爬
  56. if (this.playerJS.playerDirection == 0) { //如果玩家方向为左
  57. this.Player.getComponent(cc.Animation).play("主角左爬动画");
  58. } else { //否则,就是玩家方向为右
  59. this.Player.getComponent(cc.Animation).play("主角右爬动画");
  60. }
  61. }
  62. break;
  63. case cc.macro.KEY.s:
  64. if (this.playerJS.playerState == 0) { //如果玩家状态为站立
  65. if (this.playerJS.otherObject.node.group == "Ground") { //如果玩家依附在地面上
  66. //蹲下
  67. } else if (this.playerJS.otherObject.node.group == "Platform") { //如果玩家依附在平台上
  68. //this.Player.y -= 30; //玩家y坐标减,为了快速从平台上落下(此语句可有可无)
  69. this.playerJS.playerState = 2; //玩家状态变为下落状态
  70. if (this.playerJS.playerDirection == 0) { //如果玩家方向为左
  71. this.Player.getComponent(cc.Animation).play("主角左落动画");
  72. } else { //否则,就是玩家方向为右
  73. this.Player.getComponent(cc.Animation).play("主角右落动画");
  74. }
  75. }
  76. } else if (this.playerJS.playerState == 3) { //如果玩家状态为爬墙
  77. this.playerJS.shinState = 2 //攀爬状态设为下滑
  78. if (this.playerJS.playerDirection == 0) { //如果玩家方向为左
  79. this.Player.getComponent(cc.Animation).play("主角左爬动画");
  80. } else { //否则,就是玩家方向为右
  81. this.Player.getComponent(cc.Animation).play("主角右爬动画");
  82. }
  83. }
  84. break;
  85. case cc.macro.KEY.j:
  86. if (this.playerJS.isAttack == false) { //如果玩家攻击状态为false(已经是攻击状态不能再攻击,必须等攻击结束才能再次攻击)
  87. if (this.playerJS.playerState != 3) //如果玩家不为爬墙状态(不能在爬墙时攻击)
  88. {
  89. this.playerJS.attack(); //玩家执行攻击
  90. }
  91. }
  92. break;
  93. }
  94. }
  95. },
  • keyUp方法
  1. keyUp(event) {
  2. if (this.playerJS.hp > 0) { //如果玩家hp大于0,就是玩家活着
  3. switch (event.keyCode) {
  4. case cc.macro.KEY.a:
  5. if (this.playerJS.isRun && this.playerJS.playerDirection == 0) { //如果在跑动,并且玩家方向朝左
  6. this.playerJS.isRun = false; //能跑动设为false
  7. if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
  8. this.Player.getComponent(cc.Animation).play("主角左立动画");
  9. }
  10. }
  11. break;
  12. case cc.macro.KEY.d:
  13. if (this.playerJS.isRun && this.playerJS.playerDirection == 1) { //如果在跑动,并且玩家方向朝右
  14. this.playerJS.isRun = false; //能跑动设为false
  15. if (this.playerJS.playerState == 0) { //如果玩家狀態為站立
  16. this.Player.getComponent(cc.Animation).play("主角右立动画");
  17. }
  18. }
  19. break;
  20. case cc.macro.KEY.w:
  21. if (this.playerJS.playerState != 3) { //如果玩家状态不为爬墙
  22. if (this.playerJS.playerState == 1) { //如果玩家状态为跳,并且速度大于100
  23. if (this.playerJS.speed > 100) {
  24. this.playerJS.speed = 50; //玩家速度变慢
  25. }
  26. }
  27. } else { //否则,就是玩家为爬墙状态
  28. this.playerJS.shinState = 0; //攀爬状态设为不动
  29. if (this.playerJS.playerDirection == 0) { //如果玩家方向为左
  30. this.Player.getComponent(cc.Animation).play("主角左贴动画");
  31. } else { //否则,就是玩家方向为右
  32. this.Player.getComponent(cc.Animation).play("主角右贴动画");
  33. }
  34. }
  35. break;
  36. case cc.macro.KEY.s:
  37. if (this.playerJS.playerState == 3) {
  38. this.playerJS.shinState = 0; //攀爬状态设为不动
  39. if (this.playerJS.playerDirection == 0) { //如果玩家方向为左
  40. this.Player.getComponent(cc.Animation).play("主角左贴动画");
  41. } else { //否则,就是玩家方向为右
  42. this.Player.getComponent(cc.Animation).play("主角右贴动画");
  43. }
  44. }
  45. break;
  46. }
  47. }
  48. },

项目链接

https://github.com/VRVVR/ccz.git

原文链接:http://www.cnblogs.com/raymondking123/p/11361862.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号