This commit is contained in:
Ggafrik 2025-07-18 01:11:49 +08:00
parent 2d612189d5
commit b319dfeb27
25 changed files with 736 additions and 72 deletions

View File

@ -21,6 +21,9 @@
},
"ClientMain": {
"$path": "src/StarterPlayerScripts/ClientMain"
},
"UI": {
"$path": "src/StarterPlayerScripts/UI"
}
}
},

Binary file not shown.

View File

View File

@ -0,0 +1,7 @@
local UIEnums = {}
UIEnums.UIParent = {
UIRoot = "UIRoot",
}
return UIEnums

View File

@ -0,0 +1,124 @@
local UIList = {}
UIList.__index = UIList
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Dependencies
local Utils = require(ReplicatedStorage.Tools.Utils)
-- 子脚本自动注入
local function AutoInjectVariables(self)
for varName, _ in pairs(self.Variables) do
if typeof(varName) == "string" then
local firstChar = string.sub(varName, 1, 1)
local sixChar = string.sub(varName, 1, 6)
local target
if firstChar == "_" then
if sixChar == "__list" then
local prefab = Utils:FindInDescendantsUI(self.UIRoot, varName)
target = UIList:Init(prefab)
else
-- _开头递归查找
target = Utils:FindInDescendantsUI(self.UIRoot, varName)
end
if not target then
error("自动注入失败未找到UI节点 " .. varName)
end
self.Variables[varName] = target
end
end
end
end
function UIList:Init(Prefab: Instance)
local self = {}
setmetatable(self, UIList)
self.Data = {}
self.Instances = {}
self.Connections = {}
self.Component = nil
self.UIRoot = Prefab
self.Org = Utils:FindInDescendantsUI(Prefab, "__org")
self.Org.Visible = false
return self
end
function UIList:AddComponent(Component: table)
self.Component = Component
end
function UIList:SetData(Data: table)
self.Data = Data
self:Refresh()
end
function UIList:AddData(data: table)
self.Data[#self.Data + 1] = data
self:SetSingleInstance(#self.Data, data)
end
function UIList:SetSingleInstance(index: number, data: table)
local child = self.Component:Init(data)
local uiInstance = self.Org:Clone()
child.UIRoot = uiInstance
uiInstance.Name = index
uiInstance.Parent = self.Org.Parent
self.Instances[index] = child
AutoInjectVariables(child)
child:Refresh()
uiInstance.Visible = true
end
function UIList:Refresh()
for _, ui in pairs(self.Instances) do ui:Destroy() end
self.Instances = {}
if not self.Component then warn("UIList:Refresh() Component未设置") return end
if self.Data then
for k, v in pairs(self.Data) do
self:SetSingleInstance(k, v)
end
end
end
function UIList:Clean()
-- 清除自己的内容
for _, connection in pairs(self.Connections) do
connection:Disconnect()
end
self.Connections = {}
-- 清除下面组件的内容
for _, ui in pairs(self.Instances) do
for _, connection in pairs(ui.SignalConnections) do
connection:DisconnectAll()
end
self.SignalConnections = nil
for _, connection in pairs(ui.Connections) do
connection:Disconnect()
end
self.Connections = nil
ui:Destroy()
end
self.Instances = {}
end
function UIList:Destroy()
self:Clean()
self.SignalConnections = nil
self.Connections = nil
self.Org:Destroy()
self = nil
end
return UIList

View File

@ -4,43 +4,31 @@ UIWindow.__index = UIWindow
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Dependencies
local UIList = require(ReplicatedStorage.Base.UIList)
--> Variables
local FolderWindows = ReplicatedStorage:WaitForChild("UI"):WaitForChild("Windows")
local Utils = require(ReplicatedStorage.Tools.Utils)
local LocalPlayer = game.Players.LocalPlayer
local FolderPlayerGui = LocalPlayer:WaitForChild("PlayerGui")
--------------------------------------------------------------------------------
-- 递归查找
local function FindInDescendants(root, name)
if root:FindFirstChild(name) then
return root:FindFirstChild(name)
end
for _, child in ipairs(root:GetChildren()) do
local found = FindInDescendants(child, name)
if found then
return found
end
end
return nil
end
-- 非递归查找
local function FindInRoot(root, name)
return root:FindFirstChild(name)
end
--------------------------------------------------------------------------------
function UIWindow:Init(Data: table?)
function UIWindow:Init(UIManager: table, Data: table?)
local self = {}
self.UIManager = UIManager
self.Data = Data
self.Variables = {}
self.Connections = {}
self.SignalConnections = {}
self.UIRootName = nil
self.UIParentName = nil
self.CloseDestroy = true
-- 运行时操作变量
self.UIRoot = nil
self.AutoInject = false
@ -52,36 +40,29 @@ function UIWindow:SetData(Data: table?)
self:OnDataChanged()
end
function UIWindow:OnOpenWindow()
if not self.UIRoot then
-- 创建UI根节点
self.UIRoot = FolderWindows:FindFirstChild(self.UIRootName):Clone()
self.UIRoot.Parent = FolderPlayerGui:FindFirstChild(self.UIParentName)
end
-- 自动注入
-- 自动注入
function UIWindow:AutoInjectVariables()
if not self.AutoInject then
for varName, _ in pairs(self.Variables) do
if typeof(varName) == "string" then
local firstChar = string.sub(varName, 1, 1)
local secondChar = string.sub(varName, 2, 2)
local sixChar = string.sub(varName, 1, 6)
local target
if firstChar == "_" then
if secondChar == "_" then
-- __开头只查根目录
target = FindInRoot(self.UIRoot, varName)
if sixChar == "__list" then
local prefab = Utils:FindInDescendantsUI(self.UIRoot, varName)
target = UIList:Init(prefab)
else
-- _开头递归查找
target = FindInDescendants(self.UIRoot, varName)
target = Utils:FindInDescendantsUI(self.UIRoot, varName)
end
if not target then
error("自动注入失败未找到UI节点 " .. varName)
end
self[varName] = target
self.Variables[varName] = target
end
end
end
@ -89,8 +70,28 @@ function UIWindow:OnOpenWindow()
end
end
function UIWindow:OnCloseWindow()
function UIWindow:OnOpenWindow()
if not self.UIRoot then
-- 创建UI根节点
self.UIRoot = FolderWindows:FindFirstChild(self.UIRootName):Clone()
self.UIRoot.Parent = FolderPlayerGui:WaitForChild(self.UIParentName)
print("节点", self.UIRoot, self.UIParentName, FolderPlayerGui, FolderPlayerGui:FindFirstChild(self.UIParentName))
else
self.UIRoot.Visible = true
self:Refresh()
end
self:AutoInjectVariables()
end
function UIWindow:OnCloseWindow()
if self.CloseDestroy then
self.UIRoot:Destroy()
self.UIRoot = nil
else
self.UIRoot.Visible = false
end
end
--> 覆盖用
@ -98,4 +99,21 @@ function UIWindow:OnDataChanged() end
function UIWindow:Refresh() end
function UIWindow:Destroy()
for _, connection in pairs(self.SignalConnections) do
connection:DisconnectAll()
end
self.SignalConnections = nil
for _, connection in pairs(self.Connections) do
connection:Disconnect()
end
self.Connections = nil
for k, v in pairs(self) do
self[k] = nil
end
self = nil
end
return UIWindow

View File

@ -0,0 +1,7 @@
local SignalEnum = {}
SignalEnum = {
SHOW_ABILITY = "SHOW_ABILITY",
}
return SignalEnum

View File

@ -1,3 +1,3 @@
[
{"id":20000,"type":1,"behaviourName":"swordWave","upgradeCost":[30000,5,10],"upgradeValue":[10,200],"recycle":[30000,100]}
{"id":20000,"type":1,"icon":1,"behaviourName":"SwordWave","upgradeCost":[30000,5,10],"upgradeValue":[10,200],"recycle":[30000,100]}
]

View File

@ -7,6 +7,7 @@
Difference here compared to a BindableEvent is that it's way faster without the need of creating tons of instances using metatable magic!
]]
local freeRunnerThread = nil
local function acquireRunnerThreadAndCallEventHandler(fn, ...)
@ -72,6 +73,7 @@ local Signals = {}
local Signal = {}
Signal.__index = Signal
Signal.ClassName = "Signal"
Signal.ENUM = SignalEnum
export type Signal = typeof(Signal)

View File

@ -1,7 +1,4 @@
-- 客户端调用的内容
local RunService = game:GetService("RunService")
if RunService:IsClient() then return end
-- 本地化读取工具
local Localization = {}
--> Services
@ -19,10 +16,7 @@ local JsonImage_Zh_CN = require(ReplicatedStorage.Json.Image_Zh_CN)
--> Variables
local LocalPlayer = game.Players.LocalPlayer
local SystemLocaleId = LocalizationService:GetSystemLocaleId()
local JsonLanguage, JsonImage = Localization:GetLocalizationJson()
local SystemLocaleId = LocalizationService.SystemLocaleId
-- 获取本地Json文件
function Localization:GetLocalizationJson()
@ -33,6 +27,8 @@ function Localization:GetLocalizationJson()
end
end
local JsonLanguage, JsonImage = Localization:GetLocalizationJson()
-- 获取文本Id数据
function Localization:GetLanguageData(Id: number)
if not Id then return end

View File

@ -0,0 +1,151 @@
--[[
GoodSignal Class
stravant @ July 2021
Edited slightly by Evercyan to add globals & 'Once' method
Used for creating custom Signals - basically the signals like RBXScriptSignal is (workspace.ChildAdded, Players.PlayerAdded are both signals, because you can connect to them!)
Difference here compared to a BindableEvent is that it's way faster without the need of creating tons of instances using metatable magic!
]]
local SignalEnum = require(game:GetService("ReplicatedStorage").Data.SignalEnum)
local freeRunnerThread = nil
local function acquireRunnerThreadAndCallEventHandler(fn, ...)
local acquiredRunnerThread = freeRunnerThread
freeRunnerThread = nil
fn(...)
freeRunnerThread = acquiredRunnerThread
end
local function runEventHandlerInFreeThread(...)
acquireRunnerThreadAndCallEventHandler(...)
while true do
acquireRunnerThreadAndCallEventHandler(coroutine.yield())
end
end
---- CONNECTION CLASS ----------------------------------------------------------
local Connection = {}
Connection.__index = Connection
function Connection.new(signal, fn)
return setmetatable({
_connected = true,
_signal = signal,
_fn = fn,
_next = false,
}, Connection)
end
function Connection:Disconnect()
if not self._connected then
return
end
self._connected = false
if self._signal._handlerListHead == self then
self._signal._handlerListHead = self._next
else
local prev = self._signal._handlerListHead
while prev and prev._next ~= self do
prev = prev._next
end
if prev then
prev._next = self._next
end
end
end
setmetatable(Connection, {
__index = function(tb, key)
error(("Attempt to get Connection::%s (not a valid member)"):format(tostring(key)), 2)
end,
__newindex = function(tb, key, value)
error(("Attempt to set Connection::%s (not a valid member)"):format(tostring(key)), 2)
end
})
---- SIGNAL CLASS --------------------------------------------------------------
local Signals = {}
local Signal = {}
Signal.__index = Signal
Signal.ClassName = "Signal"
Signal.ENUM = SignalEnum
export type Signal = typeof(Signal)
function Signal.new(Name: string?): Signal
if Name and Signals[Name] then
return Signals[Name]
else
local signal = setmetatable({
_handlerListHead = false
}, Signal)
if Name then
signal.Name = Name
Signals[Name] = signal
end
return signal
end
end
function Signal:Connect(fn)
local connection = Connection.new(self, fn)
if self._handlerListHead then
connection._next = self._handlerListHead
self._handlerListHead = connection
else
self._handlerListHead = connection
end
return connection
end
function Signal:Once(fn)
local cn; cn = self:Connect(function(...)
cn:Disconnect()
fn(...)
end)
return cn
end
function Signal:DisconnectAll()
if rawget(self, "Name") then
Signals[self.Name] = nil
end
self._handlerListHead = false
end
Signal.Destroy = Signal.DisconnectAll
function Signal:Fire(...)
local item = self._handlerListHead
while item do
if item._connected then
if not freeRunnerThread then
freeRunnerThread = coroutine.create(runEventHandlerInFreeThread)
end
task.spawn(freeRunnerThread, item._fn, ...)
end
item = item._next
end
end
function Signal:Wait()
local waitingCoroutine = coroutine.running()
local cn;
cn = self:Connect(function(...)
cn:Disconnect()
task.spawn(waitingCoroutine, ...)
end)
return coroutine.yield()
end
return Signal

View File

@ -70,6 +70,16 @@ function Utils:GetIdDataFromJson(JsonData: table, id: number)
return nil -- 没有找到对应id
end
function Utils:GetSpecialKeyDataFromJson(JsonData: table, Key: string, Value: string)
-- 遍历JsonData查找id字段等于目标id的项
for _, item in ipairs(JsonData) do
if item[Key] == Value then
return item
end
end
return nil -- 没有找到对应id
end
-- 获取随机idExceptIdList为可选参数如果传入则排除ExceptIdList中的id
function Utils:GetRandomIdFromJson(JsonData: table, ExceptIdList: table?)
local rng = Random.new()
@ -218,6 +228,42 @@ function Utils:TableSafeAddTableValue(AttributesData: table, AddTableValue: tabl
end
end
-- 递归查找
function Utils:FindInDescendants(root, name)
if root:FindFirstChild(name) then
return root:FindFirstChild(name)
end
for _, child in ipairs(root:GetChildren()) do
local found = Utils:FindInDescendants(child, name)
if found then
return found
end
end
return nil
end
-- 非递归查找
function Utils:FindInRoot(root, name)
return root:FindFirstChild(name)
end
-- UI递归查找
function Utils:FindInDescendantsUI(root, name)
if root:FindFirstChild(name) then
return root:FindFirstChild(name)
end
for _, child in ipairs(root:GetChildren()) do
local firstTwo = string.sub(child.Name, 1, 2)
if firstTwo ~= "__" then
local found = Utils:FindInDescendantsUI(child, name)
if found then
return found
end
end
end
return nil
end
--------------------------------------------------------------------------------

View File

@ -52,6 +52,23 @@ function Behaviour:Execute()
end
-- 检查行为前先清理之前遗留的引用数据
function Behaviour:CheckClean()
if self.Mobs then
for _, Mob in self.Mobs do
self.Mobs[Mob] = nil
end
self.Mobs = nil
end
if self.CheckData then
for key, cha in self.CheckData do
self.CheckData[key] = nil
end
self.CheckData = nil
end
end
-- 启动冷却时间清除计时
function Behaviour:StartCooldownTask()
self.Cooldown = self.OrgCooldown
@ -115,6 +132,9 @@ function Behaviour:Destroy()
self.PlayerAI:RemoveBehaviourUniqueId(UniqueId)
end
self.UniqueIdList = {}
for k, v in pairs(self) do
self[k] = nil
end
self = nil
end

View File

@ -93,10 +93,11 @@ function Character:ChangeAttributeValue(attributeKey: string, value: any)
self.Instance.Attributes:SetAttribute(attributeKey, newValue)
-- 死亡判断
local isDied = false
if attributeKey == "hp" and self.Stats.Died == false then
if self.Config[attributeKey] <= 0 then self:Died() end
if self.Config[attributeKey] <= 0 then self:Died() isDied = true end
end
return newValue, isDied
end
function Character:GetState(state: string)

View File

@ -17,18 +17,19 @@ setmetatable(Move, {__index = Behaviour})
function Move:Init(PlayerAI, Character: TypeList.Character, Player: Player)
local self = Behaviour:Init(PlayerAI, Character, script.Name)
self.Player = Player
self.Mobs = nil
setmetatable(self, Move)
return self
end
function Move:Check(CheckInfo: table)
if Behaviour.CheckStat(self) then return -1, self.CheckData end
self:CheckClean()
local PlayerMobs = MobsProxy:GetPlayerMobs(self.Player)
if not PlayerMobs then return end
self.Mobs = MobsProxy:GetPlayerMobs(self.Player)
local closestMob, minDistance = nil, math.huge
for _, Mob in PlayerMobs do
for _, Mob in self.Mobs do
if Mob.Instance and Mob.Instance.PrimaryPart then
local dist = (Mob.Instance.PrimaryPart.Position - self.Character.Instance.PrimaryPart.Position).Magnitude
if dist < minDistance then

View File

@ -25,6 +25,7 @@ local COOLDOWN = 1
function SwordWave:Init(PlayerAI, Character: TypeList.Character, Player: Player)
local self = Behaviour:Init(PlayerAI, Character, script.Name)
self.Player = Player
self.Mobs = nil
setmetatable(self, SwordWave)
self.OrgCooldown = COOLDOWN
self:StartCooldownTask()
@ -36,12 +37,13 @@ end
function SwordWave:Check(CheckInfo: table)
if Behaviour.CheckStat(self) then return -1, self.CheckData end
self:CheckClean()
local PlayerMobs = MobsProxy:GetPlayerMobs(self.Player)
if not PlayerMobs then return end
self.Mobs = MobsProxy:GetPlayerMobs(self.Player)
if not self.Mobs then return end
local closestMob, minDistance = nil, CAST_DISTANCE
for _, Mob in PlayerMobs do
for _, Mob in self.Mobs do
if Mob.Instance and Mob.Instance.PrimaryPart then
local dist = (Mob.Instance.PrimaryPart.Position - self.Character.Instance.PrimaryPart.Position).Magnitude
if dist < minDistance then

View File

@ -136,7 +136,7 @@ function DamageProxy:TakeDamage(Caster: TypeList.Character, Victim: TypeList.Cha
for _, DamageInfo in DamageInfos do
if not Victim then continue end
if Victim:GetState("Died") then continue end
-- if not Victim.Parent then continue end
local Damage = DamageInfo.Damage
local DamageType = DamageInfo.DamageType
@ -145,8 +145,9 @@ function DamageProxy:TakeDamage(Caster: TypeList.Character, Victim: TypeList.Cha
-- 伤害计算
local VictimHealth = Victim:GetAttributeValue("hp")
Victim:ChangeAttributeValue("hp", math.max(0, VictimHealth - Damage))
print("伤害数据打印", Damage, VictimHealth)
local resultValue, isDied = Victim:ChangeAttributeValue("hp", math.max(0, VictimHealth - Damage))
print("伤害数据打印", Damage, VictimHealth, resultValue, isDied)
if isDied then break end
end
-- 实际发送数据
Communicate:SendToClient(RE_DamagePerformance, "Damage", Caster.Player, clientDamageInfos)

View File

@ -88,9 +88,10 @@ function PlayerAI:Update()
end
-- 动态添加行为
function PlayerAI:AddBehaviour(BehaviourName: string)
function PlayerAI:AddBehaviour(BehaviourName: string, WearingSlot: number?)
if not Behaviours[BehaviourName] then warn("Behaviour not found") return end
local newBehaviour = Behaviours[BehaviourName]:Init(self, self.Character, self.Player)
newBehaviour.WearingSlot = WearingSlot or 4
self.BehaviourList[BehaviourName] = newBehaviour
end
@ -101,9 +102,22 @@ end
-- 获取客户端行为列表
function PlayerAI:GetClientBehaviourList()
local clientBehaviourList = {}
local clientBehaviourList = {
Ability = {},
WearAbility = {},
}
for _, behaviour in self.BehaviourList do
clientBehaviourList[behaviour.ScriptName] = behaviour.Cooldown
if behaviour.ScriptName == "Move" then continue end
local wearTable
if behaviour.WearingSlot == 1 then
wearTable = clientBehaviourList.Ability
else
wearTable = clientBehaviourList.WearAbility
end
table.insert(wearTable, {
AbilityName = behaviour.ScriptName,
Cooldown = behaviour.Cooldown,
})
end
return clientBehaviourList
end
@ -130,6 +144,9 @@ function PlayerAI:Destroy()
task.cancel(self.LoopTask)
self.LoopTask = nil
end
for k, v in pairs(self) do
self[k] = nil
end
self = nil
end

View File

@ -198,6 +198,10 @@ function PlayerFightProxy:OnPlayerRemoving(Player: Player)
Communicate:SendToClientFree(RE_CleanPlayerPerformance, Player)
-- 正常清除玩家该模块下数据
if not PlayerFightProxy.pData[Player.UserId] then warn("PlayerFight Remove Data not found", Player.Name) return end
PlayerFightProxy.pData[Player.UserId].PlayerAI:Destroy()
PlayerFightProxy.pData[Player.UserId].PlayerAI = nil
PlayerFightProxy.pData[Player.UserId] = nil
end

View File

@ -0,0 +1,98 @@
-- 45度俯视角相机脚本
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
-- local CAMERA_DISTANCE = 20
-- local CAMERA_HEIGHT = 20
-- local CAMERA_ANGLE = math.rad(-45)
-- local SCREEN_OFFSET = 5 -- 让角色在画面下方,数值越大越靠下
-- local function getCharacterRoot()
-- local character = player.Character
-- if character then
-- return character:FindFirstChild("HumanoidRootPart") or character:FindFirstChildWhichIsA("BasePart")
-- end
-- return nil
-- end
-- RunService.RenderStepped:Connect(function()
-- local root = getCharacterRoot()
-- if root then
-- -- 让lookAt点在角色身后一点以角色朝向为基准反方向偏移
-- local lookAt = root.Position - Vector3.new(-1, 0, 0) * SCREEN_OFFSET
-- -- 相机位置俯视角Y高度固定
-- local offset = Vector3.new(
-- -CAMERA_DISTANCE * math.cos(CAMERA_ANGLE),
-- CAMERA_HEIGHT,
-- 0
-- )
-- local cameraPos = lookAt + offset
-- camera.CameraType = Enum.CameraType.Scriptable
-- camera.CFrame = CFrame.new(cameraPos, lookAt)
-- end
-- end)
local CAMERA_DISTANCE = 20
local CAMERA_HEIGHT = 20
local CAMERA_ANGLE = math.rad(-45)
local SCREEN_OFFSET = 5
local DEADZONE_SIZE = Vector2.new(10, 15) -- 死区宽高(世界坐标)
local cameraCenter -- 当前相机中心点
local function getCharacterRoot()
local character = player.Character
if character then
return character:FindFirstChild("HumanoidRootPart") or character:FindFirstChildWhichIsA("BasePart")
end
return nil
end
player.CharacterAdded:Connect(function(character)
local root = getCharacterRoot()
if root then
cameraCenter = Vector3.new(root.Position.X, 0, root.Position.Z)
end
end)
RunService.RenderStepped:Connect(function()
local root = getCharacterRoot()
if root then
if not cameraCenter then
cameraCenter = Vector3.new(root.Position.X, 0, root.Position.Z)
end
-- 只考虑X/Z平面
local delta = Vector2.new(root.Position.X - cameraCenter.X, root.Position.Z - cameraCenter.Z)
local halfSize = DEADZONE_SIZE / 2
-- 检查是否超出死区
local moveX, moveZ = 0, 0
if math.abs(delta.X) > halfSize.X then
moveX = delta.X - math.sign(delta.X) * halfSize.X
end
if math.abs(delta.Y) > halfSize.Y then
moveZ = delta.Y - math.sign(delta.Y) * halfSize.Y
end
-- 更新相机中心点
cameraCenter = cameraCenter + Vector3.new(moveX, 0, moveZ)
-- 让lookAt点在相机中心点后方
local lookAt = cameraCenter - Vector3.new(-1, 0, 0) * SCREEN_OFFSET
local offset = Vector3.new(
-CAMERA_DISTANCE * math.cos(CAMERA_ANGLE),
CAMERA_HEIGHT,
0
)
local cameraPos = lookAt + offset
camera.CameraType = Enum.CameraType.Scriptable
camera.CFrame = CFrame.new(cameraPos, lookAt)
end
end)

View File

@ -35,8 +35,8 @@ end
function DamageBoard:CreateNormalDamageBoard(DamageDetail: table)
local DamageInitPos = DamageDetail.DamagePosition
local BiasPos = Vector3.new(0, 3, 0)
local MultPos = Vector3.new(0, 1, 0)
local BiasPos = Vector3.new(0, 4, 0)
local MultPos = Vector3.new(0, 1.5, 0)
-- 根据元素类型排序
local DamageInfos = DamageDetail["DamageInfos"]
@ -48,21 +48,22 @@ function DamageBoard:CreateNormalDamageBoard(DamageDetail: table)
local index = 0
local BoardInstance = Boards["Board1"]:Clone()
local BoardCanvas = BoardInstance:FindFirstChild("BillboardGui"):FindFirstChild("Canvas")
local DamageInstance = BoardCanvas:FindFirstChild("Damage")
for _, DamageInfo in DamageInfos do
local BoardCanvas = BoardInstance:FindFirstChild("BillboardGui"):FindFirstChild("Canvas")
local DamageInstance = BoardCanvas:FindFirstChild("Damage")
local ElementInstance = BoardCanvas:FindFirstChild("Element")
ElementInstance.ImageColor3 = element_color[DamageInfo.ElementType]
DamageInstance.Text = DamageInfo.Damage
DamageInstance.TextColor3 = element_color[DamageInfo.ElementType]
local NewDamageInstance = DamageInstance:Clone()
NewDamageInstance.Text = DamageInfo.Damage
NewDamageInstance.TextColor3 = element_color[DamageInfo.ElementType]
NewDamageInstance.Visible = true
NewDamageInstance.Parent = BoardCanvas
NewDamageInstance.LayoutOrder = 10 - index
BoardInstance.Position = DamageInitPos + BiasPos + MultPos * index
BoardInstance.Parent = game.Workspace
game.Debris:AddItem(BoardInstance, 1)
index = index + 1
end
game.Debris:AddItem(BoardInstance, 1)
end

View File

@ -13,9 +13,14 @@ local RE_CleanPlayerPerformance = EventsFolder:FindFirstChild("RE_CleanPlayerPer
local RE_DamagePerformance = EventsFolder:FindFirstChild("RE_DamagePerformance")
local RE_AbilityPerformance = EventsFolder:FindFirstChild("RE_AbilityPerformance")
--> Dependencies
local UIManager = require(script.Parent.Parent.UI.UIManager)
local Signal = require(ReplicatedStorage.Tools.Signal)
--> Variables
local LocalPlayer = game.Players.LocalPlayer
local DamageBoard = require(script.DamageBoard)
local showAbilitySignal = Signal.new(Signal.ENUM.SHOW_ABILITY)
--------------------------------------------------------------------------------
-- 生成本地化表现目录
@ -64,6 +69,9 @@ RE_PerformanceEvent.OnClientEvent:Connect(function(ServerTime: number, CastTag:
local BehaviourTable = PerformanceClient.pData[UserId][BehaviourName]
BehaviourTable[CastInfo.UniqueId] = Behaviours[BehaviourName]:Init(CasterPlayer, CastInfo, delayTime, CastState)
BehaviourTable[CastInfo.UniqueId]:Show(CasterPlayer, CastInfo, delayTime, CastState)
-- 发送更新信号
showAbilitySignal:Fire(BehaviourName, CastInfo)
end
elseif CastTag == "Destroy" then
if not PerformanceClient.pData[UserId][BehaviourName] then return end
@ -76,6 +84,7 @@ RE_PerformanceEvent.OnClientEvent:Connect(function(ServerTime: number, CastTag:
end
end)
-- 监听伤害表现事件调用
RE_DamagePerformance.OnClientEvent:Connect(function(ServerTime: number, CastTag: string, CastPlayer: Player, DamageDetail: table)
-- print("DamagePerformance", ServerTime, CastTag, CastPlayer, DamageDetail)
@ -85,11 +94,11 @@ end)
-- 这里主要是初始化
RE_AbilityPerformance.OnClientEvent:Connect(function(ServerTime: number, CastTag: string, CastPlayer: Player, AbilityDetail: table)
print("AbilityPerformance", ServerTime, CastTag, CastPlayer, AbilityDetail)
UIManager:OpenWindow("AbilityStateWindow", AbilityDetail)
end)
-- 清理玩家所有表现数据
RE_CleanPlayerPerformance.OnClientEvent:Connect(function(CleanedPlayer: Player)
-- print("CleanPlayerPerformance", CleanedPlayer)
local UserId = CleanedPlayer.UserId
if not PerformanceClient.pData[UserId] then return end
for _, BehaviourList in pairs(PerformanceClient.pData[UserId]) do

View File

@ -0,0 +1,31 @@
local UIManager = {}
local FolderWindows = script.Parent.Windows
UIManager.Instances = {}
UIManager.Windows = {}
for _, child in FolderWindows:GetChildren() do
UIManager.Windows[child.Name] = require(child)
end
function UIManager:OpenWindow(WindowName: string, Data: table?)
if not UIManager.Windows[WindowName] then
warn("UIManager:OpenWindow() 窗口不存在:" .. WindowName)
return
end
UIManager.Instances[WindowName] = UIManager.Windows[WindowName]:Init(self, Data)
UIManager.Instances[WindowName]:OnOpenWindow()
end
function UIManager:CloseWindow(WindowName: string)
if not UIManager.Instances[WindowName] then
warn("UIManager:CloseWindow() 窗口不存在:" .. WindowName)
return
end
UIManager.Instances[WindowName]:OnCloseWindow()
end
return UIManager

View File

@ -0,0 +1,76 @@
local AbilityShow = {}
AbilityShow.__index = AbilityShow
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Utils = require(ReplicatedStorage.Tools.Utils)
local Localization = require(ReplicatedStorage.Tools.Localization)
local JsonAbility = require(ReplicatedStorage.Json.Ability)
local Signal = require(ReplicatedStorage.Tools.Signal)
local showAbilitySignal = Signal.new(Signal.ENUM.SHOW_ABILITY)
function AbilityShow:Init(data: table)
local self = {}
self.Data = data
self.Variables = {
["_imgIcon"] = 0,
["_imgMask"] = 0,
["_tmpCd"] = 0,
}
self.Task = nil
self.SignalConnections = {}
local con = showAbilitySignal:Connect(function(BehaviourName, CastInfo)
if BehaviourName == self.Data.AbilityName then
self:Refresh()
end
end)
table.insert(self.SignalConnections, con)
setmetatable(self, AbilityShow)
return self
end
function AbilityShow:Refresh()
if self.Task then
task.cancel(self.Task)
self.Task = nil
end
if self.Data.AbilityName ~= nil then
self.Variables._imgIcon.Image = Localization:GetImageData(Utils:GetSpecialKeyDataFromJson(JsonAbility, "behaviourName", self.Data.AbilityName).icon)
end
self.Variables._imgIcon.Visible = true
self.Variables._imgMask.Visible = false
self.Variables._tmpCd.Text = ""
if self.Data.Cooldown > 0 then
self.Variables._imgMask.Visible = true
self.Variables._tmpCd.Text = self.Data.Cooldown
self.Task = task.spawn(function()
local cd = self.Data.Cooldown
while cd > 0 do
task.wait(0.2)
cd -= 0.2
self.Variables._tmpCd.Text = string.format("%.1f", cd)
end
self.Variables._imgMask.Visible = false
self.Variables._tmpCd.Text = ""
end)
end
end
function AbilityShow:Destroy()
if self.Task then
task.cancel(self.Task)
self.Task = nil
end
for k, v in pairs(self) do
self[k] = nil
end
self = nil
end
return AbilityShow

View File

@ -0,0 +1,49 @@
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Dependencies
local UIWindow = require(ReplicatedStorage.Base.UIWindow)
local UIEnums = require(ReplicatedStorage.Base.UIEnums)
--> Components
local AbilityShow = require(script.AbilityShow)
--------------------------------------------------------------------------------
local AbilityStateWindows = {}
AbilityStateWindows.__index = AbilityStateWindows
setmetatable(AbilityStateWindows, {__index = UIWindow})
function AbilityStateWindows:Init(UIManager: table, Data: table?)
local self = UIWindow:Init(UIManager, Data)
setmetatable(self, AbilityStateWindows)
self.Variables = {
["__listAbility"] = 0,
["__listWearAbility"] = 0,
}
self.UIRootName = "ui_w_abilityState"
self.UIParentName = UIEnums.UIParent.UIRoot
return self
end
function AbilityStateWindows:OnOpenWindow()
UIWindow.OnOpenWindow(self)
self.Variables["__listAbility"]:AddComponent(AbilityShow)
self.Variables["__listWearAbility"]:AddComponent(AbilityShow)
self.Variables["__listAbility"]:SetData(self.Data.Ability)
self.Variables["__listWearAbility"]:SetData(self.Data.WearAbility)
end
function AbilityStateWindows:OnCloseWindow()
UIWindow.OnCloseWindow(self)
self.Variables["__listAbility"]:Clean()
self.Variables["__listWearAbility"]:Clean()
end
return AbilityStateWindows