当前位置 : 主页 > 网络编程 > JavaScript >

详解CocosCreator制作射击游戏

来源:互联网 收集:自由互联 发布时间:2022-04-19
目录 场景布置 游戏资源 炮塔旋转 动态生成子弹 碰撞计算 增加效果 靶子移动 增加弹药库的显示 Common公共类 ResultDialog脚本(控制分数提示框) Common脚本 游戏重开 更改Common脚本 增加一些
目录
  • 场景布置
    • 游戏资源
  • 炮塔旋转
    • 动态生成子弹
      • 碰撞计算
        • 增加效果
          • 靶子移动
            • 增加弹药库的显示
              • Common公共类
                • ResultDialog脚本(控制分数提示框)
                  • Common脚本
                    • 游戏重开
                      • 更改Common脚本
                        • 增加一些细节
                          • 增加游戏声音以及炮塔激活的变化
                          • 图片切换
                          • 音效播放
                          • 子弹脚本

                        场景布置

                        游戏资源

                        炮塔旋转

                        机制与之前手柄实例的小车相同,使用touchmove监听触摸事件,

                        1. 获取触摸位置
                        2. 通过位置用signAngle方法将该位置与cc.v2(1,0)位置的角度差求出(记得要加负号,比较所得逆时针为负,赋值angle逆指针为正)。
                        3. 所求的的角度即为最终角度。

                         onLoad(){
                                //初始化为90度
                                this.node.angle=90;
                                this.node.on('touchstart',this.onTouchStart,this);
                                this.node.on('touchmove',this.onTouchMove,this);
                                this.node.on('touchend',this.onTouchEnd,this);
                                this.node.on('touchconcel',this.onTouchConcel,this);
                            }
                           
                            onTouchStart(e:cc.Event.EventTouch){
                                //获取开始的位置
                                this.starPos=this.node.parent.convertToNodeSpace(e.getLocation());
                                //获取炮口的初始角度
                                this.starAngle=this.node.angle;
                        
                            }
                            onTouchEnd(e:cc.Event.EventTouch){
                        
                            }
                            onTouchMove(e:cc.Event.EventTouch){
                                //获取触点当前的位置
                                let pos:cc.Vec2=this.node.parent.convertToNodeSpace(e.getLocation());
                        
                                //获取角度
                                //angle顺时针为负逆时针为正
                                let sweep_radian=pos.signAngle(this.starPos);//pos相对于starPose的角度p相对s顺时针为正
                                let sweep_angle=sweep_radian*180/Math.PI;//弧度制换算角度
                                
                                //让炮塔的角度指向最终的角度
                                let angle=this.starAngle-sweep_angle;
                              
                                //将角度限制在45~135之间
                                if(angle<45)angle=45;
                                if(angle>135)angle=135;
                        
                                cc.log("炮口摆动:"+sweep_angle+"最终角度位置:"+angle);
                                this.node.angle=angle;
                            }

                        动态生成子弹

                        1. 生成节点cc.Node,并增加组件addComponent(cc.Sprite)
                        2. 为组件的属性赋值,将图片的spriteFrame赋值
                        3. 将组件挂载在一个父节点下
                        4. 设置位置、角度等
                        5. 控制其运动可以导入新建的脚本,并将该脚本增加到动态生成节点的组件中
                         onTouchEnd(e:cc.Event.EventTouch){
                                this.fire();
                            }
                            onTouchConcel(e:cc.Event.EventTouch){
                        
                            }
                        
                            fire(){
                        
                                if(this.bulleteicon==null)return;
                                let bullet:cc.Node=new cc.Node();
                                let sprite:cc.Sprite=bullet.addComponent(cc.Sprite);
                                sprite.spriteFrame=this.bulleteicon;
                                
                        
                                //挂载到射击系统节点下
                                bullet.parent=this.node.parent;
                        
                                //设置相对父节点位置
                                let ration=this.node.angle*Math.PI/180;
                                let direction=cc.v2(Math.cos(ration),Math.sin(ration));
                                bullet.angle=this.node.angle;
                                let r=100;
                                bullet.setPosition(cc.v3(r*direction.x,r*direction.y,0));
                               
                                //附加脚本组件
                                 let script=bullet.addComponent(Buletet);
                                 script.explodeImg=this.explodeImg;
                                 script.direction=direction;
                        
                            }
                        
                         start () {
                                 this.schedule(this.onTimer,0.01);
                            }
                        
                            onTimer(){
                                if(this.node.y>300){
                                    this.unschedule(this.onTimer);
                                    this.explode();
                                   
                                    return;
                                }
                                let dx=this.direction.x*5;
                                let dy=this.direction.y*5;
                                this.node.y+=dy;
                                this.node.x+=dx;
                            }
                        
                            explode(){
                        
                                let sp:cc.Sprite=this.getComponent(cc.Sprite);
                                sp.spriteFrame=this.explodeImg;
                              
                                //将子弹缩小
                                this.node.scale=0.1;
                                //爆炸动画效果缓动系统
                                let self=this;
                                cc.tween(this.node)
                                    .to(0.5,{scale:1,opacity:0})
                                    .call(function(){
                                        self.afterExplode();
                                    })
                                    .start();
                        
                                    
                            }
                        
                            afterExplode(){
                                this.node.destroy();
                            }

                        本次bug:

                        1. 导入的类名与文件名不同, 注意重命名文件不会自动修改代码中的类名,需要修改两次
                        2. setposition()方法使用时参数写在了cc.v3的构造函数内,一定注意参数的位置

                        碰撞计算

                        计算子弹和靶标的相对位置,若小于范围,则判断为命中靶,执行命中的操作,否则判断为没有命中,执行没有命中的操作。

                        脚本需传入靶子节点,增加target属性

                           @property(cc.SpriteFrame)
                            explodeImg: cc.SpriteFrame = null;
                            
                            direction: cc.Vec2 = null;
                        
                            target: cc.Node = null;
                            onLoad() {
                        
                            }
                        
                            start() {
                                this.schedule(this.onTimer, 0.01);
                            }
                        
                            onTimer() {
                                if (this.node.y > 350) {
                                    if (this.isHit()) {
                                        //播放爆炸效果
                                        this.explode();
                                        console.log("命中靶");
                                    }
                                    else {
                                        console.log("脱靶");
                                        this.disMiss();
                                    }
                                    this.unschedule(this.onTimer);
                                    return;
                                }
                                let dx = this.direction.x * 5;
                                let dy = this.direction.y * 5;
                                this.node.y += dy;
                                this.node.x += dx;
                            }
                        
                            //判断是否命中
                            isHit(): boolean {
                                let targetPos: cc.Vec2 = this.geWorldLocation(this.target);
                                let selfPos: cc.Vec2 = this.geWorldLocation(this.node);
                                let distance = Math.abs(targetPos.x - selfPos.x);
                                console.log("靶标x=" + targetPos.x + " , 子弹x=" + selfPos.x);
                        
                                if (distance < 50) {
                                    return true;
                        
                                }
                                else {
                                    return false;
                                }
                        
                            }
                            explode() {
                        
                                let sp: cc.Sprite = this.getComponent(cc.Sprite);
                                sp.spriteFrame = this.explodeImg;
                        
                                //将子弹缩小
                                this.node.scale = 0.1;
                                //爆炸动画效果缓动系统
                                let self = this;
                                cc.tween(this.node)
                                    .to(0.5, { scale: 1, opacity: 0 })
                                    .call(function () {
                                        self.disMiss();
                                    })
                                    .start();
                        
                        
                            }
                        
                            geWorldLocation(node: cc.Node): cc.Vec2 {
                                let pos = node.getPosition();
                                //注意这里是node.parent。方法的调用者要是当前节点的坐标系
                                return node.parent.convertToWorldSpaceAR(pos);
                        
                            }
                        
                            disMiss() {
                                this.node.destroy();
                            }

                        本次bug:

                        获取世界坐标时,没有调用其父节点的坐标系,用了当前节点的坐标系,所以返回的依然是自身当前坐标系的值。记得转换世界坐标的方法调用者是当前节点的坐标系,一般为其父节点 return node.parent.convertToWorldSpaceAR(pos);(以锚点为原点)

                        增加效果

                        在靶子的节点下增加脚本,控制移动,左右来回移动
                        同时,当子弹命中后增加文字提示效果。

                        文字提示:

                         cheer() {
                                //创建节点并挂载
                                let node: cc.Node = new cc.Node();
                                node.parent = this.node.parent;//两者同一级,同一个父对象
                                let label: cc.Label = node.addComponent(cc.Label);
                                label.string = "+10分";
                        
                                //设置位置、透明度等
                                node.setPosition(cc.v3(0, 250, 0));
                                node.opacity = 200;
                                node.color = new cc.Color(255, 0, 0);
                        
                                //动效
                                cc.tween(node)
                                    .to(0.5, { scale: 1.5 })
                                    .to(0.2, { opacity: 0 })
                                    .call(function () {
                                        node.destroy();
                                    })
                                    .start();
                        
                            }
                        

                        靶子移动

                         update (dt) {
                                 let speed=3;
                                 if(this.isLeft){
                                     speed=-speed;
                                 }
                                 this.node.x+=speed;
                                 if(this.isLeft&&this.node.x<-350){
                                     this.isLeft=false;
                                 }
                                 if(!this.isLeft&&this.node.x>350){
                                    this.isLeft=true;
                                }
                            }

                        增加弹药库的显示

                        1. 增加弹药库节点,批量生成子弹图片(可用widget组件设置位置)
                        2. 增加减少子弹方法,并通过设置子弹图片的active属性来减少子弹。
                        3. 在炮塔的fire方法中调用减少子弹的方法

                        调用方法有两种,一种为在炮塔脚本中获取弹药库节点在调用,另一种为设置公共类,(静态变量),在onLoad()方法中就初始化该节点,然后直接调用。用后者。

                         @property(cc.SpriteFrame)
                            bulleteIcon: cc.SpriteFrame = null;
                            capacity: number = 10;
                            stockNumber: number = 10;
                        
                        
                            onLoad() {
                        
                                let space: number = this.node.width / this.capacity;
                                for (let i = 0; i < this.capacity; i++) {
                                    //生成图片
                                    let bulleteNode: cc.Node = new cc.Node();
                                    let bulleteSprite: cc.Sprite = bulleteNode.addComponent(cc.Sprite);
                                    bulleteSprite.spriteFrame = this.bulleteIcon;
                                    this.node.addChild(bulleteNode);
                        
                                    //设置位置
                                    bulleteNode.x += space * i + 10;
                                    bulleteNode.y = 0;
                        
                                }
                        
                            }
                        
                            start() {
                        
                            }
                        
                            consum(num: number) {
                                this.stockNumber -= num;
                                if (this.stockNumber < 0) {
                                    this.stockNumber = 0;
                                }
                                this.display();
                            }
                        
                            display() {
                                let nodes: cc.Node[] = this.node.children;
                                console.log(nodes.length);
                                for(let i=0;i<nodes.length;i++){
                                    if(i>=this.stockNumber){
                                        nodes[i].active=false;
                                    }
                                   
                                }
                            }

                        Common公共类

                          //静态类,全局变量,将所有会公用的变量、类定义在Common类中
                            static ammo:Ammo=null;
                        
                            onLoad() {
                                Common.ammo=cc.find('Canvas/弹药').getComponent('Ammo');
                                console.log(Common.ammo);
                            }
                        

                        此处bug:

                        cc.find()方法中记得用除法的斜杠。

                        子弹耗尽提示分数

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

                        ResultDialog脚本(控制分数提示框)

                         onLoad () {
                                 let replay:cc.Node=cc.find('Canvas/结束提示框/再玩一局');
                                 console.log(replay);
                                 replay.on('touchstart',this.dismiss,this);
                        
                                 this.node.on('touchstart',this.onTouchdisable,this);
                                 this.node.on('touchmove',this.onTouchdisable,this);
                                 this.node.on('touchend',this.onTouchdisable,this);
                           
                             }
                        
                             //显示提示框
                             show(){
                                this.node.active=true;  
                                let scoreNode : cc.Node = cc.find('分数框/分数', this.node);
                                let scoreLabel : cc.Label = scoreNode.getComponent(cc.Label);   
                                scoreLabel.string = Common.score + '分';   
                               
                                
                             }
                        
                             //隐藏提示框
                             dismiss(){
                                 this.node.active=false;
                             }
                        
                             //遮罩显示时屏蔽
                             onTouchdisable(e:cc.Event.EventTouch){
                                 e.stopPropagation();
                             }
                            start () {
                        
                            }
                        

                        Common脚本

                         //静态类,全局变量,将所有会公用的变量、类定义在Common类中
                            static ammo:Ammo=null;
                            static score : number = 0;
                            static resultdialog : ResultDialog = null;
                          
                        
                            onLoad() {
                                Common.resultdialog=cc.find('Canvas/结束提示框').getComponent('ResultDialog');
                                Common.ammo=cc.find('Canvas/弹药').getComponent('Ammo');
                            }

                        在Bullete方法中增加分数增加

                          if (this.isHit()) {
                                        //播放爆炸效果
                                        this.explode();
                                        //显示+10分
                                        this.cheer();
                                        //总分数+10
                                        Common.score += 10;
                                        console.log("命中靶");
                                    }

                        游戏重开

                        该小游戏比较简单,重开只需要重置弹药库节点即可,因此reset方法放在Ammo脚本中
                        在公共类中创建Ammo对象,设置静态方法,重置得分、以及调用Ammo的reset方法。

                        Ammo(弹药库类)脚本添加

                         reset(){
                                this.stockNumber=this.capacity;
                                this.display();
                            }

                        更改Common脚本

                         //静态类,全局变量,将所有会公用的变量、类定义在Common类中
                            static ammo:Ammo=null;
                            static score : number = 0;
                            static resultdialog : ResultDialog = null;
                          
                        
                            onLoad() {
                                Common.resultdialog=cc.find('Canvas/结束提示框').getComponent('ResultDialog');
                                Common.ammo=cc.find('Canvas/弹药').getComponent('Ammo');
                                console.log(Common.ammo);
                            }
                            static resetGame() {
                                Common.score=0;
                                Common.ammo.reset();
                            }
                        
                        

                        增加一些细节

                        增加游戏声音以及炮塔激活的变化

                        1.炮塔脚本增加属性

                         //音效
                            @property(cc.AudioClip)
                            audioFire: cc.AudioClip = null;
                            @property(cc.AudioClip)
                            audioExplode: cc.AudioClip = null;
                        
                            //炮塔图片
                            @property(cc.SpriteFrame)
                            iconNormal: cc.SpriteFrame = null;
                            @property(cc.SpriteFrame)
                            iconActive: cc.SpriteFrame = null;

                        图片切换

                         onTouchStart(e: cc.Event.EventTouch) {方法最后添加
                        	   //炮塔图片切换至激活
                                this.node.getComponent(cc.Sprite).spriteFrame = this.iconActive;	
                         onTouchEnd(e: cc.Event.EventTouch) {方法最后添加
                                  //图片恢复
                                this.node.getComponent(cc.Sprite).spriteFrame = this.iconNormal;
                              }

                        音效播放

                        fire(){   方法后添加
                        
                               //将子弹爆炸音频传送至子弹脚本
                                script.audioExplode = this.audioExplode;
                         if (this.audioFire != null) {
                                    cc.audioEngine.play(this.audioFire, false, 1);
                                }
                            }

                        播放音频的方法:==cc.audioEngine.play(this.audioFire, false, 1);==第二个参数为是否循环播放,第三个参数为音量大小

                        子弹脚本

                        //添加属性
                        @property(cc.SpriteFrame)
                        explodeImg: cc.SpriteFrame = null;
                        在判断子弹命中靶子的操作后添加
                        if(this.audioExplode!=null){
                        	cc.audioEngine.play(this.audioExplode,false,1);
                        }

                        以上就是详解CocosCreator制作射击游戏的详细内容,更多关于CocosCreator射击游戏的资料请关注自由互联其它相关文章!

                        上一篇:CocosCreator骨骼动画之龙骨DragonBones
                        下一篇:没有了
                        网友评论