Compare commits
2 Commits
587690b2fc
...
b319dfeb27
Author | SHA1 | Date | |
---|---|---|---|
b319dfeb27 | |||
2d612189d5 |
@ -21,6 +21,9 @@
|
|||||||
},
|
},
|
||||||
"ClientMain": {
|
"ClientMain": {
|
||||||
"$path": "src/StarterPlayerScripts/ClientMain"
|
"$path": "src/StarterPlayerScripts/ClientMain"
|
||||||
|
},
|
||||||
|
"UI": {
|
||||||
|
"$path": "src/StarterPlayerScripts/UI"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Binary file not shown.
0
src/ReplicatedStorage/Base/TypeList.luau
Normal file
0
src/ReplicatedStorage/Base/TypeList.luau
Normal file
7
src/ReplicatedStorage/Base/UIEnums.luau
Normal file
7
src/ReplicatedStorage/Base/UIEnums.luau
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
local UIEnums = {}
|
||||||
|
|
||||||
|
UIEnums.UIParent = {
|
||||||
|
UIRoot = "UIRoot",
|
||||||
|
}
|
||||||
|
|
||||||
|
return UIEnums
|
124
src/ReplicatedStorage/Base/UIList.luau
Normal file
124
src/ReplicatedStorage/Base/UIList.luau
Normal 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
|
119
src/ReplicatedStorage/Base/UIWindow.luau
Normal file
119
src/ReplicatedStorage/Base/UIWindow.luau
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
local UIWindow = {}
|
||||||
|
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")
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function UIWindow:SetData(Data: table?)
|
||||||
|
self.Data = Data
|
||||||
|
self:OnDataChanged()
|
||||||
|
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 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
|
||||||
|
self.AutoInject = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
--> 覆盖用
|
||||||
|
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
|
7
src/ReplicatedStorage/Data/SignalEnum.luau
Normal file
7
src/ReplicatedStorage/Data/SignalEnum.luau
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
local SignalEnum = {}
|
||||||
|
|
||||||
|
SignalEnum = {
|
||||||
|
SHOW_ABILITY = "SHOW_ABILITY",
|
||||||
|
}
|
||||||
|
|
||||||
|
return SignalEnum
|
@ -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]}
|
||||||
]
|
]
|
@ -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!
|
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 freeRunnerThread = nil
|
||||||
|
|
||||||
local function acquireRunnerThreadAndCallEventHandler(fn, ...)
|
local function acquireRunnerThreadAndCallEventHandler(fn, ...)
|
||||||
@ -72,6 +73,7 @@ local Signals = {}
|
|||||||
local Signal = {}
|
local Signal = {}
|
||||||
Signal.__index = Signal
|
Signal.__index = Signal
|
||||||
Signal.ClassName = "Signal"
|
Signal.ClassName = "Signal"
|
||||||
|
Signal.ENUM = SignalEnum
|
||||||
|
|
||||||
export type Signal = typeof(Signal)
|
export type Signal = typeof(Signal)
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
-- 客户端调用的内容
|
-- 本地化读取工具
|
||||||
local RunService = game:GetService("RunService")
|
|
||||||
if RunService:IsClient() then return end
|
|
||||||
|
|
||||||
local Localization = {}
|
local Localization = {}
|
||||||
|
|
||||||
--> Services
|
--> Services
|
||||||
@ -19,10 +16,7 @@ local JsonImage_Zh_CN = require(ReplicatedStorage.Json.Image_Zh_CN)
|
|||||||
|
|
||||||
--> Variables
|
--> Variables
|
||||||
local LocalPlayer = game.Players.LocalPlayer
|
local LocalPlayer = game.Players.LocalPlayer
|
||||||
local SystemLocaleId = LocalizationService:GetSystemLocaleId()
|
local SystemLocaleId = LocalizationService.SystemLocaleId
|
||||||
local JsonLanguage, JsonImage = Localization:GetLocalizationJson()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- 获取本地Json文件
|
-- 获取本地Json文件
|
||||||
function Localization:GetLocalizationJson()
|
function Localization:GetLocalizationJson()
|
||||||
@ -33,6 +27,8 @@ function Localization:GetLocalizationJson()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local JsonLanguage, JsonImage = Localization:GetLocalizationJson()
|
||||||
|
|
||||||
-- 获取文本Id数据
|
-- 获取文本Id数据
|
||||||
function Localization:GetLanguageData(Id: number)
|
function Localization:GetLanguageData(Id: number)
|
||||||
if not Id then return end
|
if not Id then return end
|
||||||
|
151
src/ReplicatedStorage/Tools/Signal.luau
Normal file
151
src/ReplicatedStorage/Tools/Signal.luau
Normal 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
|
@ -70,6 +70,16 @@ function Utils:GetIdDataFromJson(JsonData: table, id: number)
|
|||||||
return nil -- 没有找到对应id
|
return nil -- 没有找到对应id
|
||||||
end
|
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
|
||||||
|
|
||||||
-- 获取随机id,ExceptIdList为可选参数,如果传入则排除ExceptIdList中的id
|
-- 获取随机id,ExceptIdList为可选参数,如果传入则排除ExceptIdList中的id
|
||||||
function Utils:GetRandomIdFromJson(JsonData: table, ExceptIdList: table?)
|
function Utils:GetRandomIdFromJson(JsonData: table, ExceptIdList: table?)
|
||||||
local rng = Random.new()
|
local rng = Random.new()
|
||||||
@ -218,6 +228,42 @@ function Utils:TableSafeAddTableValue(AttributesData: table, AddTableValue: tabl
|
|||||||
end
|
end
|
||||||
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
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -52,6 +52,23 @@ function Behaviour:Execute()
|
|||||||
|
|
||||||
end
|
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()
|
function Behaviour:StartCooldownTask()
|
||||||
self.Cooldown = self.OrgCooldown
|
self.Cooldown = self.OrgCooldown
|
||||||
@ -115,6 +132,9 @@ function Behaviour:Destroy()
|
|||||||
self.PlayerAI:RemoveBehaviourUniqueId(UniqueId)
|
self.PlayerAI:RemoveBehaviourUniqueId(UniqueId)
|
||||||
end
|
end
|
||||||
self.UniqueIdList = {}
|
self.UniqueIdList = {}
|
||||||
|
for k, v in pairs(self) do
|
||||||
|
self[k] = nil
|
||||||
|
end
|
||||||
self = nil
|
self = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -93,10 +93,11 @@ function Character:ChangeAttributeValue(attributeKey: string, value: any)
|
|||||||
self.Instance.Attributes:SetAttribute(attributeKey, newValue)
|
self.Instance.Attributes:SetAttribute(attributeKey, newValue)
|
||||||
|
|
||||||
-- 死亡判断
|
-- 死亡判断
|
||||||
|
local isDied = false
|
||||||
if attributeKey == "hp" and self.Stats.Died == false then
|
if attributeKey == "hp" and self.Stats.Died == false then
|
||||||
|
if self.Config[attributeKey] <= 0 then self:Died() isDied = true end
|
||||||
if self.Config[attributeKey] <= 0 then self:Died() end
|
|
||||||
end
|
end
|
||||||
|
return newValue, isDied
|
||||||
end
|
end
|
||||||
|
|
||||||
function Character:GetState(state: string)
|
function Character:GetState(state: string)
|
||||||
|
@ -17,18 +17,19 @@ setmetatable(Move, {__index = Behaviour})
|
|||||||
function Move:Init(PlayerAI, Character: TypeList.Character, Player: Player)
|
function Move:Init(PlayerAI, Character: TypeList.Character, Player: Player)
|
||||||
local self = Behaviour:Init(PlayerAI, Character, script.Name)
|
local self = Behaviour:Init(PlayerAI, Character, script.Name)
|
||||||
self.Player = Player
|
self.Player = Player
|
||||||
|
self.Mobs = nil
|
||||||
setmetatable(self, Move)
|
setmetatable(self, Move)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Move:Check(CheckInfo: table)
|
function Move:Check(CheckInfo: table)
|
||||||
if Behaviour.CheckStat(self) then return -1, self.CheckData end
|
if Behaviour.CheckStat(self) then return -1, self.CheckData end
|
||||||
|
self:CheckClean()
|
||||||
|
|
||||||
local PlayerMobs = MobsProxy:GetPlayerMobs(self.Player)
|
self.Mobs = MobsProxy:GetPlayerMobs(self.Player)
|
||||||
if not PlayerMobs then return end
|
|
||||||
|
|
||||||
local closestMob, minDistance = nil, math.huge
|
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
|
if Mob.Instance and Mob.Instance.PrimaryPart then
|
||||||
local dist = (Mob.Instance.PrimaryPart.Position - self.Character.Instance.PrimaryPart.Position).Magnitude
|
local dist = (Mob.Instance.PrimaryPart.Position - self.Character.Instance.PrimaryPart.Position).Magnitude
|
||||||
if dist < minDistance then
|
if dist < minDistance then
|
||||||
|
@ -25,6 +25,7 @@ local COOLDOWN = 1
|
|||||||
function SwordWave:Init(PlayerAI, Character: TypeList.Character, Player: Player)
|
function SwordWave:Init(PlayerAI, Character: TypeList.Character, Player: Player)
|
||||||
local self = Behaviour:Init(PlayerAI, Character, script.Name)
|
local self = Behaviour:Init(PlayerAI, Character, script.Name)
|
||||||
self.Player = Player
|
self.Player = Player
|
||||||
|
self.Mobs = nil
|
||||||
setmetatable(self, SwordWave)
|
setmetatable(self, SwordWave)
|
||||||
self.OrgCooldown = COOLDOWN
|
self.OrgCooldown = COOLDOWN
|
||||||
self:StartCooldownTask()
|
self:StartCooldownTask()
|
||||||
@ -36,12 +37,13 @@ end
|
|||||||
|
|
||||||
function SwordWave:Check(CheckInfo: table)
|
function SwordWave:Check(CheckInfo: table)
|
||||||
if Behaviour.CheckStat(self) then return -1, self.CheckData end
|
if Behaviour.CheckStat(self) then return -1, self.CheckData end
|
||||||
|
self:CheckClean()
|
||||||
|
|
||||||
local PlayerMobs = MobsProxy:GetPlayerMobs(self.Player)
|
self.Mobs = MobsProxy:GetPlayerMobs(self.Player)
|
||||||
if not PlayerMobs then return end
|
if not self.Mobs then return end
|
||||||
|
|
||||||
local closestMob, minDistance = nil, CAST_DISTANCE
|
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
|
if Mob.Instance and Mob.Instance.PrimaryPart then
|
||||||
local dist = (Mob.Instance.PrimaryPart.Position - self.Character.Instance.PrimaryPart.Position).Magnitude
|
local dist = (Mob.Instance.PrimaryPart.Position - self.Character.Instance.PrimaryPart.Position).Magnitude
|
||||||
if dist < minDistance then
|
if dist < minDistance then
|
||||||
@ -97,6 +99,25 @@ function SwordWave:OnHit(Victim: TypeList.Character)
|
|||||||
Damage = 30,
|
Damage = 30,
|
||||||
Type = DamageProxy.DamageType.SKILL,
|
Type = DamageProxy.DamageType.SKILL,
|
||||||
Tag = DamageProxy.DamageTag.NORMAL,
|
Tag = DamageProxy.DamageTag.NORMAL,
|
||||||
|
ElementType = DamageProxy.ElementType.NONE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Damage = 5,
|
||||||
|
Type = DamageProxy.DamageType.SKILL,
|
||||||
|
Tag = DamageProxy.DamageTag.NORMAL,
|
||||||
|
ElementType = DamageProxy.ElementType.FIRE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Damage = 5,
|
||||||
|
Type = DamageProxy.DamageType.SKILL,
|
||||||
|
Tag = DamageProxy.DamageTag.NORMAL,
|
||||||
|
ElementType = DamageProxy.ElementType.ICE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Damage = 5,
|
||||||
|
Type = DamageProxy.DamageType.SKILL,
|
||||||
|
Tag = DamageProxy.DamageTag.NORMAL,
|
||||||
|
ElementType = DamageProxy.ElementType.SHADOW,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
|
@ -24,17 +24,27 @@ local DamageTag = {
|
|||||||
NORMAL = "Normal", -- 普攻
|
NORMAL = "Normal", -- 普攻
|
||||||
CRIT = "Crit", -- 暴击
|
CRIT = "Crit", -- 暴击
|
||||||
}
|
}
|
||||||
|
local ElementType = {
|
||||||
|
NONE = "None", -- 无
|
||||||
|
FIRE = "Fire",
|
||||||
|
ICE = "Ice", -- 冰
|
||||||
|
SHADOW = "Shadow", -- 暗影
|
||||||
|
LIGHT = "Light", -- 光
|
||||||
|
}
|
||||||
|
|
||||||
export type DamageTag = "Normal" | "Critical"
|
export type DamageTag = "Normal" | "Critical"
|
||||||
export type DamageType = "Normal" | "Skill"
|
export type DamageType = "Normal" | "Skill"
|
||||||
|
export type ElementType = "Fire" | "Ice" | "Shadow" | "Light"
|
||||||
export type DamageInfo = {
|
export type DamageInfo = {
|
||||||
Damage: number,
|
Damage: number,
|
||||||
Type: DamageType,
|
Type: DamageType,
|
||||||
Tag: DamageTag,
|
Tag: DamageTag,
|
||||||
|
ElementType: ElementType,
|
||||||
}
|
}
|
||||||
|
|
||||||
DamageProxy.DamageType = DamageType
|
DamageProxy.DamageType = DamageType
|
||||||
DamageProxy.DamageTag = DamageTag
|
DamageProxy.DamageTag = DamageTag
|
||||||
|
DamageProxy.ElementType = ElementType
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -125,14 +135,19 @@ function DamageProxy:TakeDamage(Caster: TypeList.Character, Victim: TypeList.Cha
|
|||||||
clientDamageInfos.DamageInfos = Utils:DeepCopyTable(DamageInfos)
|
clientDamageInfos.DamageInfos = Utils:DeepCopyTable(DamageInfos)
|
||||||
|
|
||||||
for _, DamageInfo in DamageInfos do
|
for _, DamageInfo in DamageInfos do
|
||||||
|
if not Victim then continue end
|
||||||
|
-- if not Victim.Parent then continue end
|
||||||
|
|
||||||
local Damage = DamageInfo.Damage
|
local Damage = DamageInfo.Damage
|
||||||
local DamageType = DamageInfo.DamageType
|
local DamageType = DamageInfo.DamageType
|
||||||
local DamageTag = DamageInfo.DamageTag
|
local DamageTag = DamageInfo.DamageTag
|
||||||
|
local ElementType = DamageInfo.ElementType
|
||||||
|
|
||||||
-- 伤害计算
|
-- 伤害计算
|
||||||
local VictimHealth = Victim:GetAttributeValue("hp")
|
local VictimHealth = Victim:GetAttributeValue("hp")
|
||||||
Victim:ChangeAttributeValue("hp", math.max(0, VictimHealth - Damage))
|
local resultValue, isDied = Victim:ChangeAttributeValue("hp", math.max(0, VictimHealth - Damage))
|
||||||
print("伤害数据打印", Damage, VictimHealth)
|
print("伤害数据打印", Damage, VictimHealth, resultValue, isDied)
|
||||||
|
if isDied then break end
|
||||||
end
|
end
|
||||||
-- 实际发送数据
|
-- 实际发送数据
|
||||||
Communicate:SendToClient(RE_DamagePerformance, "Damage", Caster.Player, clientDamageInfos)
|
Communicate:SendToClient(RE_DamagePerformance, "Damage", Caster.Player, clientDamageInfos)
|
||||||
|
@ -98,7 +98,8 @@ task.defer(function()
|
|||||||
{
|
{
|
||||||
Damage = Mob.Config.attack,
|
Damage = Mob.Config.attack,
|
||||||
DamageType = DamageProxy.DamageType.PHYSICAL,
|
DamageType = DamageProxy.DamageType.PHYSICAL,
|
||||||
DamageTag = DamageProxy.DamageTag.NORMAL
|
DamageTag = DamageProxy.DamageTag.NORMAL,
|
||||||
|
ElementType = DamageProxy.ElementType.NONE,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -88,9 +88,10 @@ function PlayerAI:Update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- 动态添加行为
|
-- 动态添加行为
|
||||||
function PlayerAI:AddBehaviour(BehaviourName: string)
|
function PlayerAI:AddBehaviour(BehaviourName: string, WearingSlot: number?)
|
||||||
if not Behaviours[BehaviourName] then warn("Behaviour not found") return end
|
if not Behaviours[BehaviourName] then warn("Behaviour not found") return end
|
||||||
local newBehaviour = Behaviours[BehaviourName]:Init(self, self.Character, self.Player)
|
local newBehaviour = Behaviours[BehaviourName]:Init(self, self.Character, self.Player)
|
||||||
|
newBehaviour.WearingSlot = WearingSlot or 4
|
||||||
self.BehaviourList[BehaviourName] = newBehaviour
|
self.BehaviourList[BehaviourName] = newBehaviour
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -101,9 +102,22 @@ end
|
|||||||
|
|
||||||
-- 获取客户端行为列表
|
-- 获取客户端行为列表
|
||||||
function PlayerAI:GetClientBehaviourList()
|
function PlayerAI:GetClientBehaviourList()
|
||||||
local clientBehaviourList = {}
|
local clientBehaviourList = {
|
||||||
|
Ability = {},
|
||||||
|
WearAbility = {},
|
||||||
|
}
|
||||||
for _, behaviour in self.BehaviourList do
|
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
|
end
|
||||||
return clientBehaviourList
|
return clientBehaviourList
|
||||||
end
|
end
|
||||||
@ -130,6 +144,9 @@ function PlayerAI:Destroy()
|
|||||||
task.cancel(self.LoopTask)
|
task.cancel(self.LoopTask)
|
||||||
self.LoopTask = nil
|
self.LoopTask = nil
|
||||||
end
|
end
|
||||||
|
for k, v in pairs(self) do
|
||||||
|
self[k] = nil
|
||||||
|
end
|
||||||
self = nil
|
self = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -198,6 +198,10 @@ function PlayerFightProxy:OnPlayerRemoving(Player: Player)
|
|||||||
Communicate:SendToClientFree(RE_CleanPlayerPerformance, Player)
|
Communicate:SendToClientFree(RE_CleanPlayerPerformance, Player)
|
||||||
-- 正常清除玩家该模块下数据
|
-- 正常清除玩家该模块下数据
|
||||||
if not PlayerFightProxy.pData[Player.UserId] then warn("PlayerFight Remove Data not found", Player.Name) return end
|
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
|
PlayerFightProxy.pData[Player.UserId] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
98
src/StarterPlayerScripts/ClientMain/Camera.client.luau
Normal file
98
src/StarterPlayerScripts/ClientMain/Camera.client.luau
Normal 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)
|
@ -6,6 +6,21 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|||||||
--> Variables
|
--> Variables
|
||||||
local FolderDamageBoard = ReplicatedStorage.UI.DamageBoard
|
local FolderDamageBoard = ReplicatedStorage.UI.DamageBoard
|
||||||
|
|
||||||
|
local sort_order = {
|
||||||
|
["None"] = 1,
|
||||||
|
["Fire"] = 2,
|
||||||
|
["Ice"] = 3,
|
||||||
|
["Shadow"] = 4,
|
||||||
|
["Light"] = 5,
|
||||||
|
}
|
||||||
|
local element_color = {
|
||||||
|
["None"] = Color3.fromRGB(218, 218, 218),
|
||||||
|
["Fire"] = Color3.fromRGB(255, 65, 65),
|
||||||
|
["Ice"] = Color3.fromRGB(64, 217, 255),
|
||||||
|
["Shadow"] = Color3.fromRGB(165, 65, 252),
|
||||||
|
["Light"] = Color3.fromRGB(215, 215, 57),
|
||||||
|
}
|
||||||
|
|
||||||
local LocalPlayer = game.Players.LocalPlayer
|
local LocalPlayer = game.Players.LocalPlayer
|
||||||
|
|
||||||
local Boards = {}
|
local Boards = {}
|
||||||
@ -19,15 +34,35 @@ function DamageBoard:GetBoard(Name: string)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function DamageBoard:CreateNormalDamageBoard(DamageDetail: table)
|
function DamageBoard:CreateNormalDamageBoard(DamageDetail: table)
|
||||||
print(DamageDetail)
|
local DamageInitPos = DamageDetail.DamagePosition
|
||||||
|
local BiasPos = Vector3.new(0, 4, 0)
|
||||||
|
local MultPos = Vector3.new(0, 1.5, 0)
|
||||||
|
|
||||||
|
-- 根据元素类型排序
|
||||||
|
local DamageInfos = DamageDetail["DamageInfos"]
|
||||||
|
table.sort(DamageInfos, function(a, b)
|
||||||
|
local a_order = sort_order[a.ElementType] or 99
|
||||||
|
local b_order = sort_order[b.ElementType] or 99
|
||||||
|
return a_order < b_order
|
||||||
|
end)
|
||||||
|
|
||||||
|
local index = 0
|
||||||
local BoardInstance = Boards["Board1"]:Clone()
|
local BoardInstance = Boards["Board1"]:Clone()
|
||||||
BoardInstance.Parent = game.Workspace
|
local BoardCanvas = BoardInstance:FindFirstChild("BillboardGui"):FindFirstChild("Canvas")
|
||||||
-- local biasPos = (LocalPlayer.Character.HumanoidRootPart.Position - DamageDetail.DamagePosition).Unit * 2
|
local DamageInstance = BoardCanvas:FindFirstChild("Damage")
|
||||||
BoardInstance.Position = DamageDetail.DamagePosition + Vector3.new(0, 3, 0)
|
for _, DamageInfo in DamageInfos do
|
||||||
|
|
||||||
local DamageInstance = BoardInstance:FindFirstChild("BillboardGui"):FindFirstChild("Canvas"):FindFirstChild("Damage")
|
local NewDamageInstance = DamageInstance:Clone()
|
||||||
DamageInstance.Text = DamageDetail["DamageInfos"][1].Damage
|
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
|
||||||
|
index = index + 1
|
||||||
|
end
|
||||||
game.Debris:AddItem(BoardInstance, 1)
|
game.Debris:AddItem(BoardInstance, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -13,9 +13,14 @@ local RE_CleanPlayerPerformance = EventsFolder:FindFirstChild("RE_CleanPlayerPer
|
|||||||
local RE_DamagePerformance = EventsFolder:FindFirstChild("RE_DamagePerformance")
|
local RE_DamagePerformance = EventsFolder:FindFirstChild("RE_DamagePerformance")
|
||||||
local RE_AbilityPerformance = EventsFolder:FindFirstChild("RE_AbilityPerformance")
|
local RE_AbilityPerformance = EventsFolder:FindFirstChild("RE_AbilityPerformance")
|
||||||
|
|
||||||
|
--> Dependencies
|
||||||
|
local UIManager = require(script.Parent.Parent.UI.UIManager)
|
||||||
|
local Signal = require(ReplicatedStorage.Tools.Signal)
|
||||||
|
|
||||||
--> Variables
|
--> Variables
|
||||||
local LocalPlayer = game.Players.LocalPlayer
|
local LocalPlayer = game.Players.LocalPlayer
|
||||||
local DamageBoard = require(script.DamageBoard)
|
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]
|
local BehaviourTable = PerformanceClient.pData[UserId][BehaviourName]
|
||||||
BehaviourTable[CastInfo.UniqueId] = Behaviours[BehaviourName]:Init(CasterPlayer, CastInfo, delayTime, CastState)
|
BehaviourTable[CastInfo.UniqueId] = Behaviours[BehaviourName]:Init(CasterPlayer, CastInfo, delayTime, CastState)
|
||||||
BehaviourTable[CastInfo.UniqueId]:Show(CasterPlayer, CastInfo, delayTime, CastState)
|
BehaviourTable[CastInfo.UniqueId]:Show(CasterPlayer, CastInfo, delayTime, CastState)
|
||||||
|
|
||||||
|
-- 发送更新信号
|
||||||
|
showAbilitySignal:Fire(BehaviourName, CastInfo)
|
||||||
end
|
end
|
||||||
elseif CastTag == "Destroy" then
|
elseif CastTag == "Destroy" then
|
||||||
if not PerformanceClient.pData[UserId][BehaviourName] then return end
|
if not PerformanceClient.pData[UserId][BehaviourName] then return end
|
||||||
@ -76,6 +84,7 @@ RE_PerformanceEvent.OnClientEvent:Connect(function(ServerTime: number, CastTag:
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
-- 监听伤害表现事件调用
|
-- 监听伤害表现事件调用
|
||||||
RE_DamagePerformance.OnClientEvent:Connect(function(ServerTime: number, CastTag: string, CastPlayer: Player, DamageDetail: table)
|
RE_DamagePerformance.OnClientEvent:Connect(function(ServerTime: number, CastTag: string, CastPlayer: Player, DamageDetail: table)
|
||||||
-- print("DamagePerformance", ServerTime, CastTag, CastPlayer, DamageDetail)
|
-- print("DamagePerformance", ServerTime, CastTag, CastPlayer, DamageDetail)
|
||||||
@ -85,11 +94,11 @@ end)
|
|||||||
-- 这里主要是初始化
|
-- 这里主要是初始化
|
||||||
RE_AbilityPerformance.OnClientEvent:Connect(function(ServerTime: number, CastTag: string, CastPlayer: Player, AbilityDetail: table)
|
RE_AbilityPerformance.OnClientEvent:Connect(function(ServerTime: number, CastTag: string, CastPlayer: Player, AbilityDetail: table)
|
||||||
print("AbilityPerformance", ServerTime, CastTag, CastPlayer, AbilityDetail)
|
print("AbilityPerformance", ServerTime, CastTag, CastPlayer, AbilityDetail)
|
||||||
|
UIManager:OpenWindow("AbilityStateWindow", AbilityDetail)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- 清理玩家所有表现数据
|
-- 清理玩家所有表现数据
|
||||||
RE_CleanPlayerPerformance.OnClientEvent:Connect(function(CleanedPlayer: Player)
|
RE_CleanPlayerPerformance.OnClientEvent:Connect(function(CleanedPlayer: Player)
|
||||||
-- print("CleanPlayerPerformance", CleanedPlayer)
|
|
||||||
local UserId = CleanedPlayer.UserId
|
local UserId = CleanedPlayer.UserId
|
||||||
if not PerformanceClient.pData[UserId] then return end
|
if not PerformanceClient.pData[UserId] then return end
|
||||||
for _, BehaviourList in pairs(PerformanceClient.pData[UserId]) do
|
for _, BehaviourList in pairs(PerformanceClient.pData[UserId]) do
|
||||||
|
31
src/StarterPlayerScripts/UI/UIManager.luau
Normal file
31
src/StarterPlayerScripts/UI/UIManager.luau
Normal 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
|
@ -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
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user