跳至正文
版本:最新版 - a1.169.2 ⚖️

基本 HUD(画布)

如何使用渲染器的画布功能添加一个基本 HUD,以显示角色的生命值和弹药量。

提示

你也可以使用 HTML + JavaScript + CSS 创建完整且复杂的 UI,快来看看吧:基本 HUD(HTML)。

画布(Canvas)可以用来绘制图形。以下代码展示了如何使用画布添加基础 UI:

Client/Index.lua
-- 生成画布
HUDCanvas = Canvas(true, Color.TRANSPARENT, 0, true)

-- 订阅 Update 事件,我们只能在此事件内部进行绘制
HUDCanvas:Subscribe("Update", function(self, width, height)
-- 获取本地玩家
local local_player = Client.GetLocalPlayer()
if (not local_player) then return end

-- 获取本地角色(由本地玩家所控制)
local local_character = local_player:GetControlledCharacter()
if (not local_character) then return end

-- 绘制生命值
self:DrawText(tostring(local_character:GetHealth()), Vector2D(100, height - 100), FontType.PoiretOne, 25, Color.WHITE)

-- 获取本地武器
local character_weapon = local_character:GetPicked()
if (character_weapon and character_weapon:IsA(Weapon)) then
-- 绘制弹夹弹药
self:DrawText(tostring(character_weapon:GetAmmoClip()), Vector2D(width - 200, height - 100), FontType.PoiretOne, 25, Color.WHITE)

-- 绘制备用弹药
self:DrawText("/ " .. tostring(character_weapon:GetAmmoBag()), Vector2D(width - 140, height - 87), FontType.PoiretOne, 15, Color.WHITE)
end
end)

上面的示例在每个刻都会重新渲染并更新所有生命值/弹药信息,这可能会影响性能。幸运的是,我们可以通过在构造函数中修改自动重绘速率来限制它,从而快速改善性能:

-- 不在 auto_repaint_rate 参数中使用 0
HUDCanvas = Canvas(true, Color.TRANSPARENT, 0, true)

-- 改为使用 0.033,这样它仅每 0.033 秒更新一次(即大约每秒 30 次)
HUDCanvas = Canvas(true, Color.TRANSPARENT, 0.033, true)

另一种进一步的优化是仅在需要时进行更新,对于这种情况,我们需要实现一些额外的代码。在这种情况下,我们将 auto_repaint_rate 设置为 -1 以禁用自动重绘:

-- 我们将其设置为 -1 以永不自动重绘
HUDCanvas = Canvas(true, Color.TRANSPARENT, -1, true)

-- 订阅 Update 事件,我们只能在此事件内部进行绘制(按常规方式)
HUDCanvas:Subscribe("Update", function(self, width, height)
-- 获取本地玩家
local local_player = Client.GetLocalPlayer()
if (not local_player) then return end

-- 获取本地角色(由本地玩家所控制)
local local_character = local_player:GetControlledCharacter()
if (not local_character) then return end

-- 绘制生命值
self:DrawText(tostring(local_character:GetHealth()), Vector2D(100, height - 100), FontType.PoiretOne, 25, Color.WHITE)

-- 获取本地武器
local character_weapon = local_character:GetPicked()
if (character_weapon and character_weapon:IsA(Weapon)) then
-- 绘制弹夹弹药
self:DrawText(tostring(character_weapon:GetAmmoClip()), Vector2D(width - 200, height - 100), FontType.PoiretOne, 25, Color.WHITE)

-- 绘制备用弹药
self:DrawText("/ " .. tostring(character_weapon:GetAmmoBag()), Vector2D(width - 140, height - 87), FontType.PoiretOne, 15, Color.WHITE)
end
end)


-- 现在我们订阅希望其更新的时机:

-- 为本地角色设置所有必要事件的函数(在生命值或武器发生变化时更新 UI)
function UpdateLocalCharacter(character)
-- 验证角色是否不为 nil(例如当 GetControlledCharacter() 未返回角色时)
if (character == nil) then return end

-- 每当生命值发生变化(受到伤害、受到治疗、重生或脚本控制)时更新生命值 UI
character:Subscribe("HealthChange", function(charac, old_health, new_health)
HUDCanvas:Repaint()
end)

-- 拾取武器时更新弹药 UI
character:Subscribe("PickUp", function(charac, object)
if (object:IsA(Weapon)) then
HUDCanvas:Repaint()
end
end)

-- 丢弃武器时移除弹药 UI
character:Subscribe("Drop", function(charac, object)
HUDCanvas:Repaint()
end)

-- 开火时更新弹药 UI
character:Subscribe("Fire", function(charac, weapon)
HUDCanvas:Repaint()
end)

-- 重新装填时更新弹药 UI
character:Subscribe("Reload", function(charac, weapon, ammo_to_reload)
HUDCanvas:Repaint()
end)

-- 立即更新 UI
HUDCanvas:Repaint()
end

-- 当我们停止控制旧角色时退订其事件,以免事件叠加累积
function ClearLocalCharacter(character)
if (character == nil or not character:IsValid()) then return end

character:Unsubscribe("HealthChange")
character:Unsubscribe("PickUp")
character:Unsubscribe("Drop")
character:Unsubscribe("Fire")
character:Unsubscribe("Reload")
end

-- 当 LocalPlayer 生成时,订阅以了解其何时控制新的 角色。此事件仅调用一次,重新加载包时的机制请参考 Package.Subscribe("Load")
Client.Subscribe("SpawnLocalPlayer", function(local_player)
local_player:Subscribe("Possess", function(player, character)
UpdateLocalCharacter(character)
end)

local_player:Subscribe("UnPossess", function(player, character)
ClearLocalCharacter(character)
HUDCanvas:Repaint()
end)
end)

-- 当包加载时,验证 LocalPlayer 是否已经存在(例如在重新加载包时),然后获取其控制的 角色
Package.Subscribe("Load", function()
local local_player = Client.GetLocalPlayer()
if (local_player ~= nil) then
UpdateLocalCharacter(local_player:GetControlledCharacter())
end
end)

-- 当视口(屏幕)调整大小时更新 UI 位置
Viewport.Subscribe("Resize", function(new_size)
HUDCanvas:Repaint()
end)
备注

不传入回调函数的 Unsubscribe 将会移除当前包在实体上订阅的该事件的所有回调。如果你的包还在其他地方订阅了这些事件,请保留 Subscribe 返回的回调函数,并改为将其传递给 Unsubscribe。