Lua实现Unity象素鸟小游戏摘要

Lua实现Unity象素鸟小游戏要点记录:

游戏玩法简介

不用物理引擎实现像素鸟在障碍物之间飞行,通过配表配置小鸟向下的加速度和点击一次屏幕给小鸟赋值一次初始速度和向上加速度来模拟小鸟飞行。障碍物根据配置难度在固定距离随机生成。

功能实现简述

飞行障碍实现

创建AmusementParkBirdObstacleCell障碍控件。游戏运行时,根据固定的配置距离,生成不同难度的障碍实例。障碍物分为上,下两部份,中间有可穿行的空隙。

AmusementParkBirdObstacleCell

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
---@class AmusementParkBirdObstacleCell
local m = {
View = nil,
---@type number 移动速度
Speed = 0,
---@type UnityEngine.Collider2D
TopCollider2D = nil,
---@type UnityEngine.Collider2D
BottomCollider2D = nil,
}

function m.New(view)
local obj = Clone(m)
obj:Init(view)
return obj
end

function m:Init(view)
self.View = view
LuaCodeInterface.BindOutlet(self.View, self)

self.TopCollider2D = self.View.transform:Find("Top").gameObject:GetComponent(typeof(UnityEngine.Collider2D))
self.BottomCollider2D = self.View.transform:Find("Bottom").gameObject:GetComponent(typeof(UnityEngine.Collider2D))

self:AddListener()
end

function m:AddListener()

end

function m:RemoveListener()

end

---界面轮询。
---@param elapseSeconds number
---@param realElapseSeconds number
---@return void
function m:OnUpdate(elapseSeconds, realElapseSeconds)
self.View.transform:Translate(Vector3.New(self.Speed * UnityEngine.Time.deltaTime * -1, 0, 0))
end

---@param speed number
function m:SetData(speed)
self.Speed = speed
end

function m:Dispose()
AssetUtil.UnloadAsset(self)
self:RemoveListener()

LuaCodeInterface.ClearOutlet(self.View, self)
UnityEngine.Object.Destroy(self.View)
self.View = nil
end

return m

控制小鸟上下移动速度

创建小鸟角色对象AmusementParkBirdPlayer,其移动只在Y轴方向移动。如果不点击屏幕给予向上的初始速度和加速度,小鸟会向下加速度掉落,当碰触到地面和障碍时,小鸟死亡,游戏结束。

AmusementParkBirdPlayer

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
---@class AmusementParkBirdPlayer
local m = {
View = nil,
---@type UnityEngine.Rigidbody2D
Rigidbody2D = nil,
---@type UnityEngine.Collider2D
BirdCollider2D = nil,
---@type Vector3 玩家初始位置
StartPos = Vector3.New(0,0,0),
---@type number Y轴方向速度
Speed = 0,
---@type number 加速度
Acceleration = 0,
}

function m.New(view)
local obj = Clone(m)
obj:Init(view)
return obj
end

function m:Init(view)
self.View = view
LuaCodeInterface.BindOutlet(self.View, self)

self.BirdCollider2D = self.View:GetComponent(typeof(UnityEngine.Collider2D))
self.Rigidbody2D = self.View:GetComponent("Rigidbody2D")
self.Rigidbody2D.simulated = false
self.StartPos = self.View.transform.localPosition

self.Acceleration = -AmusementParkBirdGameModule.apBirdGameData:GetCfgData().VerticalDownAcceleration
self:AddListener()
end

function m:AddListener()

end

function m:RemoveListener()

end

---界面轮询。
---@param elapseSeconds number
---@param realElapseSeconds number
---@return void
function m:OnUpdate(elapseSeconds, realElapseSeconds)
self.Speed = self.Speed + self.Acceleration
local pos = self.View.transform.localPosition
pos.y = pos.y + self.Speed * UnityEngine.Time.deltaTime
self.View.transform.localPosition = pos
end

---设置向上初速度和加速度
---@param acceleration number 加速度
---@param speed number 初始速度
function m:SetData(acceleration,speed)
self.Acceleration = acceleration
if speed then
self.Speed = speed
end
end

---重置玩家状态
function m:ResetPlayer()
self.Rigidbody2D.simulated = false
self.View.transform.localPosition = self.StartPos
self.View.transform.localRotation = Vector3.New(0,0,0)
self.Speed = 0
self.Acceleration = -AmusementParkBirdGameModule.apBirdGameData:GetCfgData().VerticalDownAcceleration
end

---玩家死亡
function m:PlayerDead()
self.Rigidbody2D.simulated = true
log("游戏结束 小鸟死亡")
end

---检测小鸟与障碍物碰撞
---@param collider2D UnityEngine.Collider2D 目标对象collider2D
---@return boolean
function m:CheckBirdBoundsIntersects(collider2D)
return self.BirdCollider2D.bounds:Intersects(collider2D.bounds)
end

function m:Dispose()
AssetUtil.UnloadAsset(self)
self:RemoveListener()

LuaCodeInterface.ClearOutlet(self.View, self)
UnityEngine.Object.Destroy(self.View)
self.View = nil
end

return m

障碍碰撞检测

小鸟在飞行过程中,通过collider2D碰撞检测,是否与障碍物相撞 self.BirdCollider2D.bounds:Intersects(collider2D.bounds)

实例

---@class AmusementParkGameFlappyBirdUI
local AmusementParkGameFlappyBirdUI = {
    ---@type number
    PassTime = 0,
    ---@type AmusementParkBirdBG
    ParkBirdBG = nil,
    ---@type AmusementParkBirdObstacleCell[]
    BirdObstacleCells = {},
    ---@type AmusementParkBirdObstacleCell 上一个被创建的障碍物
    LastBirdObstacleCell = nil,
    ---@type Vector3 障碍物出生位置(固定在屏幕右侧)
    BirthLocation = Vector3.New(670,0,0),
    ---@type UnityEngine.GameObject[] 障碍物预设集合
    ObstaclePrefabs = {},
    ---@type AmusementParkBirdPlayer
    BirdPlayer = nil,
    ---@type UnityEngine.Collider2D 上边界
    UpCollider2D = nil,
    ---@type UnityEngine.Collider2D 下边界
    DownCollider2D = nil,
}
AmusementParkGameFlappyBirdUI = Base:Extend("AmusementParkGameFlappyBirdUI", "IQIGame.Onigao.UI.AmusementParkGameFlappyBirdUI", AmusementParkGameFlappyBirdUI)

require("IQIGame.UIExternalApi.AmusementParkGameFlappyBirdUIApi")
local AmusementParkBirdBG = require("IQIGame.UI.ExploreHall.AmusementPark.AmusementParkBirdGame.AmusementParkBirdBG")
local AmusementParkBirdObstacleCell = require("IQIGame.UI.ExploreHall.AmusementPark.AmusementParkBirdGame.AmusementParkBirdObstacleCell")
local AmusementParkBirdPlayer = require("IQIGame.UI.ExploreHall.AmusementPark.AmusementParkBirdGame.AmusementParkBirdPlayer")

---界面初始化。
---@return void
function AmusementParkGameFlappyBirdUI:OnInit()
    self.DelegateOnClickBtnClose = function() self:OnClickBtnClose() end
    self.DelegateOnClickBtnStartGame = function() self:OnClickBtnStartGame() end
    self.DelegateOnClickBtnRank = function() self:OnClickBtnRank() end
    self.DelegateOnClickBtnRest = function() self:OnClickBtnRest() end
    self.DelegateUpdateBirdGameRank = function() self:RefreshRank() end
    self.DelegateOnClick = function() self:OnClick() end

    self.UpCollider2D = self.Top:GetComponent(typeof(UnityEngine.Collider2D))
    self.DownCollider2D = self.Bottom:GetComponent(typeof(UnityEngine.Collider2D))

    self.ParkBirdBG = AmusementParkBirdBG.New(self.BGNode)
    self.BirdPlayer = AmusementParkBirdPlayer.New(self.Player)

    self.ObstaclePrefabs = {}
    for i = 1, self.BalkPrefabs.transform.childCount do
        local objTrs = self.BalkPrefabs.transform:Find(i)
        if objTrs then
            self.ObstaclePrefabs[i] = objTrs.gameObject
        end
    end
end

---OnInit预加载资源路径列表。
---@return table
function AmusementParkGameFlappyBirdUI:GetPreloadAssetPaths()
    return nil
end

---OnOpen预加载资源路径列表。
---@return table
function AmusementParkGameFlappyBirdUI:GetOpenPreloadAssetPaths(userData)
    return nil
end

---OnOpen时调用,判断当前UI是否在卡顿结束后手动显示
---@return boolean
function AmusementParkGameFlappyBirdUI:IsManualShowOnOpen(userData)
    return false
end

---UI是否有独立的背景音乐,如果有则返回,否则返回nil,注意一个UI同时只能有一个背景音乐,如果需要切换,调用self.UIController:ChangeBGM()方法
---@return number @SoundCid
function AmusementParkGameFlappyBirdUI:GetBGM(userData)
    return nil
end

---界面打开。
---@param userData table
---@return void
function AmusementParkGameFlappyBirdUI:OnOpen(userData)
    AmusementParkBirdGameModule.GetBirdGameRank()
    self:ResetGame()
end

---界面关闭。
---@param userData table
---@return void
function AmusementParkGameFlappyBirdUI:OnClose(userData)

end

---添加侦听。
---@return void
function AmusementParkGameFlappyBirdUI:OnAddListeners()
    self.BtnClose:GetComponent("Button").onClick:AddListener(self.DelegateOnClickBtnClose)
    self.BtnStartGame:GetComponent("Button").onClick:AddListener(self.DelegateOnClickBtnStartGame)
    self.BtnRank:GetComponent("Button").onClick:AddListener(self.DelegateOnClickBtnRank)
    self.BtnRest:GetComponent("Button").onClick:AddListener(self.DelegateOnClickBtnRest)
    self.BtnJump:GetComponent("Button").onClick:AddListener(self.DelegateOnClick)

    EventDispatcher.AddEventListener(EventID.APUpdateBirdGameRankEvent,self.DelegateUpdateBirdGameRank)
end

---移除侦听。
---@return void
function AmusementParkGameFlappyBirdUI:OnRemoveListeners()
    self.BtnClose:GetComponent("Button").onClick:RemoveListener(self.DelegateOnClickBtnClose)
    self.BtnStartGame:GetComponent("Button").onClick:RemoveListener(self.DelegateOnClickBtnStartGame)
    self.BtnRank:GetComponent("Button").onClick:RemoveListener(self.DelegateOnClickBtnRank)
    self.BtnRest:GetComponent("Button").onClick:RemoveListener(self.DelegateOnClickBtnRest)
    self.BtnJump:GetComponent("Button").onClick:RemoveListener(self.DelegateOnClick)

    EventDispatcher.RemoveEventListener(EventID.APUpdateBirdGameRankEvent,self.DelegateUpdateBirdGameRank)
end

---界面暂停。
---@return void
function AmusementParkGameFlappyBirdUI:OnPause()

end

---界面暂停恢复。
---@return void
function AmusementParkGameFlappyBirdUI:OnResume()

end

---界面遮挡。
---@return void
function AmusementParkGameFlappyBirdUI:OnCover()

end

---界面遮挡恢复。
---@return void
function AmusementParkGameFlappyBirdUI:OnReveal()

end

---界面激活。
---@param userData table
---@return void
function AmusementParkGameFlappyBirdUI:OnRefocus(userData)

end

---界面轮询。
---@param elapseSeconds number
---@param realElapseSeconds number
---@return void
function AmusementParkGameFlappyBirdUI:OnUpdate(elapseSeconds, realElapseSeconds)
    if AmusementParkBirdGameModule.apBirdGameData.startGame then
        self.ParkBirdBG:OnUpdate(elapseSeconds, realElapseSeconds)
        self.BirdPlayer:OnUpdate(elapseSeconds, realElapseSeconds)
        if PlayerModule.GetServerTime() - self.PassTime > 1 then
            self.PassTime = PlayerModule.GetServerTime()
            self:RefreshScore()
            AmusementParkBirdGameModule.PixelBird()
        end
        ---检障碍物创建
        if self.LastBirdObstacleCell then
            local direction = self.BirthLocation.x - self.LastBirdObstacleCell.View.transform.localPosition.x
            if direction >= AmusementParkBirdGameModule.apBirdGameData:GetCfgData().BalkCoolDown then
                self.LastBirdObstacleCell = nil
                self:CreateObstacle()
            end
        end

        for i, v in pairs(self.BirdObstacleCells) do
            v:OnUpdate(elapseSeconds, realElapseSeconds)
            if v.View.transform.localPosition.x < -self.BirthLocation.x then
                v:Dispose()
                self.BirdObstacleCells[i] = nil
            end
        end

        ---碰撞检测
        local result = self:CheckCollision()
        if result then
            self:GameOver()
        end
    end
end

---界面深度改变。
---@param uiGroupDepth number
---@param depthInUIGroup number
---@return void
function AmusementParkGameFlappyBirdUI:OnDepthChanged(uiGroupDepth, depthInUIGroup)

end

---加载资源成功
---该方法委托类型为底层框架类型
---@param assetName string
---@param asset table
---@param duration number
---@param userData table
---@return void
function AmusementParkGameFlappyBirdUI:OnLoadSucceed(assetName, asset, duration, userData)

end

---加载资源失败
---该方法委托类型为底层框架类型
---@param assetName string
---@param status number
---@param errorMessage string
---@param userData table
---@return void
function AmusementParkGameFlappyBirdUI:OnLoadFailed(assetName, status, errorMessage, userData)

end

---销毁
function AmusementParkGameFlappyBirdUI:OnDestroy()
    AssetUtil.UnloadAsset(self)
    self.ParkBirdBG:Dispose()
    self.BirdPlayer:Dispose()
    self:CleanObstacles()
end

function AmusementParkGameFlappyBirdUI:OnClickBtnClose()
    UIModule.CloseSelf(self)
end

---创建障碍物
function AmusementParkGameFlappyBirdUI:CreateObstacle()
    local index = math.random(1,#AmusementParkBirdGameModule.apBirdGameData:GetCfgData().BalkGroup)
    local id = AmusementParkBirdGameModule.apBirdGameData:GetCfgData().BalkGroup[index]
    local obj = UnityEngine.Object.Instantiate(self.ObstaclePrefabs[id])
    obj:SetActive(true)
    local speed = AmusementParkBirdGameModule.apBirdGameData:GetCfgData().HorizontalSpeed
    local cell = AmusementParkBirdObstacleCell.New(obj)
    cell.View.transform:SetParent(self.ObstacleNode.transform,false)
    cell.View.transform.localPosition = self.BirthLocation
    cell:SetData(speed)
    table.insert(self.BirdObstacleCells,cell)
    self.LastBirdObstacleCell = cell
end

function AmusementParkGameFlappyBirdUI:UpdateView()
    self.ResultView:SetActive(false)
    ---@type AmusementParkBirdGameData
    local apBirdGameData = AmusementParkBirdGameModule.apBirdGameData
    self.ParkBirdBG:SetSpeed(apBirdGameData:GetCfgData().HorizontalSpeed)
    UGUIUtil.SetText(self.TextCurrentLV,AmusementParkGameFlappyBirdUIApi:GetString("TextCurrentLV",apBirdGameData:GetCfgData().Level))
    self:RefreshScore()
    self:RefreshRank()
end

---清空障碍物
function AmusementParkGameFlappyBirdUI:CleanObstacles()
    for i, v in pairs(self.BirdObstacleCells) do
        v:Dispose()
    end
    self.BirdObstacleCells = {}
end

function AmusementParkGameFlappyBirdUI:RefreshScore()
    UGUIUtil.SetText(self.TextGameScore,AmusementParkGameFlappyBirdUIApi:GetString("TextGameScore",AmusementParkBirdGameModule.apBirdGameData:GetGameScore()))
    UGUIUtil.SetText(self.TextMaxLv,AmusementParkGameFlappyBirdUIApi:GetString("TextCurrentLV",AmusementParkBirdGameModule.maxLv))
    UGUIUtil.SetText(self.TextMaxScore,AmusementParkGameFlappyBirdUIApi:GetString("TextGameScore",AmusementParkBirdGameModule.maxScore))
end

function AmusementParkGameFlappyBirdUI:RefreshRank()
    local showMyRank = AmusementParkBirdGameModule.myRanking
    self.TextRankTag:SetActive(false)
    if showMyRank > 999 then
        showMyRank = 999
        self.TextRankTag:SetActive(true)
    end
    UGUIUtil.SetText(self.TextRank,AmusementParkGameFlappyBirdUIApi:GetString("TextRank",showMyRank))
end

function AmusementParkGameFlappyBirdUI:OnClick()
    local speed = AmusementParkBirdGameModule.apBirdGameData:GetCfgData().VerticalUpSpeed
    local acceleration = AmusementParkBirdGameModule.apBirdGameData:GetCfgData().VerticalUpAcceleration
    self.BirdPlayer:SetData(acceleration,speed)
end

---碰撞检测
---@return boolean
function AmusementParkGameFlappyBirdUI:CheckCollision()
    local top = false
    ---边界检测
    top = self.BirdPlayer:CheckBirdBoundsIntersects(self.UpCollider2D)
    if top then
        return true
    end
    top = self.BirdPlayer:CheckBirdBoundsIntersects(self.DownCollider2D)
    if top then
        return true
    end
    ---障碍物检测
    for i, v in pairs(self.BirdObstacleCells) do
        top = self.BirdPlayer:CheckBirdBoundsIntersects(v.TopCollider2D)
        if not top then
            top = self.BirdPlayer:CheckBirdBoundsIntersects(v.BottomCollider2D)
        end
        if top then
            return true
        end
    end
    return top
end

---游戏结束
function AmusementParkGameFlappyBirdUI:GameOver()
    AmusementParkBirdGameModule.PixelBird()
    AmusementParkBirdGameModule.apBirdGameData:EndGame()
    self.BirdPlayer:PlayerDead()
    self.ResultView:SetActive(true)
end

---重置游戏
function AmusementParkGameFlappyBirdUI:ResetGame()
    AmusementParkBirdGameModule.CreateNewGame()
    self.ParkBirdBG:ResetPos()
    self.BirdPlayer:ResetPlayer()
    self.BtnStartGame:SetActive(true)
    self:CleanObstacles()
    self:UpdateView()
end

---开始游戏
function AmusementParkGameFlappyBirdUI:OnClickBtnStartGame()
    self.BtnStartGame:SetActive(false)
    self.ParkBirdBG:SetStartMove(true)
    AmusementParkBirdGameModule.apBirdGameData:StartGame()
    self:CreateObstacle()
end

---重置游戏
function AmusementParkGameFlappyBirdUI:OnClickBtnRest()
    self:ResetGame()
end

---查看排行榜
function AmusementParkGameFlappyBirdUI:OnClickBtnRank()
    UIModule.Open(Constant.UIControllerName.AmusementParkGameFlappyBirdRankUI,Constant.UILayer.UI)
end

return AmusementParkGameFlappyBirdUI