Lua实现U3D弹幕飞行游戏摘要

用Lua实现Unity弹幕飞行游戏要点记录:

- 对象池

创建游戏中可循环利用的游戏对象池

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
---获取一个可用子弹
---@return FlightGameBullet
function m:GetBullet()
for i, v in pairs(self.flightGameBulletPool) do
if not v.active then
return v
end
end
---如果没有可用子弹,动态创建一个新的子弹
local bulletCell = FlightGameBullet.New(UnityEngine.Object.Instantiate(self.BulletModel))
self.flightGameBulletPool[bulletCell.View:GetInstanceID()] = bulletCell
return bulletCell
end

---回收子弹
---@param bullet FlightGameBullet
function m:RecycleBullet(bullet)
bullet.View.transform:SetParent(self.RecoveryNode.transform, false)
bullet:Reset()
self.flightGameBulletPool[bullet.View:GetInstanceID()] = bullet
end

---获取一个未被激活的敌人
---@return FlightGameEnemy
function m:GetEnemy()
for i, v in pairs(self.flightGameEnemyPool) do
if not v.active and v:IsPlayDamageEnd() then
return v
end
end
---如果没有未被激活的敌人,动态创建一个新的敌人
local enemy = FlightGameEnemy.New(UnityEngine.Object.Instantiate(self.EnemyModel),self.rootScene)
self.flightGameEnemyPool[enemy.View:GetInstanceID()] = enemy
return enemy
end

---回收敌人设置成未激活状态
---@param enemy FlightGameEnemy
function m:RecycleEnemy(enemy)
enemy.View.transform:SetParent(self.RecoveryNode.transform, false)
enemy:Reset()
self.flightGameEnemyPool[enemy.View:GetInstanceID()] = enemy
end
--endregion Object Management

- 碰撞检测

实现场景中游戏对象的碰撞检测

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
--region Collision Detection
---检测敌人碰撞检测
---@param enemy FlightGameEnemy
function m:CheckCollisionEnemy(enemy)
if enemy.active and enemy.born and enemy.blood > 0 and enemy.collider2D then
---和玩家发出的子弹碰撞
for i, v in pairs(self.flightGameBulletPool) do
if v.active then
if v.bulletData.launcher == FlightGameConstant.ObjectType.FlightGame_Player then
local res = v.collider2D.bounds:Intersects(enemy.collider2D.bounds)
if res then
local hurt = v.bulletData.hurt
local addHurt = v.bulletData:AddHurtByMonsterType(enemy.enemyData:GetConfigData().Race)
hurt = math.floor(hurt * (1 + addHurt / 100))
enemy:ReduceBlood(hurt,v.bulletData.bulletPathID)
self:RecycleBullet(v)
end
end
end
end

---和玩家碰撞
if enemy.active and enemy.born and enemy.blood > 0 then
local result = self.rootScene.gamePlayer:CheckPlayerBoundsIntersects(enemy.collider2D)
if result then
local hurt = enemy.enemyData:GetChallengeEnemyAttr(FlightGameConstant.Attribute.Flight_Collision_Damage)
self.rootScene.gamePlayer:ReduceBlood(hurt)
enemy:ReduceBlood(hurt)
end
end
end
end

---玩家碰撞检测
function m:CheckPlayerCollision()
---和敌人子弹碰撞
for i, v in pairs(self.flightGameBulletPool) do
if v.active then
if v.bulletData.launcher == FlightGameConstant.ObjectType.FlightGame_Enemy then
local res = self.rootScene.gamePlayer:CheckPlayerBoundsIntersects(v.collider2D)
if res then
self.rootScene.gamePlayer:ReduceBlood(v.bulletData.hurt)
self:RecycleBullet(v)
end
end
end
end
---奖励箱拾取
for i, v in pairs(self.flightGameRewardBoxCellPool) do
if v.active and not v.picked then
local res = self.rootScene.gamePlayer:CheckPlayerBoundsIntersects(v.collider2D)
if res then
local rewardBoxData = FlightGameRewardBoxData.New(v.rewardBoxData.specialCid,v.rewardBoxData.createTime)
rewardBoxData:SetStartEffectTime(PlayerModule.GetServerTime())
self.rootScene.gamePlayer:AddRewardBoxEffect(rewardBoxData)
v:Pickup()
end
end
end
end

---检测对象是否超出场景边界
---@return boolean
function m:CheckBeyondBoundaries(sceneElement)
if sceneElement.View.transform.position.x < self.rootScene.LimitLeft.transform.position.x or
sceneElement.View.transform.position.x > self.rootScene.LimitRight.transform.position.x or
sceneElement.View.transform.position.y > self.rootScene.LimitUp.transform.position.y or
sceneElement.View.transform.position.y < self.rootScene.LimitDown.transform.position.y then
return true
end
return false
end
--endregion Collision Detection

- 子弹弹道实现

- 平行直线型

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
---不同攻击类型 双弹道直线
---@param num number 直线弹道数量
---@param bulletSpeed number 子弹移速
---@param cid number 弹道ID
function m:AttackDoubleLine(num,bulletSpeed,cid)
local offset = 0
local spacing = 0.6
if num % 2 == 0 then
offset = spacing / 2
end
for i = 0, num -1 do
local pos = self.FirePos.transform.position
local bulletCell = self.rootScene.gameManager:GetBullet()
bulletCell.View.transform:SetParent(self.rootScene.RootNode.transform, false)
if i == 0 then
pos = Vector3.New(pos.x,pos.y + spacing * i - offset, pos.z)
else
if i%2 == 1 then
pos = Vector3.New(pos.x,pos.y + spacing * math.ceil(i/2) - offset, pos.z)
else
pos = Vector3.New(pos.x,pos.y - spacing * math.ceil(i/2) - offset, pos.z)
end
end
local bulletData = FlightGameBulletData.New(self.playerData.cid,self.hurt,FlightGameConstant.ObjectType.FlightGame_Player,Vector2.New(bulletSpeed,0),cid)
bulletCell:SetData(pos,bulletData,function (bullet) self.rootScene.gameManager:RecycleBullet(bullet) end)
end
end

- 扇形功击

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
---扇形功击
---@param num number 子弹数量
---@param angle number 间隔角度
---@param bulletSpeed number 子弹移速
---@param cid number 弹道ID
function m:AttackRange(num,angle,bulletSpeed,cid)
local offset = 0
if num % 2 == 0 then
offset = angle / 2
end
for i = 0, num - 1 do
local pos = self.FirePos.transform.position
local bulletCell = self.rootScene.gameManager:GetBullet()
bulletCell.View.transform:SetParent(self.rootScene.RootNode.transform, false)
if i == 0 then
bulletCell.View.transform:Rotate(0,0,angle * i - offset)
else
if i%2 == 1 then
bulletCell.View.transform:Rotate(0,0,angle * math.ceil(i/2) - offset)
else
bulletCell.View.transform:Rotate(0,0,-angle * math.ceil(i/2) - offset)
end
end
local bulletData = FlightGameBulletData.New(self.playerData.cid,self.hurt,FlightGameConstant.ObjectType.FlightGame_Player,Vector2.New(bulletSpeed,0),cid)
bulletCell:SetData(Vector3.New(pos.x,pos.y,pos.z),bulletData,
function (bullet) self.rootScene.gameManager:RecycleBullet(bullet) end)
end
end