开发工具

  1. Cocos Dashboard 1.0.20
  2. Cocos Creator 3.4.0
  3. Visual Studio Code 1.63
  4. Microsoft Edge 97.0.1072.69

添加碰撞分组

  1. 主菜单 点击 项目 -> 项目设置, 打开 项目设置 窗口。
  1. 物理 选项最下方的 碰撞矩阵+ 添加四个分组:PLAYER_PLANE, ENEMY_PLANE, PLAYER_BULLET, ENEMY_BULLET

  2. 设置分组的掩码:

1
2
3
PLAYER_PLANE - ENEMY_BULLET
PLAYER_PLANE - ENEMY_PLANE
ENEMY_PLANE - PLAYER_BULLET
  1. 设置完毕,关闭对话框。

设置碰撞组件和分组

  1. 层级管理器 点击选中 playerPlane 节点, 在其 属性检查器 中点 添加组件 -> Physics -> BoxCollider
  1. playerPlane 节点 属性检查器 中的 cc.BoxCollider+ 属性 Size 的值改为大一些的整数,例如:Size, X:11, Y:1, Z:10; 或是直接在 场景编辑器 中拉伸飞机 淡绿色边框碰撞包围盒 包围玩家飞机, 刚添加 BoxCollider 的时候 碰撞包围盒 很小, 并且可能和 playerPlane 节点的坐标轴箭头重叠在一起,可以先修改 Size 的值后将其变大再进行拉伸; 将 cc.BoxCollider+isTrigger(触发器) 打勾。。
  1. 继续在 playerPlane 节点的 属性检查器 中点 添加组件 -> Physics -> RigidBody, 选择分组 Group 的值为 PLAYER_PLANE, Type 属性的值改为 KINEMATIC (运动学刚体)。
  1. 点击 playerPlane 节点的 属性检查器 右上角的 应用 按钮更新修改到对应的 预制 中。
  1. 分别双击打开 资源管理器 中的敌方飞机预制 plane02plane03, 重做与玩家飞机相似的设置, RigidBody 分组 Group 的值选择为 ENEMY_PLANE, 保存预制 。
  1. 资源管理器 中双击打开 子弹1 - 子弹5 的预制 bullet01 - bullet05, 重做与玩家飞机相似的设置, RigidBody 分组 Group 的值选择为 PLAYER_BULLET (先设置为玩家子弹) 。

注册玩家飞机触发事件回调

  1. 修改 Constant, 添加 碰撞类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export class Constant {

// 敌机类型
public static EnemyType = {
TYPE1: 1,
TYPE2: 2,
}

// 敌机组合类型
public static Combination = {
PLAN1: 1, // 组合1, 每次随机1架敌机, 发射子弹
PLAN2: 2, // 组合2, 每次5架敌机,一 字型, 不发射子弹
PLAN3: 3, // 组合3, 每次7架敌机,V 字型, 不发射子弹
}

// 碰撞组合类型, 要和碰撞矩阵的顺序一致
public static CollistionType = {
// 使用左移符号设置为二进制值, 右边数字与碰撞矩阵序号一致
PLAYER_PLANE: 1 << 1,
ENEMY_PLANE: 1 << 2,
PLAYER_BULLET: 1 << 3,
ENEMY_BULLET: 1 << 4,
}
}
  1. 资源管理器 点击选中 plane 文件夹, 添加名为 PlayerPlane 的脚本, 添加用于检测 玩家飞机 是否碰到 敌人子弹敌机
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { _decorator, Component, Node, Collider, ITriggerEvent } from 'cc';
import { Constant } from '../framework/Constant';
const { ccclass, property } = _decorator;

@ccclass('PlayerPlane')
export class PlayerPlane extends Component {
onEnable() {
// 获取碰撞组件
const collider = this.getComponent(Collider);
// 监听触发事件
collider.on('onTriggerEnter', this._onTriggerEnter, this);
}

onDisable() {
const collider = this.getComponent(Collider);
// 停止监听触发事件
collider.off('onTriggerEnter', this._onTriggerEnter, this);
}

private _onTriggerEnter(event: ITriggerEvent) {
// 获取分组
const collisionGroup = event.otherCollider.getGroup();
// 遇到敌方飞机或敌方子弹,玩家飞机掉血
if (collisionGroup === Constant.CollistionType.ENEMY_PLANE
|| collisionGroup === Constant.CollistionType.ENEMY_BULLET) {
// 现在玩家飞机发生碰撞只在控制台输出文件
console.log('player plane reduce blood');
}
}
}

  1. 层级管理器 点击选中 playerPlane 节点, 在其 属性检查器 中点 添加组件 -> 自定义脚本 -> PlayerPlane, 将玩家飞机脚本绑定到 playerPlane 根节点, 保存场景。
  1. 修改 敌人飞机 脚本 EnemyPlane, 添加 onEnable()onDisable()回调函数 检测 敌人飞机 是否碰到 玩家子弹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

import { _decorator, Component, Node, Collider, ITriggerEvent } from 'cc';
import { Constant } from '../framework/Constant';
import { GameManager } from '../framework/GameManager';
const { ccclass, property } = _decorator;

// 敌机被销毁的位置(超出屏幕下边沿后的Z轴坐标值)
const OUTOFBOUNCE = 50;

@ccclass('EnemyPlane')
export class EnemyPlane extends Component {

@property
public createBulletTime = 0.5; // 敌机子弹发射周期

private _enemySpeed = 0;
private _needBullet = false; // 敌机是否发射子弹
private _currCreateBulletTime = 0; // 敌机当前子弹发射周期
private _gameManager: GameManager = null;

onEnable() {
// 获取碰撞组件
const collider = this.getComponent(Collider);
// 监听触发事件
collider.on('onTriggerEnter', this._onTriggerEnter, this);
}

onDisable() {
// 获取碰撞组件
const collider = this.getComponent(Collider);
// 停止监听触发事件
collider.off('onTriggerEnter', this._onTriggerEnter, this);
}

private _onTriggerEnter(event: ITriggerEvent) {
// 获取分组
const collisionGroup = event.otherCollider.getGroup();
// 遇到玩家飞机或子弹,敌人飞机销毁
if (collisionGroup === Constant.CollistionType.PLAYER_PLANE
|| collisionGroup === Constant.CollistionType.PLAYER_BULLET) {
console.log('trigger enemy destroy');
this.node.destroy();
this._gameManager.addScore(); // 分数增加
console.log('reduce blood');
}
}

update(deltaTime: number) {
const pos = this.node.position;
// 敌机向下飞 pos.z 值要增加
let movePos = pos.z + this._enemySpeed;

// 增加用于判断是否要发射子弹
if (this._needBullet) {
this._currCreateBulletTime += deltaTime;
if (this._currCreateBulletTime > this.createBulletTime) {
// 敌机发射子弹,需要在 GameManager 添加相应函数
// this.node.position 是发射子弹时敌机的位置
this._gameManager.createEnemyBullet(this.node.position);
this._currCreateBulletTime = 0;
}
}

this.node.setPosition(pos.x, pos.y, movePos);
if (movePos > OUTOFBOUNCE) {
this.node.destroy();
}
}

show(gameManager: GameManager, speed: number, needBullet: boolean = false) {
this._gameManager = gameManager;
this._enemySpeed = speed;
this._needBullet = needBullet;
}
}

  1. 修改子弹脚本 Bullet, 添加 onEnable()onDisable()回调函数 检测 子弹 是否与 飞机 发生 碰撞
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

import { _decorator, Component, Node, Collider, ITriggerEvent } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('Bullet')
export class Bullet extends Component {

// @property
// public bulletSpeed = 0;

private _bulletSpeed = 0; //子弹速度改为私有变量
private _isEnemyBullet = false; // 判断是否为敌机子弹

update(deltaTime: number) {
let pos = this.node.position; // 获取子弹的位置
let moveLength = pos.z - this._bulletSpeed; // 计算每一帧子弹要移动的位置
// 计算每一帧子弹要移动的位置, 玩家和敌机子弹方向不同
if (this._isEnemyBullet) {
moveLength = pos.z + this._bulletSpeed;
this.node.setPosition(pos.x, pos.y, moveLength);
if (moveLength > 50) {
this.node.destroy();
console.log(`enemy bullet destory`);
}
} else {
moveLength = pos.z - this._bulletSpeed;
this.node.setPosition(pos.x, pos.y, moveLength);
if (moveLength < -50) {
this.node.destroy();
console.log(`player bullet destory`);
}
}
}

// 从 GameManager 脚本接收子弹速度和是否为敌机子弹
show(speed: number, isEnemyBullet: boolean = false) {
this._bulletSpeed = speed;
this._isEnemyBullet = isEnemyBullet;
}

onEnable() {
// 获取碰撞组件
const collider = this.getComponent(Collider);
// 监听触发事件
collider.on('onTriggerEnter', this._onTriggerEnter, this);
}

onDisable() {
// 获取碰撞组件
const collider = this.getComponent(Collider);
// 停止监听触发事件
collider.off('onTriggerEnter', this._onTriggerEnter, this);
}

private _onTriggerEnter(event: ITriggerEvent) {
console.log('trigger bullet destroy');
// 子弹碰到任意飞机都销毁
this.node.destroy();
}
}

  1. 修改游戏控制脚本 GameManagercreateEnemyBullet 函数, 给敌机发射的子弹设置 碰撞矩阵分组碰撞掩码, 这两项在之前的 设置窗口没有设置; 添加 计分函数(暂时为空函数) 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

import { _decorator, Component, Node, Prefab, instantiate, math, Vec3, BoxCollider } from 'cc';
import { Bullet } from '../bullet/Bullet';
import { EnemyPlane } from '../plane/EnemyPlane';
import { Constant } from './Constant';
const { ccclass, property } = _decorator;


@ccclass('GameManager')
export class GameManager extends Component {
@property(Node)
public playerPlane: Node = null; // 玩家飞机

@property(Prefab)
public bullet01: Prefab = null; // 子弹1
@property(Prefab)
public bullet02: Prefab = null; // 子弹2
@property(Prefab)
public bullet03: Prefab = null; // 子弹3
@property(Prefab)
public bullet04: Prefab = null; // 子弹4
@property(Prefab)
public bullet05: Prefab = null; // 子弹5

@property
public shootTime = 0.3; // 射击周期(间隔时间)
@property
public bulletSpeed = 1; // 子弹速度

@property(Node)
public bulletRoot: Node = null; // 子弹管理节点

// 敌机
@property(Prefab)
public enemy01: Prefab = null; // 敌机1
@property(Prefab)
public enemy02: Prefab = null; // 敌机2
@property
public createEnemyTime = 1; // 敌机生成时间
@property
public enemy01Speed = 0.5; // 敌机1速度
@property
public enemy02Speed = 0.7; // 敌机2速度

private _currShootTime = 0;
private _isShooting = false;
private _currCreateEnemyTiime = 0; // 当前敌机的生成时间
private _combinationInterval = Constant.Combination.PLAN1; // 组合的间隔状态

start() {
this._init();
}

update(deltaTime: number) {
this._currShootTime += deltaTime;
if (this._isShooting && this._currShootTime > this.shootTime) {
this.createPlayerBullet();
this._currShootTime = 0;
}
this._currCreateEnemyTiime += deltaTime;

// 判断组合方式创建相应的敌机
if (this._combinationInterval === Constant.Combination.PLAN1) {
if (this._currCreateEnemyTiime > this.createEnemyTime) {
this.createEnemyPlane();
this._currCreateEnemyTiime = 0;
}
} else if (this._combinationInterval === Constant.Combination.PLAN2) {
// 第二阶段,前两种组合随机出现
// 这里乘以 0.9 是缩短敌机出现的间隔时间
if (this._currCreateEnemyTiime > this.createEnemyTime * 0.9) {
// 用于随机出现组合1或组合2
const randomCombination = math.randomRangeInt(1, 6);
if (randomCombination === Constant.Combination.PLAN2) {
this.createCombination01();
} else {
this.createEnemyPlane();
}
this._currCreateEnemyTiime = 0;
}
} else {
// 第三阶段,三个组合随机出现
// 这里乘以 0.9 是缩短敌机出现的间隔时间
if (this._currCreateEnemyTiime > this.createEnemyTime * 0.8) {
// 用于随机出现组合1或组合2
const randomCombination = math.randomRangeInt(1, 7);
if (randomCombination === Constant.Combination.PLAN2) {
this.createCombination01();
} else if (randomCombination === Constant.Combination.PLAN3) {
this.createCombination02();
} else {
this.createEnemyPlane();
}
this._currCreateEnemyTiime = 0;
}
}
}

//创建玩家飞机的子弹
public createPlayerBullet() {
// 实例材质类型的子弹, 实例出来的对象不在场景中
const bullet = instantiate(this.bullet01);
bullet.setParent(this.bulletRoot); // 子弹挂载到子弹管理节点中

let pos = this.playerPlane.position; // 获取玩家飞机位置
// 子弹出现的位置为飞机 Z轴 -7
bullet.setPosition(pos.x, pos.y, pos.z - 7);
// 获取 Bullet类 (子弹预制根节点添加 Bullet 脚本)
const bulletComp = bullet.getComponent(Bullet);
bulletComp.show(this.bulletSpeed, false); // 设置子弹速度
}

// 是否处理触摸状态,触摸才发射子弹
public isShooting(value: boolean) {
this._isShooting = value;
}

public createEnemyPlane() {
// math 是 cc 带的数字模块
const whichEnemy = math.randomRangeInt(1, 3);
let prefab: Prefab = null;
let speed = 0;
if (whichEnemy === Constant.EnemyType.TYPE1) {
prefab = this.enemy01;
speed = this.enemy01Speed;
} else {
prefab = this.enemy02;
speed = this.enemy02Speed;
}

// 实例化预制
const enemy = instantiate(prefab);
enemy.setParent(this.node); // 预制挂载到组件中
const enemyComp = enemy.getComponent(EnemyPlane);
enemyComp.show(this, speed, true);

// 敌机随机出现的X轴(左右)坐标范围
const randomPos = math.randomRangeInt(-25, 26);
enemy.setPosition(randomPos, 0, -50);
}

// 敌机出现组合1:5架飞机一字型同时出现
public createCombination01() {
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);
const enemyComp = element.getComponent(EnemyPlane);
enemyComp.show(this, this.enemy01Speed, false);
}
}

// 敌机出现组合1, 7架飞机V字型出现
public createCombination02() {
const enemyArray = new Array<Node>(7);
// 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;
element.setPosition(combinationPos[startIndex],
combinationPos[startIndex + 1],
combinationPos[startIndex + 2]);
const enemyComp = element.getComponent(EnemyPlane);
enemyComp.show(this, this.enemy02Speed, false);
}
}

// 敌机发射子弹
public createEnemyBullet(targetPos: Vec3) {
// 实例材质类型的子弹, 实例出来的对象不在场景中
const bullet = instantiate(this.bullet01);
bullet.setParent(this.bulletRoot); // 子弹挂载到子弹管理节点中

// 子弹出现的位置为飞机 Z轴 -7
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.CollistionType.ENEMY_BULLET);
// 设置碰撞掩码 与 玩家飞机 碰撞
colliderComp.setMask(Constant.CollistionType.PLAYER_PLANE);

}

// 计分
public addScore() {

}


private _init() {
// 用于按下的时候即发射第一颗子弹
this._currShootTime = this.shootTime;
this._changePlaneModel();
}

// 定时器函数
private _changePlaneModel() {
// this.schedule()四个参数:回调, 间隔时间,重复次数, 延迟时间
this.schedule(this._modeChanged, 10, 3);
}

// 改变组合状态
private _modeChanged() {
this._combinationInterval++;
}
}

  1. 保存场景, 预览。 敌人飞机子弹 碰到 玩家飞机玩家子弹 会消失, 玩家飞机 目前还处于 无敌 状态。

===END===