詳解CocosCreator制作射擊游戲
場景布置
游戲資源



炮塔旋轉(zhuǎn)
機制與之前手柄實例的小車相同,使用touchmove監(jiān)聽觸摸事件,
- 獲取觸摸位置
- 通過位置用signAngle方法將該位置與cc.v2(1,0)位置的角度差求出(記得要加負號,比較所得逆時針為負,賦值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){
//獲取開始的位置
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;
}
動態(tài)生成子彈

- 生成節(jié)點cc.Node,并增加組件addComponent(cc.Sprite)
- 為組件的屬性賦值,將圖片的spriteFrame賦值
- 將組件掛載在一個父節(jié)點下
- 設(shè)置位置、角度等
- 控制其運動可以導(dǎo)入新建的腳本,并將該腳本增加到動態(tài)生成節(jié)點的組件中
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é)點下
bullet.parent=this.node.parent;
//設(shè)置相對父節(jié)點位置
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;
//爆炸動畫效果緩動系統(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)入的類名與文件名不同, 注意重命名文件不會自動修改代碼中的類名,需要修改兩次
- setposition()方法使用時參數(shù)寫在了cc.v3的構(gòu)造函數(shù)內(nèi),一定注意參數(shù)的位置
碰撞計算

計算子彈和靶標的相對位置,若小于范圍,則判斷為命中靶,執(zhí)行命中的操作,否則判斷為沒有命中,執(zhí)行沒有命中的操作。
腳本需傳入靶子節(jié)點,增加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;
//爆炸動畫效果緩動系統(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)用者要是當前節(jié)點的坐標系
return node.parent.convertToWorldSpaceAR(pos);
}
disMiss() {
this.node.destroy();
}
本次bug:
獲取世界坐標時,沒有調(diào)用其父節(jié)點的坐標系,用了當前節(jié)點的坐標系,所以返回的依然是自身當前坐標系的值。記得轉(zhuǎn)換世界坐標的方法調(diào)用者是當前節(jié)點的坐標系,一般為其父節(jié)點 return node.parent.convertToWorldSpaceAR(pos);(以錨點為原點)
增加效果

在靶子的節(jié)點下增加腳本,控制移動,左右來回移動
同時,當子彈命中后增加文字提示效果。
文字提示:
cheer() {
//創(chuàng)建節(jié)點并掛載
let node: cc.Node = new cc.Node();
node.parent = this.node.parent;//兩者同一級,同一個父對象
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);
//動效
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;
}
}
增加彈藥庫的顯示

- 增加彈藥庫節(jié)點,批量生成子彈圖片(可用widget組件設(shè)置位置)
- 增加減少子彈方法,并通過設(shè)置子彈圖片的active屬性來減少子彈。
- 在炮塔的fire方法中調(diào)用減少子彈的方法
調(diào)用方法有兩種,一種為在炮塔腳本中獲取彈藥庫節(jié)點在調(diào)用,另一種為設(shè)置公共類,(靜態(tài)變量),在onLoad()方法中就初始化該節(jié)點,然后直接調(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公共類
//靜態(tài)類,全局變量,將所有會公用的變量、類定義在Common類中
static ammo:Ammo=null;
onLoad() {
Common.ammo=cc.find('Canvas/彈藥').getComponent('Ammo');
console.log(Common.ammo);
}
此處bug:
cc.find()方法中記得用除法的斜杠。
子彈耗盡提示分數(shù)

- 創(chuàng)建遮罩層,將腳本類導(dǎo)入到Common類中,設(shè)置active屬性為false
- 在ResultDialog腳本 增加show方法,讓其active屬性變?yōu)閠rue同時將分數(shù)顯示在屏幕上。
- 在Bullete(控制子彈運動腳本)中判斷子彈數(shù)量是否<=0,并調(diào)用Common中show方法顯示分數(shù)提示框。
ResultDialog腳本(控制分數(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('分數(shù)框/分數(shù)', 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腳本
//靜態(tài)類,全局變量,將所有會公用的變量、類定義在Common類中
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方法中增加分數(shù)增加
if (this.isHit()) {
//播放爆炸效果
this.explode();
//顯示+10分
this.cheer();
//總分數(shù)+10
Common.score += 10;
console.log("命中靶");
}
游戲重開

該小游戲比較簡單,重開只需要重置彈藥庫節(jié)點即可,因此reset方法放在Ammo腳本中
在公共類中創(chuàng)建Ammo對象,設(shè)置靜態(tài)方法,重置得分、以及調(diào)用Ammo的reset方法。
Ammo(彈藥庫類)腳本添加
reset(){
this.stockNumber=this.capacity;
this.display();
}
更改Common腳本
//靜態(tài)類,全局變量,將所有會公用的變量、類定義在Common類中
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();
}
增加一些細節(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);==第二個參數(shù)為是否循環(huán)播放,第三個參數(shù)為音量大小
子彈腳本
//添加屬性
@property(cc.SpriteFrame)
explodeImg: cc.SpriteFrame = null;
在判斷子彈命中靶子的操作后添加
if(this.audioExplode!=null){
cc.audioEngine.play(this.audioExplode,false,1);
}
以上就是詳解CocosCreator制作射擊游戲的詳細內(nèi)容,更多關(guān)于CocosCreator射擊游戲的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
原生js FileReader對象實現(xiàn)圖片上傳本地預(yù)覽效果
這篇文章主要介紹了原生js FileReader對象實現(xiàn)圖片上傳本地預(yù)覽效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07
基于layui數(shù)據(jù)表格以及傳數(shù)據(jù)的方式
今天小編就為大家分享一篇基于layui數(shù)據(jù)表格以及傳數(shù)據(jù)的方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
javascript基礎(chǔ)知識大全 便于大家學習,也便于我自己查看
發(fā)些javascript基礎(chǔ)知識,便于大家學習,也便于我自己查看2012-08-08

