• 售前

  • 售后

热门帖子
入门百科

详解CocosCreator制作射击游戏

[复制链接]
徐殿军 显示全部楼层 发表于 2021-10-26 13:11:16 |阅读模式 打印 上一主题 下一主题
目录


  • 场景摆设

    • 游戏资源

  • 炮塔旋转
  • 动态天生子弹
  • 碰撞盘算
  • 增加效果
  • 靶子移动
  • 增加弹药库的体现
  • Common公共类
  • ResultDialog脚本(控制分数提示框)
  • Common脚本
  • 游戏重开
  • 更改Common脚本
  • 增加一些细节

    • 增加游戏声音以及炮塔激活的厘革
    • 图片切换
    • 音效播放
    • 子弹脚本


场景摆设


游戏资源





炮塔旋转

机制与之前手柄实例的小车相同,利用touchmove监听触摸变乱,
       
  • 获取触摸位置   
  • 通过位置用signAngle方法将该位置与cc.v2(1,0)位置的角度差求出(记得要加负号,比力所得逆时针为负,赋值angle逆指针为正)。   
  • 所求的的角度即为最终角度。
  1. onLoad(){
  2.         //初始化为90度
  3.         this.node.angle=90;
  4.         this.node.on('touchstart',this.onTouchStart,this);
  5.         this.node.on('touchmove',this.onTouchMove,this);
  6.         this.node.on('touchend',this.onTouchEnd,this);
  7.         this.node.on('touchconcel',this.onTouchConcel,this);
  8.     }
  9.    
  10.     onTouchStart(e:cc.Event.EventTouch){
  11.         //获取开始的位置
  12.         this.starPos=this.node.parent.convertToNodeSpace(e.getLocation());
  13.         //获取炮口的初始角度
  14.         this.starAngle=this.node.angle;
  15.     }
  16.     onTouchEnd(e:cc.Event.EventTouch){
  17.     }
  18.     onTouchMove(e:cc.Event.EventTouch){
  19.         //获取触点当前的位置
  20.         let pos:cc.Vec2=this.node.parent.convertToNodeSpace(e.getLocation());
  21.         //获取角度
  22.         //angle顺时针为负逆时针为正
  23.         let sweep_radian=pos.signAngle(this.starPos);//pos相对于starPose的角度p相对s顺时针为正
  24.         let sweep_angle=sweep_radian*180/Math.PI;//弧度制换算角度
  25.         
  26.         //让炮塔的角度指向最终的角度
  27.         let angle=this.starAngle-sweep_angle;
  28.       
  29.         //将角度限制在45~135之间
  30.         if(angle<45)angle=45;
  31.         if(angle>135)angle=135;
  32.         cc.log("炮口摆动:"+sweep_angle+"最终角度位置:"+angle);
  33.         this.node.angle=angle;
  34.     }
复制代码
动态天生子弹


       
  • 天生节点cc.Node,并增加组件addComponent(cc.Sprite)   
  • 为组件的属性赋值,将图片的spriteFrame赋值   
  • 将组件挂载在一个父节点下   
  • 设置位置、角度等   
  • 控制其运动可以导入新建的脚本,并将该脚本增加到动态天生节点的组件中
  1. onTouchEnd(e:cc.Event.EventTouch){
  2.         this.fire();
  3.     }
  4.     onTouchConcel(e:cc.Event.EventTouch){
  5.     }
  6.     fire(){
  7.         if(this.bulleteicon==null)return;
  8.         let bullet:cc.Node=new cc.Node();
  9.         let sprite:cc.Sprite=bullet.addComponent(cc.Sprite);
  10.         sprite.spriteFrame=this.bulleteicon;
  11.         
  12.         //挂载到射击系统节点下
  13.         bullet.parent=this.node.parent;
  14.         //设置相对父节点位置
  15.         let ration=this.node.angle*Math.PI/180;
  16.         let direction=cc.v2(Math.cos(ration),Math.sin(ration));
  17.         bullet.angle=this.node.angle;
  18.         let r=100;
  19.         bullet.setPosition(cc.v3(r*direction.x,r*direction.y,0));
  20.       
  21.         //附加脚本组件
  22.          let script=bullet.addComponent(Buletet);
  23.          script.explodeImg=this.explodeImg;
  24.          script.direction=direction;
  25.     }
复制代码
  1. start () {
  2.          this.schedule(this.onTimer,0.01);
  3.     }
  4.     onTimer(){
  5.         if(this.node.y>300){
  6.             this.unschedule(this.onTimer);
  7.             this.explode();
  8.            
  9.             return;
  10.         }
  11.         let dx=this.direction.x*5;
  12.         let dy=this.direction.y*5;
  13.         this.node.y+=dy;
  14.         this.node.x+=dx;
  15.     }
  16.     explode(){
  17.         let sp:cc.Sprite=this.getComponent(cc.Sprite);
  18.         sp.spriteFrame=this.explodeImg;
  19.       
  20.         //将子弹缩小
  21.         this.node.scale=0.1;
  22.         //爆炸动画效果缓动系统
  23.         let self=this;
  24.         cc.tween(this.node)
  25.             .to(0.5,{scale:1,opacity:0})
  26.             .call(function(){
  27.                 self.afterExplode();
  28.             })
  29.             .start();
  30.             
  31.     }
  32.     afterExplode(){
  33.         this.node.destroy();
  34.     }
复制代码
本次bug:
       
  • 导入的类名与文件名差别, 留意重命名文件不会自动修改代码中的类名,必要修改两次   
  • setposition()方法利用时参数写在了cc.v3的构造函数内,肯定留意参数的位置

碰撞盘算


盘算子弹和靶标的相对位置,若小于范围,则判断为命中靶,执行命中的操作,否则判断为没有命中,执行没有命中的操作。
  1. 脚本需传入靶子节点,增加target属性
复制代码
  1.    @property(cc.SpriteFrame)
  2.     explodeImg: cc.SpriteFrame = null;
  3.    
  4.     direction: cc.Vec2 = null;
  5.     target: cc.Node = null;
  6.     onLoad() {
  7.     }
  8.     start() {
  9.         this.schedule(this.onTimer, 0.01);
  10.     }
  11.     onTimer() {
  12.         if (this.node.y > 350) {
  13.             if (this.isHit()) {
  14.                 //播放爆炸效果
  15.                 this.explode();
  16.                 console.log("命中靶");
  17.             }
  18.             else {
  19.                 console.log("脱靶");
  20.                 this.disMiss();
  21.             }
  22.             this.unschedule(this.onTimer);
  23.             return;
  24.         }
  25.         let dx = this.direction.x * 5;
  26.         let dy = this.direction.y * 5;
  27.         this.node.y += dy;
  28.         this.node.x += dx;
  29.     }
  30.     //判断是否命中
  31.     isHit(): boolean {
  32.         let targetPos: cc.Vec2 = this.geWorldLocation(this.target);
  33.         let selfPos: cc.Vec2 = this.geWorldLocation(this.node);
  34.         let distance = Math.abs(targetPos.x - selfPos.x);
  35.         console.log("靶标x=" + targetPos.x + " , 子弹x=" + selfPos.x);
  36.         if (distance < 50) {
  37.             return true;
  38.         }
  39.         else {
  40.             return false;
  41.         }
  42.     }
  43.     explode() {
  44.         let sp: cc.Sprite = this.getComponent(cc.Sprite);
  45.         sp.spriteFrame = this.explodeImg;
  46.         //将子弹缩小
  47.         this.node.scale = 0.1;
  48.         //爆炸动画效果缓动系统
  49.         let self = this;
  50.         cc.tween(this.node)
  51.             .to(0.5, { scale: 1, opacity: 0 })
  52.             .call(function () {
  53.                 self.disMiss();
  54.             })
  55.             .start();
  56.     }
  57.     geWorldLocation(node: cc.Node): cc.Vec2 {
  58.         let pos = node.getPosition();
  59.         //注意这里是node.parent。方法的调用者要是当前节点的坐标系
  60.         return node.parent.convertToWorldSpaceAR(pos);
  61.     }
  62.     disMiss() {
  63.         this.node.destroy();
  64.     }
复制代码
本次bug:
  1. 获取世界坐标时,没有调用其父节点的坐标系,用了当前节点的坐标系,所以返回的依然是自身当前坐标系的值。<mark>记得转换世界坐标的方法调用者是当前节点的坐标系,一般为其父节点</mark> return node.parent.convertToWorldSpaceAR(pos);(以锚点为原点)
复制代码
增加效果


在靶子的节点下增加脚本,控制移动,左右往返移动
同时,当子弹命中后增加笔墨提示效果。
笔墨提示:
  1. cheer() {
  2.         //创建节点并挂载
  3.         let node: cc.Node = new cc.Node();
  4.         node.parent = this.node.parent;//两者同一级,同一个父对象
  5.         let label: cc.Label = node.addComponent(cc.Label);
  6.         label.string = "+10分";
  7.         //设置位置、透明度等
  8.         node.setPosition(cc.v3(0, 250, 0));
  9.         node.opacity = 200;
  10.         node.color = new cc.Color(255, 0, 0);
  11.         //动效
  12.         cc.tween(node)
  13.             .to(0.5, { scale: 1.5 })
  14.             .to(0.2, { opacity: 0 })
  15.             .call(function () {
  16.                 node.destroy();
  17.             })
  18.             .start();
  19.     }
复制代码
靶子移动
  1. update (dt) {
  2.          let speed=3;
  3.          if(this.isLeft){
  4.              speed=-speed;
  5.          }
  6.          this.node.x+=speed;
  7.          if(this.isLeft&&this.node.x<-350){
  8.              this.isLeft=false;
  9.          }
  10.          if(!this.isLeft&&this.node.x>350){
  11.             this.isLeft=true;
  12.         }
  13.     }
复制代码
增加弹药库的体现


       
  • 增加弹药库节点,批量天生子弹图片(可用widget组件设置位置)   
  • 增加淘汰子弹方法,并通过设置子弹图片的active属性来淘汰子弹。   
  • 在炮塔的fire方法中调用淘汰子弹的方法
  1. 调用方法有两种,一种为在炮塔脚本中获取弹药库节点在调用,另一种为设置公共类,(静态变量),在onLoad()方法中就初始化该节点,然后直接调用。用后者。
复制代码
  1. @property(cc.SpriteFrame)
  2.     bulleteIcon: cc.SpriteFrame = null;
  3.     capacity: number = 10;
  4.     stockNumber: number = 10;
  5.     onLoad() {
  6.         let space: number = this.node.width / this.capacity;
  7.         for (let i = 0; i < this.capacity; i++) {
  8.             //生成图片
  9.             let bulleteNode: cc.Node = new cc.Node();
  10.             let bulleteSprite: cc.Sprite = bulleteNode.addComponent(cc.Sprite);
  11.             bulleteSprite.spriteFrame = this.bulleteIcon;
  12.             this.node.addChild(bulleteNode);
  13.             //设置位置
  14.             bulleteNode.x += space * i + 10;
  15.             bulleteNode.y = 0;
  16.         }
  17.     }
  18.     start() {
  19.     }
  20.     consum(num: number) {
  21.         this.stockNumber -= num;
  22.         if (this.stockNumber < 0) {
  23.             this.stockNumber = 0;
  24.         }
  25.         this.display();
  26.     }
  27.     display() {
  28.         let nodes: cc.Node[] = this.node.children;
  29.         console.log(nodes.length);
  30.         for(let i=0;i<nodes.length;i++){
  31.             if(i>=this.stockNumber){
  32.                 nodes[i].active=false;
  33.             }
  34.            
  35.         }
  36.     }
复制代码
Common公共类
  1.   //静态类,全局变量,将所有会公用的变量、类定义在Common类中
  2.     static ammo:Ammo=null;
  3.     onLoad() {
  4.         Common.ammo=cc.find('Canvas/弹药').getComponent('Ammo');
  5.         console.log(Common.ammo);
  6.     }
复制代码
此处bug:
  1. <mark>cc.find()方法中记得用除法的斜杠。</mark>
复制代码
子弹耗尽提示分数

       
  • 创建遮罩层,将脚本类导入到Common类中,设置active属性为false   
  • 在ResultDialog脚本 增加show方法,让其active属性变为true同时将分数体现在屏幕上。   
  • 在Bullete(控制子弹运动脚本)中判断子弹数量是否<=0,并调用Common中show方法体现分数提示框。

ResultDialog脚本(控制分数提示框)
  1. onLoad () {
  2.          let replay:cc.Node=cc.find('Canvas/结束提示框/再玩一局');
  3.          console.log(replay);
  4.          replay.on('touchstart',this.dismiss,this);
  5.          this.node.on('touchstart',this.onTouchdisable,this);
  6.          this.node.on('touchmove',this.onTouchdisable,this);
  7.          this.node.on('touchend',this.onTouchdisable,this);
  8.    
  9.      }
  10.      //显示提示框
  11.      show(){
  12.         this.node.active=true;  
  13.         let scoreNode : cc.Node = cc.find('分数框/分数', this.node);
  14.         let scoreLabel : cc.Label = scoreNode.getComponent(cc.Label);   
  15.         scoreLabel.string = Common.score + '分';   
  16.       
  17.         
  18.      }
  19.      //隐藏提示框
  20.      dismiss(){
  21.          this.node.active=false;
  22.      }
  23.      //遮罩显示时屏蔽
  24.      onTouchdisable(e:cc.Event.EventTouch){
  25.          e.stopPropagation();
  26.      }
  27.     start () {
  28.     }
复制代码
Common脚本
  1. //静态类,全局变量,将所有会公用的变量、类定义在Common类中
  2.     static ammo:Ammo=null;
  3.     static score : number = 0;
  4.     static resultdialog : ResultDialog = null;
  5.   
  6.     onLoad() {
  7.         Common.resultdialog=cc.find('Canvas/结束提示框').getComponent('ResultDialog');
  8.         Common.ammo=cc.find('Canvas/弹药').getComponent('Ammo');
  9.     }
复制代码
在Bullete方法中增加分数增加
  1.   if (this.isHit()) {
  2.                 //播放爆炸效果
  3.                 this.explode();
  4.                 //显示+10分
  5.                 this.cheer();
  6.                 //总分数+10
  7.                 Common.score += 10;
  8.                 console.log("命中靶");
  9.             }
复制代码
游戏重开


该小游戏比力简朴,重开只必要重置弹药库节点即可,因此reset方法放在Ammo脚本中
在公共类中创建Ammo对象,设置静态方法,重置得分、以及调用Ammo的reset方法。
Ammo(弹药库类)脚本添加
  1. reset(){
  2.         this.stockNumber=this.capacity;
  3.         this.display();
  4.     }
复制代码
更改Common脚本
  1. //静态类,全局变量,将所有会公用的变量、类定义在Common类中
  2.     static ammo:Ammo=null;
  3.     static score : number = 0;
  4.     static resultdialog : ResultDialog = null;
  5.   
  6.     onLoad() {
  7.         Common.resultdialog=cc.find('Canvas/结束提示框').getComponent('ResultDialog');
  8.         Common.ammo=cc.find('Canvas/弹药').getComponent('Ammo');
  9.         console.log(Common.ammo);
  10.     }
  11.     static resetGame() {
  12.         Common.score=0;
  13.         Common.ammo.reset();
  14.     }
复制代码
增加一些细节



增加游戏声音以及炮塔激活的厘革

1.炮塔脚本增加属性
  1. //音效
  2.     @property(cc.AudioClip)
  3.     audioFire: cc.AudioClip = null;
  4.     @property(cc.AudioClip)
  5.     audioExplode: cc.AudioClip = null;
  6.     //炮塔图片
  7.     @property(cc.SpriteFrame)
  8.     iconNormal: cc.SpriteFrame = null;
  9.     @property(cc.SpriteFrame)
  10.     iconActive: cc.SpriteFrame = null;
复制代码
图片切换
  1. onTouchStart(e: cc.Event.EventTouch) {方法最后添加
  2.            //炮塔图片切换至激活
  3.         this.node.getComponent(cc.Sprite).spriteFrame = this.iconActive;       
复制代码
  1. onTouchEnd(e: cc.Event.EventTouch) {方法最后添加
  2.           //图片恢复
  3.         this.node.getComponent(cc.Sprite).spriteFrame = this.iconNormal;
  4.       }
复制代码
音效播放
  1. fire(){   方法后添加
  2.        //将子弹爆炸音频传送至子弹脚本
  3.         script.audioExplode = this.audioExplode;
  4. if (this.audioFire != null) {
  5.             cc.audioEngine.play(this.audioFire, false, 1);
  6.         }
  7.     }
复制代码
  1. 播放音频的方法:==cc.audioEngine.play(this.audioFire, false, 1);==第二个参数为是否循环播放,第三个参数为音量大小
复制代码
子弹脚本
  1. //添加属性
  2. @property(cc.SpriteFrame)
  3. explodeImg: cc.SpriteFrame = null;
  4. 在判断子弹命中靶子的操作后添加
  5. if(this.audioExplode!=null){
  6.         cc.audioEngine.play(this.audioExplode,false,1);
  7. }
复制代码
以上就是详解CocosCreator制作射击游戏的具体内容,更多关于CocosCreator射击游戏的资料请关注草根技能分享别的相关文章!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作