更新
This commit is contained in:
parent
2d612189d5
commit
b319dfeb27
@ -21,6 +21,9 @@
|
||||
},
|
||||
"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
|
@ -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
|
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!
|
||||
]]
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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
|
||||
|
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
|
||||
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
|
||||
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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
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)
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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