文章目录
- 前言
- 添加音频脚本并绑定组件
- 音频脚本
- 子弹发射播放音频并将播放音频方法在管理脚本中暴露
- 点击按钮播放音频
- 敌机销毁的时候播放音频
- 玩家飞机销毁播放音频
- 完整代码
前言
游戏音效会分为游戏开始音效,玩家飞机死亡音效,敌机死亡音效,以及子弹发射音效和按钮点击音效
添加音频脚本并绑定组件
首先创建个空节点audio用于存放音频,然后将背景音乐挂在其下
音频脚本
创建脚本AudioManager.ts,将需要的音频用一个数组接收,
将音频脚本挂入引擎
子弹发射播放音频并将播放音频方法在管理脚本中暴露
将音频节点挂入
点击按钮播放音频
敌机销毁的时候播放音频
玩家飞机销毁播放音频
在引擎中添加音频
完整代码
- AudioManager
import { _decorator, Component, Node, AudioClip, AudioSource } from 'cc';
const { ccclass, property } = _decorator;
/**
* Predefined variables
* Name = AudioManager
* DateTime = Fri Nov 26 2021 16:11:32 GMT+0800 (China Standard Time)
* Author = mywayday
* FileBasename = AudioManager.ts
* FileBasenameNoExtension = AudioManager
* URL = db://assets/script/framework/AudioManager.ts
* ManualUrl = https://docs.cocos.com/creator/3.3/manual/en/
*
*/
interface IAudioMap {
[name: string]: AudioClip;
}
@ccclass('AudioManager')
export class AudioManager extends Component {
//音频列表
@property([AudioClip])
public audioList: AudioClip[] = [];
private _dict: IAudioMap = {};//音频集合
private _audioSource: AudioSource = null;
start() {
//将音频存入集合中
for (let i = 0; i < this.audioList.length; i++) {
const element = this.audioList[i];
this._dict[element.name] = element;
}
this._audioSource = this.getComponent(AudioSource);//音频组件初始化
}
//播放音频
public play(name: string) {
const audioClip = this._dict[name];
if (audioClip !== undefined) {
this._audioSource.playOneShot(audioClip);
}
}
// update (deltaTime: number) {
// // [4]
// }
}
/**
* [1] Class member could be defined like this.
* [2] Use `property` decorator if your want the member to be serializable.
* [3] Your initialization goes here.
* [4] Your update function goes here.
*
* Learn more about scripting: https://docs.cocos.com/creator/3.3/manual/en/scripting/
* Learn more about CCClass: https://docs.cocos.com/creator/3.3/manual/en/scripting/ccclass.html
* Learn more about life-cycle callbacks: https://docs.cocos.com/creator/3.3/manual/en/scripting/life-cycle-callbacks.html
*/
- GameManager
import { _decorator, Component, Node, Prefab, instantiate, math, Vec3, BoxCollider, macro, Label } from 'cc';
import { Bullet } from '../bullet/Bullet';
import { BulletProp } from '../bullet/BulletProp';
import { EnemyPlane } from '../plane/EnemyPlane';
import { SelfPlane } from '../plane/SelfPlane';
import { AudioManager } from './AudioManager';
import { Constant } from './Constant';
const { ccclass, property } = _decorator;
@ccclass('GameManager')
export class GameManager extends Component {
// 关联玩家飞机
@property(SelfPlane)
public playerPlane: SelfPlane = null;
// bullet 关联所有子弹
@property(Prefab)
public bullet01: Prefab = null;
@property(Prefab)
public bullet02: Prefab = null;
@property(Prefab)
public bullet03: Prefab = null;
@property(Prefab)
public bullet04: Prefab = null;
@property(Prefab)
public bullet05: Prefab = null;
// 射击周期
@property
public shootTime = 0.3;
// 子弹移动速度
@property
public bulletSpeed = 1;
//子弹管理节点
@property(Node)
public bulletRoot: Node = null;
// enemy/** 关联敌机 */
@property(Prefab)
public enemy01: Prefab = null;
@property(Prefab)
public enemy02: Prefab = null;
@property
public createEnemtTime = 1;//创建敌机时间
@property
public enemy1Speed = 0.5;//敌机1速度
@property
public enemy2Speed = 0.7;//敌机2速度
// prop 定义道具属性
@property(Prefab)
public bulletPropM: Prefab = null;
@property(Prefab)
public bulletPropH: Prefab = null;
@property(Prefab)
public bulletPropS: Prefab = null;
@property
public bulletPropSpeed = 0.3;//道具速度
// ui
@property(Node)
public gamePage: Node = null;//游戏界面
@property(Node)
public gameOverPage: Node = null;//游戏结束界面
@property(Label)
public gameOverScore: Label = null;//游戏结算分数
@property(Label)
public gameScore: Label = null;//游戏分数组件
// audio
@property(AudioManager)
public audioEffect: AudioManager = null;//获取音频脚本
public isGameStart = false;//游戏是否开始
private _currShootTime = 0;
// 是否触摸屏幕
private _isShooting = false;
private _currCreateEnemyTime = 0//当前创建的敌机时间
private _combinationInterval = Constant.Combination.PLAN1//组合间隔状态
private _bulletType = Constant.BulletPropType.BULLET_M;//子弹类型
private _score = 0;//分数
start() {
this._init();
}
update(deltaTime: number) {
//判断游戏是否开始
if (!this.isGameStart) {
return;
}
//判断玩家是否死亡
if (this.playerPlane.isDie) {
this.gameOver();
return;
}
// 这步加时间是为了发射子弹
this._currShootTime += deltaTime;
// 判断是触摸状态 并且射击时间大于我们的周期 发射子弹
if (this._isShooting && this._currShootTime > this.shootTime) {
/**
* 根据吃道具类型来产生子弹
*/
if (this._bulletType === Constant.BulletPropType.BULLET_H) {
this.createPlayerBulletH();
} else if (this._bulletType === Constant.BulletPropType.BULLET_S) {
this.createPlayerBulletS();
} else {
this.createPlayerBulletM();
}
//发射子弹播放声音由于音频只有1,2子弹却有3个,这里是处理逻辑
const name = 'bullet' + (this._bulletType % 2 + 1);
this.playAudioEffect(name);
this._currShootTime = 0;
}
this._currCreateEnemyTime += deltaTime
//判断组合方式
if (this._combinationInterval == Constant.Combination.PLAN1) {
//只创建单一的飞机 0-10秒飞机创建
if (this._currCreateEnemyTime > this.createEnemtTime) {//普通飞机创建
this.createEnemyPlane()
this._currCreateEnemyTime = 0
}
} else if (this._combinationInterval == Constant.Combination.PLAN2) {
// 10-20秒飞机创建
if (this._currCreateEnemyTime > this.createEnemtTime * 0.9) {//0.9飞机组合间隔
const randomCombination = math.randomRangeInt(1, 3)//随机1,2飞机
if (randomCombination === Constant.Combination.PLAN2) {
this.createCombination1()
} else {
this.createEnemyPlane()
}
this._currCreateEnemyTime = 0
}
} else {
//20+ 飞机创建
if (this._currCreateEnemyTime > this.createEnemtTime * 0.8) {//0.8飞机组合间隔
const randomCombination = math.randomRangeInt(1, 4)//随机1,2,3飞机
if (randomCombination === Constant.Combination.PLAN2) {
this.createCombination1()
} else if (randomCombination === Constant.Combination.PLAN3) {
this.createCombination2()
} else {
this.createEnemyPlane()
}
this._currCreateEnemyTime = 0
}
}
}
/**
* 返回主界面逻辑,数据重置
*/
public returnMain() {
this._currShootTime = 0;
this._currCreateEnemyTime = 0;
this._combinationInterval = Constant.Combination.PLAN1;
this._bulletType = Constant.BulletPropType.BULLET_M;
this.playerPlane.node.setPosition(0, 0, 15);
this._score = 0;
}
/**
* 游戏开始页面数据重置
*/
public gameStart() {
this.isGameStart = true;
this._changePlanMode();//开启定时器
this._score = 0;
this.gameScore.string = this._score.toString();
}
/**
* 游戏再次开始
*/
public gameReStart() {
this.isGameStart = true;
this._currShootTime = 0;
this._currCreateEnemyTime = 0;
this._combinationInterval = Constant.Combination.PLAN1;
this._bulletType = Constant.BulletPropType.BULLET_M;
this.playerPlane.node.setPosition(0, 0, 15);
this._score = 0;
this._changePlanMode();//开启定时器
this.gameScore.string = this._score.toString();
}
/**
* 游戏结束
*/
public gameOver() {
this.isGameStart = false;
this.gamePage.active = false;
this.gameOverPage.active = true;
this.gameOverScore.string = this._score.toString();
this._isShooting = false;
this.unschedule(this._modeChanged);//取消定时器
this.playerPlane.init();//这里注意游戏结束要初始化
this._destroyAll();
}
/**
* 加分
*/
public addScore() {
this._score++;
this.gameScore.string = this._score.toString();
}
/**
* 创建子弹
*/
public createPlayerBulletM() {
// 子弹实例化
const bullet = instantiate(this.bullet01);
// 将子弹放在子弹管理节点下面
bullet.setParent(this.bulletRoot);
// 获取飞机位置
const pos = this.playerPlane.node.position;
// 设置子弹位置
bullet.setPosition(pos.x, pos.y, pos.z - 7);
// 设置子弹速度
const bulletComp = bullet.getComponent(Bullet);
bulletComp.show(this.bulletSpeed, false)
}
/**
* H形状子弹创建
*/
public createPlayerBulletH() {
const pos = this.playerPlane.node.position;
// left
const bullet1 = instantiate(this.bullet03);
bullet1.setParent(this.bulletRoot);
bullet1.setPosition(pos.x - 2.5, pos.y, pos.z - 7);
const bulletComp1 = bullet1.getComponent(Bullet);
bulletComp1.show(this.bulletSpeed, false);
// right
const bullet2 = instantiate(this.bullet03);
bullet2.setParent(this.bulletRoot);
bullet2.setPosition(pos.x + 2.5, pos.y, pos.z - 7);
const bulletComp2 = bullet2.getComponent(Bullet);
bulletComp2.show(this.bulletSpeed, false);
}
/**
* S型子弹创建
*/
public createPlayerBulletS() {
const pos = this.playerPlane.node.position;
// middle
const bullet = instantiate(this.bullet05);
bullet.setParent(this.bulletRoot);
bullet.setPosition(pos.x, pos.y, pos.z - 7);
const bulletComp = bullet.getComponent(Bullet);
bulletComp.show(this.bulletSpeed, false);
// left
const bullet1 = instantiate(this.bullet05);
bullet1.setParent(this.bulletRoot);
bullet1.setPosition(pos.x - 4, pos.y, pos.z - 7);
const bulletComp1 = bullet1.getComponent(Bullet);
bulletComp1.show(this.bulletSpeed, false, Constant.Direction.LEFT);
// right
const bullet2 = instantiate(this.bullet05);
bullet2.setParent(this.bulletRoot);
bullet2.setPosition(pos.x + 4, pos.y, pos.z - 7);
const bulletComp2 = bullet2.getComponent(Bullet);
bulletComp2.show(this.bulletSpeed, false, Constant.Direction.RIGHT);
}
/**
* 创建敌机子弹
* @param targetPos 敌机子弹位置
*/
public createEnemyBullet(targetPos: Vec3) {
// 子弹实例化
const bullet = instantiate(this.bullet01);
// 将子弹放在子弹管理节点下面
bullet.setParent(this.bulletRoot);
// 设置子弹位置
bullet.setPosition(targetPos.x, targetPos.y, targetPos.z + 6);
// 设置子弹速度
const bulletComp = bullet.getComponent(Bullet);
bulletComp.show(1, true)
/**
* 敌机子弹分组
*/
const colliderComp = bullet.getComponent(BoxCollider);
colliderComp.setGroup(Constant.CollisionType.ENEMY_BULLET);
colliderComp.setMask(Constant.CollisionType.SELF_PLANE);//设置掩码
}
/**
* 创建敌机
*
*/
public createEnemyPlane() {
// 两架飞机随机选一(1,2)
const whichEnemy = math.randomRangeInt(1, 3)
let prefab: Prefab = null
let speed = 0
// 创建敌机1或2
if (whichEnemy == Constant.EnemyType.TYPE1) {
prefab = this.enemy01
speed = this.enemy1Speed
} else {
prefab = this.enemy02
speed = this.enemy2Speed
}
// 预制实例化
const enemy = instantiate(prefab)
console.log(enemy);
enemy.setParent(this.node)
const enemyComp = enemy.getComponent(EnemyPlane)
enemyComp.show(this, speed, true)//单架敌机需要发射子弹
// 设置飞机位置
const randomPos = math.randomRangeInt(-25, 26)
enemy.setPosition(randomPos, 0, -50)
}
/**
* 组合1创建 横向排列 z轴50,x轴从-20开始
*
*/
public createCombination1() {
const enemyArray = new Array<Node>(5)//飞机数组
for (let i = 0; i < enemyArray.length; i++) {
// 敌机资源实例化
enemyArray[i] = instantiate(this.enemy01)
const element = enemyArray[i]
element.parent = this.node
element.setPosition(-20 + i * 10, 0, -50)//-20起始左位置,10飞机间隔,其实创建位置
//设置飞机速度
const enemyComp = element.getComponent(EnemyPlane)
enemyComp.show(this, this.enemy1Speed, false)//组合飞机不需要发射子弹
}
}
/**
* 组合2创建 V字排列
*
*/
public createCombination2() {
const enemyArray = new Array<Node>(7)//飞机数组
// 位置数组
const combinationPos = [
-21, 0, -60,
-14, 0, -55,
-7, 0, -50,
0, 0, -45,
7, 0, -50,
14, 0, -55,
21, 0, -60
]
for (let i = 0; i < enemyArray.length; i++) {
// 敌机资源实例化
enemyArray[i] = instantiate(this.enemy02)
const element = enemyArray[i]
element.parent = this.node
const startIndex = i * 3//因为位置数组有7个 但是位置有3×7 21个 所以每架飞机位置偏移是3
element.setPosition(combinationPos[startIndex], combinationPos[startIndex + 1], combinationPos[startIndex + 2])
//设置飞机速度
const enemyComp = element.getComponent(EnemyPlane)
enemyComp.show(this, this.enemy2Speed, false)//组合飞机不需要发射子弹
}
}
/**
* 创建子弹道具
* 随机1~3是根据然后创建相对应的道具
*/
public createBulletProp() {
const randomProp = math.randomRangeInt(1, 4);
let prefab: Prefab = null;
if (randomProp === Constant.BulletPropType.BULLET_H) {
prefab = this.bulletPropH;
} else if (randomProp === Constant.BulletPropType.BULLET_S) {
prefab = this.bulletPropS;
} else {
prefab = this.bulletPropM;
}
//实例化道具
const prop = instantiate(prefab);
prop.setParent(this.node);
prop.setPosition(15, 0, -50);
const propComp = prop.getComponent(BulletProp);
propComp.show(this, -this.bulletPropSpeed);
}
/**
* 触摸状态设置
* @param value true/false
*/
public isShooting(value: boolean) {
this._isShooting = value;
}
/**
* 改变子弹类型
* @param type 类型
*/
public changeBulletType(type: number) {
this._bulletType = type;
}
/**
* 播放音频
* @param name 音频名字
*/
public playAudioEffect(name: string) {
this.audioEffect.play(name);
}
/**
* 默认发射子弹
*/
private _init() {
this._currShootTime = this.shootTime;
this.playerPlane.init();
// this._changePlanMode();
}
/**
* 设计定时器
*/
private _changePlanMode() {
/**
* 每10秒改变一次状态,,根据状态就能决定采用什么组合
*/
this.schedule(this._modeChanged, 10, macro.REPEAT_FOREVER);//组件自带定时器(回调函数,间隔时间,重复次数,延迟时间)
}
/**
* 改变组合状态
*/
private _modeChanged() {
this._combinationInterval++
this.createBulletProp();//创建子弹道具
}
/**
* 销毁所有的飞机与子弹
*/
private _destroyAll() {
let children = this.node.children;
let length = children.length;
let i = 0;
for (i = length - 1; i >= 0; i--) {
const child = children[i];
child.destroy();
}
children = this.bulletRoot.children;
length = children.length;
for (i = length - 1; i >= 0; i--) {
const child = children[i];
child.destroy();
}
}
}
- UIMain
import { _decorator, Component, Node, systemEvent, SystemEvent, Touch, EventTouch, Vec2 } from 'cc';
import { GameManager } from '../framework/GameManager';
const { ccclass, property } = _decorator;
@ccclass('UIMain')
export class UIMain extends Component {
@property
public planeSpeed = 1;//飞机移动速度
@property(Node)
public playerPlane: Node = null;
//gameManager的引用
@property(GameManager)
public gameManager: GameManager = null;
@property(Node)
public gameStart: Node = null;//游戏开始2D UI
@property(Node)
public game: Node = null;//游戏中2D UI
@property(Node)
public gameOver: Node = null;//游戏结束2D UI
start() {
this.node.on(SystemEvent.EventType.TOUCH_START, this._touchStart, this);
this.node.on(SystemEvent.EventType.TOUCH_MOVE, this._touchMove, this);
this.node.on(SystemEvent.EventType.TOUCH_END, this._touchEnd, this);
//初始化游戏开始页面展示
this.gameStart.active = true;
}
// update (deltaTime: number) {
// // [4]
// }
/**
* 再来一局的按钮点击事件
*/
public reStart() {
this.gameOver.active = false;
this.game.active = false;
//点击按钮播放
this.gameManager.playAudioEffect('button');
this.gameManager.gameReStart();
}
/**
* 回到主页的按钮点击事件
*/
public returnMain() {
this.gameOver.active = false;
this.gameStart.active = true;
//点击按钮播放
this.gameManager.playAudioEffect('button');
this.gameManager.returnMain();
}
/**
* 触摸开始 子弹发射
* @param touch
* @param event
*/
_touchStart(touch: Touch, event: EventTouch) {
//判断第一次触摸屏幕的时候游戏是否开始
if (this.gameManager.isGameStart) {
this.gameManager.isShooting(true);
} else {
//游戏开始切换游戏状态
this.gameStart.active = false;
this.game.active = true;
//点击按钮播放
this.gameManager.playAudioEffect('button');
this.gameManager.gameStart();
}
}
/**
* 触摸中
* @param touch
* @param event
*/
_touchMove(touch: Touch, event: EventTouch) {
//判断游戏状态
if (!this.gameManager.isGameStart) {
return;
}
//获取当前触点值与上一次触点值之间的差值
const delta = touch.getDelta();
//获取当前位置
let pos = this.playerPlane.position;
//移动位置z与x移动 y轴不变
/**
* 乘0.01是因为每移动一段距离移动的都是单位,但是在操作屏幕触点的时候操作的其实是像素,两者之间不能转换,所以只能预估一下大概的移动距离 */
this.playerPlane.setPosition(pos.x + 0.01 * this.planeSpeed * delta.x, pos.y, pos.z - 0.01 * this.planeSpeed * delta.y);
}
/**
* 触摸结束 子弹取消发射
* @param touch
* @param event
*/
_touchEnd(touch: Touch, event: EventTouch) {
//判断游戏状态
if (!this.gameManager.isGameStart) {
return;
}
this.gameManager.isShooting(false);
}
}
- EnemyPlane
import { _decorator, Component, Node, Collider, ITriggerEvent } from 'cc';
import { Constant } from '../framework/Constant';
import { GameManager } from '../framework/GameManager';
const { ccclass, property } = _decorator;
// 敌机销毁位置
const OUTOFBOUNCE = 50
@ccclass('EnemyPlane')
export class EnemyPlane extends Component {
/** 子弹发射周期 */
@property
public createBulletTime = 0.5
// 敌机速度
private _enemySpeed = 0;
private _needBullet = false//当前是否发射子弹
private _gameManager: GameManager = null
private _currCreateBulletTime = 0
// // 敌机类型
// public enemyType = Constant.EnemyType.TYPE1
start() {
// [3]
}
//监听事件
onEnable() {
const collider = this.getComponent(Collider);//获取碰撞组件
collider.on('onTriggerEnter', this._onTriggerEnter, this);//碰撞到了立马处理逻辑
}
//取消事件
onDisable() {
const collider = this.getComponent(Collider);//获取碰撞组件
collider.off('onTriggerEnter', this._onTriggerEnter, this);
}
update(deltaTime: number) {
const pos = this.node.position
const movePos = pos.z + this._enemySpeed
// 因为敌机向西飞所以为正向
this.node.setPosition(pos.x, pos.y, movePos)
if (this._needBullet) {//发射子弹逻辑
this._currCreateBulletTime += deltaTime
if (this._currCreateBulletTime > this.createBulletTime) {
this._gameManager.createEnemyBullet(this.node.position)
this._currCreateBulletTime = 0
}
}
if (movePos > OUTOFBOUNCE) {//超出边界销毁
this.node.destroy()
}
}
/**
* 设置飞机移动速度
* @param speed 移动速度
*/
show(gameManager: GameManager, speed: number, needBullet: boolean) {
this._gameManager = gameManager
this._enemySpeed = speed
this._needBullet = needBullet
}
private _onTriggerEnter(event: ITriggerEvent) {
const collisionGroup = event.otherCollider.getGroup();//获取分组
if (collisionGroup === Constant.CollisionType.SELF_PLANE || collisionGroup === Constant.CollisionType.SELF_BULLET) {//玩家飞机或者玩家子弹判断掉血
// console.log('trigger enemy destroy');
//敌方飞机销毁的时候播放音频
this._gameManager.playAudioEffect('enemy');
this.node.destroy();
this._gameManager.addScore();//获得分数
}
}
}
- SelfPlane
import { _decorator, Component, Node, Collider, ITriggerEvent, AudioSource } from 'cc';
import { Constant } from '../framework/Constant';
const { ccclass, property } = _decorator;
@ccclass('SelfPlane')
export class SelfPlane extends Component {
public lifeValue = 5;//玩家飞机血量上限
public isDie = false;//是否死亡
private _currLife = 0;//当前飞机血量
private _audioEffect: AudioSource = null;
start(){
//获取音频组件
this._audioEffect = this.getComponent(AudioSource);
}
//监听事件
onEnable() {
const collider = this.getComponent(Collider);//获取碰撞组件
collider.on('onTriggerEnter', this._onTriggerEnter, this);//碰撞到了立马处理逻辑
}
//取消事件
onDisable() {
const collider = this.getComponent(Collider);//获取碰撞组件
collider.off('onTriggerEnter', this._onTriggerEnter, this);
}
public init() {
this._currLife = this.lifeValue;//重置飞机血量
this.isDie = false;
}
// update (deltaTime: number) {
// // [4]
// }
private _onTriggerEnter(event: ITriggerEvent) {
const collisionGroup = event.otherCollider.getGroup();//获取分组
if (collisionGroup === Constant.CollisionType.ENEMY_PLANE || collisionGroup === Constant.CollisionType.ENEMY_BULLET) {//敌方飞机或者敌方子弹判断掉血
this._currLife--;//掉血
if (this._currLife <= 0) {//判断当前血量是不是小于0
this.isDie = true;
this._audioEffect.play();
console.log('self plane is die');
}
}
}
}