場(chǎng)景布置
游戲資源
炮塔旋轉(zhuǎn)
機(jī)制與之前手柄實(shí)例的小車(chē)相同,使用touchmove監(jiān)聽(tīng)觸摸事件,
- 獲取觸摸位置
- 通過(guò)位置用signAngle方法將該位置與cc.v2(1,0)位置的角度差求出(記得要加負(fù)號(hào),比較所得逆時(shí)針為負(fù),賦值angle逆指針為正)。
- 所求的的角度即為最終角度。
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){ //獲取開(kāi)始的位置 this.starPos=this.node.parent.convertToNodeSpace(e.getLocation()); //獲取炮口的初始角度 this.starAngle=this.node.angle; } onTouchEnd(e:cc.Event.EventTouch){ } onTouchMove(e:cc.Event.EventTouch){ //獲取觸點(diǎn)當(dāng)前的位置 let pos:cc.Vec2=this.node.parent.convertToNodeSpace(e.getLocation()); //獲取角度 //angle順時(shí)針為負(fù)逆時(shí)針為正 let sweep_radian=pos.signAngle(this.starPos);//pos相對(duì)于starPose的角度p相對(duì)s順時(shí)針為正 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("炮口擺動(dòng):"+sweep_angle+"最終角度位置:"+angle); this.node.angle=angle; }
動(dòng)態(tài)生成子彈
- 生成節(jié)點(diǎn)cc.Node,并增加組件addComponent(cc.Sprite)
- 為組件的屬性賦值,將圖片的spriteFrame賦值
- 將組件掛載在一個(gè)父節(jié)點(diǎn)下
- 設(shè)置位置、角度等
- 控制其運(yùn)動(dòng)可以導(dǎo)入新建的腳本,并將該腳本增加到動(dòng)態(tài)生成節(jié)點(diǎn)的組件中
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; //掛載到射擊系統(tǒng)節(jié)點(diǎn)下 bullet.parent=this.node.parent; //設(shè)置相對(duì)父節(jié)點(diǎn)位置 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; //爆炸動(dòng)畫(huà)效果緩動(dòng)系統(tǒng) let self=this; cc.tween(this.node) .to(0.5,{scale:1,opacity:0}) .call(function(){ self.afterExplode(); }) .start(); } afterExplode(){ this.node.destroy(); }
本次bug:
- 導(dǎo)入的類(lèi)名與文件名不同, 注意重命名文件不會(huì)自動(dòng)修改代碼中的類(lèi)名,需要修改兩次
- setposition()方法使用時(shí)參數(shù)寫(xiě)在了cc.v3的構(gòu)造函數(shù)內(nèi),一定注意參數(shù)的位置
碰撞計(jì)算
計(jì)算子彈和靶標(biāo)的相對(duì)位置,若小于范圍,則判斷為命中靶,執(zhí)行命中的操作,否則判斷為沒(méi)有命中,執(zhí)行沒(méi)有命中的操作。
腳本需傳入靶子節(jié)點(diǎn),增加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("靶標(biāo)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; //爆炸動(dòng)畫(huà)效果緩動(dòng)系統(tǒng) 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。方法的調(diào)用者要是當(dāng)前節(jié)點(diǎn)的坐標(biāo)系 return node.parent.convertToWorldSpaceAR(pos); } disMiss() { this.node.destroy(); }
本次bug:
獲取世界坐標(biāo)時(shí),沒(méi)有調(diào)用其父節(jié)點(diǎn)的坐標(biāo)系,用了當(dāng)前節(jié)點(diǎn)的坐標(biāo)系,所以返回的依然是自身當(dāng)前坐標(biāo)系的值。記得轉(zhuǎn)換世界坐標(biāo)的方法調(diào)用者是當(dāng)前節(jié)點(diǎn)的坐標(biāo)系,一般為其父節(jié)點(diǎn) return node.parent.convertToWorldSpaceAR(pos);(以錨點(diǎn)為原點(diǎn))
增加效果
在靶子的節(jié)點(diǎn)下增加腳本,控制移動(dòng),左右來(lái)回移動(dòng)
同時(shí),當(dāng)子彈命中后增加文字提示效果。
文字提示:
cheer() { //創(chuàng)建節(jié)點(diǎn)并掛載 let node: cc.Node = new cc.Node(); node.parent = this.node.parent;//兩者同一級(jí),同一個(gè)父對(duì)象 let label: cc.Label = node.addComponent(cc.Label); label.string = "+10分"; //設(shè)置位置、透明度等 node.setPosition(cc.v3(0, 250, 0)); node.opacity = 200; node.color = new cc.Color(255, 0, 0); //動(dòng)效 cc.tween(node) .to(0.5, { scale: 1.5 }) .to(0.2, { opacity: 0 }) .call(function () { node.destroy(); }) .start(); }
靶子移動(dòng)
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; } }
增加彈藥庫(kù)的顯示
- 增加彈藥庫(kù)節(jié)點(diǎn),批量生成子彈圖片(可用widget組件設(shè)置位置)
- 增加減少子彈方法,并通過(guò)設(shè)置子彈圖片的active屬性來(lái)減少子彈。
- 在炮塔的fire方法中調(diào)用減少子彈的方法
調(diào)用方法有兩種,一種為在炮塔腳本中獲取彈藥庫(kù)節(jié)點(diǎn)在調(diào)用,另一種為設(shè)置公共類(lèi),(靜態(tài)變量),在onLoad()方法中就初始化該節(jié)點(diǎn),然后直接調(diào)用。用后者。
@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); //設(shè)置位置 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公共類(lèi)
//靜態(tài)類(lèi),全局變量,將所有會(huì)公用的變量、類(lèi)定義在Common類(lèi)中 static ammo:Ammo=null; onLoad() { Common.ammo=cc.find("Canvas/彈藥").getComponent("Ammo"); console.log(Common.ammo); }
此處bug:
cc.find()方法中記得用除法的斜杠。
子彈耗盡提示分?jǐn)?shù)
- 創(chuàng)建遮罩層,將腳本類(lèi)導(dǎo)入到Common類(lèi)中,設(shè)置active屬性為false
- 在ResultDialog腳本 增加show方法,讓其active屬性變?yōu)閠rue同時(shí)將分?jǐn)?shù)顯示在屏幕上。
- 在Bullete(控制子彈運(yùn)動(dòng)腳本)中判斷子彈數(shù)量是否<=0,并調(diào)用Common中show方法顯示分?jǐn)?shù)提示框。
ResultDialog腳本(控制分?jǐn)?shù)提示框)
onLoad () { let replay:cc.Node=cc.find("Canvas/結(jié)束提示框/再玩一局"); 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("分?jǐn)?shù)框/分?jǐn)?shù)", this.node); let scoreLabel : cc.Label = scoreNode.getComponent(cc.Label); scoreLabel.string = Common.score + "分"; } //隱藏提示框 dismiss(){ this.node.active=false; } //遮罩顯示時(shí)屏蔽 onTouchdisable(e:cc.Event.EventTouch){ e.stopPropagation(); } start () { }
Common腳本
//靜態(tài)類(lèi),全局變量,將所有會(huì)公用的變量、類(lèi)定義在Common類(lèi)中 static ammo:Ammo=null; static score : number = 0; static resultdialog : ResultDialog = null; onLoad() { Common.resultdialog=cc.find("Canvas/結(jié)束提示框").getComponent("ResultDialog"); Common.ammo=cc.find("Canvas/彈藥").getComponent("Ammo"); }
在Bullete方法中增加分?jǐn)?shù)增加
if (this.isHit()) { //播放爆炸效果 this.explode(); //顯示+10分 this.cheer(); //總分?jǐn)?shù)+10 Common.score += 10; console.log("命中靶"); }
游戲重開(kāi)
該小游戲比較簡(jiǎn)單,重開(kāi)只需要重置彈藥庫(kù)節(jié)點(diǎn)即可,因此reset方法放在Ammo腳本中
在公共類(lèi)中創(chuàng)建Ammo對(duì)象,設(shè)置靜態(tài)方法,重置得分、以及調(diào)用Ammo的reset方法。
Ammo(彈藥庫(kù)類(lèi))腳本添加
reset(){ this.stockNumber=this.capacity; this.display(); }
更改Common腳本
//靜態(tài)類(lèi),全局變量,將所有會(huì)公用的變量、類(lèi)定義在Common類(lèi)中 static ammo:Ammo=null; static score : number = 0; static resultdialog : ResultDialog = null; onLoad() { Common.resultdialog=cc.find("Canvas/結(jié)束提示框").getComponent("ResultDialog"); Common.ammo=cc.find("Canvas/彈藥").getComponent("Ammo"); console.log(Common.ammo); } static resetGame() { Common.score=0; Common.ammo.reset(); }
增加一些細(xì)節(jié)
增加游戲聲音以及炮塔激活的變化
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) {方法最后添加 //圖片恢復(fù) 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);==第二個(gè)參數(shù)為是否循環(huán)播放,第三個(gè)參數(shù)為音量大小
子彈腳本
//添加屬性 @property(cc.SpriteFrame) explodeImg: cc.SpriteFrame = null; 在判斷子彈命中靶子的操作后添加 if(this.audioExplode!=null){ cc.audioEngine.play(this.audioExplode,false,1); }
以上就是詳解CocosCreator制作射擊游戲的詳細(xì)內(nèi)容,更多關(guān)于CocosCreator射擊游戲的資料請(qǐng)關(guān)注服務(wù)器之家其它相關(guān)文章!
原文鏈接:https://blog.csdn.net/m0_46113894/article/details/111301163