fix(camera): align FaceCam with head 3D orientation, prevent detachment during ultimates and separate hotkeys
This commit is contained in:
1 parent
276cb14299
commit
a396f1227c
3 files changed
+339
-103
No files matched your search
+153
-90
@@ -125,18 +125,11 @@ end
|
||||
-- entity state, and anchor to that model's observed head bone. If the current
|
||||
-- phase does not expose either, do not guess a world position.
|
||||
local function current_facecam_target()
|
||||
local flags = CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags), 60)
|
||||
local phaseField = _G.__FC_PHASE_FIELD or typeof(CS.RPG.Client.GamePhaseManager):GetField('_CurrentPhase', flags)
|
||||
_G.__FC_PHASE_FIELD = phaseField
|
||||
local manager = CS.RPG.Client.GlobalVars.s_GamePhaseManager
|
||||
local phase = nil
|
||||
if manager then
|
||||
local flags = CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags), 60)
|
||||
local field = typeof(CS.RPG.Client.GamePhaseManager):GetField('_CurrentPhase', flags)
|
||||
if field then
|
||||
phase = field:GetValue(manager)
|
||||
end
|
||||
if not phase and manager.PhaseContext then
|
||||
phase = manager.PhaseContext
|
||||
end
|
||||
end
|
||||
local phase = manager and phaseField and phaseField:GetValue(manager)
|
||||
if not phase then return nil, nil, nil, false end
|
||||
|
||||
local now = Time.unscaledTime or 0
|
||||
@@ -203,7 +196,7 @@ local function current_facecam_target()
|
||||
return nil, nil, nil, false
|
||||
end
|
||||
|
||||
if not entity then return nil, nil, nil, true end
|
||||
if not entity then return nil, nil, nil, false end
|
||||
|
||||
local model = model_for(entity)
|
||||
if not model and mode then
|
||||
@@ -213,7 +206,7 @@ local function current_facecam_target()
|
||||
entity, model = fallback, fallbackModel
|
||||
end
|
||||
end
|
||||
if not model then return nil, nil, nil, true end
|
||||
if not model then return nil, nil, nil, false end
|
||||
|
||||
local cachedAlive, cachedActive = pcall(function()
|
||||
return _G.__FC_FACE_HEAD and _G.__FC_FACE_ROOT
|
||||
@@ -228,7 +221,7 @@ local function current_facecam_target()
|
||||
return head, model.transform, entity.RuntimeID, true
|
||||
end
|
||||
|
||||
return nil, model.transform, entity.RuntimeID, true
|
||||
return nil, model.transform, entity.RuntimeID, false
|
||||
end
|
||||
|
||||
-- Log only changes, so diagnostics stay useful during long battles.
|
||||
@@ -263,11 +256,9 @@ end
|
||||
local function facecam_target(cam)
|
||||
local ok, head, root = pcall(function()
|
||||
local battleHead, battleRoot, id, tracked = current_facecam_target()
|
||||
if tracked then
|
||||
if tracked and battleHead and battleRoot then
|
||||
_G.__FC_FACE_ENTITY = id
|
||||
if battleHead and battleRoot then
|
||||
return battleHead, battleRoot
|
||||
end
|
||||
return battleHead, battleRoot
|
||||
end
|
||||
local brain = cam:GetComponent(typeof(CS.Cinemachine.CinemachineBrain))
|
||||
local virtual = brain and brain.ActiveVirtualCamera
|
||||
@@ -277,11 +268,22 @@ local function facecam_target(cam)
|
||||
if not animator and follow.root then
|
||||
animator = follow.root:GetComponentInChildren(typeof(CS.UnityEngine.Animator))
|
||||
end
|
||||
if not animator or not animator.isHuman then return nil, nil end
|
||||
local bone = animator:GetBoneTransform(CS.UnityEngine.HumanBodyBones.Head)
|
||||
return bone, animator.transform
|
||||
if animator and animator.isHuman then
|
||||
local bone = animator:GetBoneTransform(CS.UnityEngine.HumanBodyBones.Head)
|
||||
if bone then return bone, animator.transform end
|
||||
end
|
||||
local nodes = follow:GetComponentsInChildren(typeof(CS.UnityEngine.Transform), true)
|
||||
if nodes then
|
||||
for i = 0, nodes.Length - 1 do
|
||||
local node = nodes[i]
|
||||
if node.name == 'Head_M' and node.gameObject.activeInHierarchy then
|
||||
return node, follow.transform
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end)
|
||||
if ok then return head, root end
|
||||
if ok and head and root then return head, root end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
@@ -291,9 +293,14 @@ local function update_facecam(cam, settings)
|
||||
_G.__FC_FACE_NEXT = now + 0.08
|
||||
local ok, head, root, id, tracked = pcall(current_facecam_target)
|
||||
log_facecam_target(ok, head, root, id, tracked)
|
||||
if ok and tracked then
|
||||
if head and root then
|
||||
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT, _G.__FC_FACE_ENTITY = head, root, id
|
||||
if ok and tracked and head and root then
|
||||
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT, _G.__FC_FACE_ENTITY = head, root, id
|
||||
_G.__FC_FACE_MISSING_SINCE = nil
|
||||
else
|
||||
-- Fall back to Cinemachine active follow target if current phase entity is not resolved
|
||||
local fbHead, fbRoot = facecam_target(cam)
|
||||
if fbHead and fbRoot then
|
||||
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = fbHead, fbRoot
|
||||
_G.__FC_FACE_MISSING_SINCE = nil
|
||||
end
|
||||
end
|
||||
@@ -302,10 +309,29 @@ local function update_facecam(cam, settings)
|
||||
local head, root = _G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT
|
||||
local alive, active = pcall(function() return head and root and head.gameObject.activeInHierarchy end)
|
||||
if not alive or not active then
|
||||
-- Character transformations or ultimate cutscenes can briefly unload/hide a model.
|
||||
-- Hold last valid camera transform; do not drop FaceCam lock during ultimates.
|
||||
-- Immediate re-acquisition: cutscenes or transformations can swap model rigs.
|
||||
-- Try current target or Cinemachine follow target right now in the same frame!
|
||||
local ok, nHead, nRoot, nId, tracked = pcall(current_facecam_target)
|
||||
if ok and tracked and nHead and nRoot then
|
||||
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT, _G.__FC_FACE_ENTITY = nHead, nRoot, nId
|
||||
head, root = nHead, nRoot
|
||||
alive, active = true, true
|
||||
_G.__FC_FACE_MISSING_SINCE = nil
|
||||
else
|
||||
local fbHead, fbRoot = facecam_target(cam)
|
||||
if fbHead and fbRoot then
|
||||
_G.__FC_FACE_HEAD, _G.__FC_FACE_ROOT = fbHead, fbRoot
|
||||
head, root = fbHead, fbRoot
|
||||
alive, active = true, true
|
||||
_G.__FC_FACE_MISSING_SINCE = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not alive or not active then
|
||||
-- Hold last valid camera transform within grace period (10s); do not drop FaceCam lock.
|
||||
_G.__FC_FACE_MISSING_SINCE = _G.__FC_FACE_MISSING_SINCE or now
|
||||
if now - _G.__FC_FACE_MISSING_SINCE > 8.0 then
|
||||
if now - _G.__FC_FACE_MISSING_SINCE > 10.0 then
|
||||
return false
|
||||
end
|
||||
if _G.__FC_POS and _G.__FC_EULER then
|
||||
@@ -330,44 +356,42 @@ local function update_facecam(cam, settings)
|
||||
local eyeOffset = 0.08
|
||||
local faceCenter = head.position + Vector3(0, eyeOffset, 0)
|
||||
|
||||
-- Prioritize character model forward direction so camera stays solidly centered
|
||||
-- in front of the avatar without snapping/flipping during attacks or ultimate cutscenes.
|
||||
local fwd
|
||||
if root then
|
||||
local rX, rZ = root.forward.x, root.forward.z
|
||||
local rMagSq = rX * rX + rZ * rZ
|
||||
if rMagSq > 0.001 then
|
||||
local invR = 1.0 / math.sqrt(rMagSq)
|
||||
fwd = Vector3(rX * invR, 0, rZ * invR)
|
||||
end
|
||||
end
|
||||
if not fwd then
|
||||
local hX, hZ = head.up.x, head.up.z
|
||||
local hMagSq = hX * hX + hZ * hZ
|
||||
if hMagSq > 0.001 then
|
||||
local invH = 1.0 / math.sqrt(hMagSq)
|
||||
fwd = Vector3(hX * invH, 0, hZ * invH)
|
||||
else
|
||||
fwd = Vector3(0, 0, 1)
|
||||
end
|
||||
-- Rotate according to head bone in full 3D (user request: "ให้มันหมุนตามหัวเหมือนเดิม")
|
||||
-- In HSR rigs, head.up points directly outward from the face (facial normal).
|
||||
local fwd = head.up
|
||||
if not fwd or fwd.sqrMagnitude < 0.001 then
|
||||
fwd = root and root.forward or Vector3(0, 0, 1)
|
||||
else
|
||||
fwd = fwd.normalized
|
||||
end
|
||||
|
||||
local desiredPos = faceCenter + fwd * distance
|
||||
local lookDir = faceCenter - desiredPos
|
||||
local desiredRot = CS.UnityEngine.Quaternion.LookRotation(lookDir, Vector3.up)
|
||||
|
||||
-- Smooth interpolation to remove micro-jitter while tracking
|
||||
-- Prevent camera gimbal flip when head tilts steeply up or down
|
||||
local upVector = Vector3.up
|
||||
if math.abs(fwd.y) > 0.88 then
|
||||
upVector = root and root.up or Vector3(0, 0, 1)
|
||||
end
|
||||
local desiredRot = CS.UnityEngine.Quaternion.LookRotation(lookDir, upVector)
|
||||
|
||||
-- Dynamic snapping & smoothing:
|
||||
-- If character dashed / teleported during an attack animation (gap > 3.0m),
|
||||
-- snap immediately so the camera stays solidly glued to the head and never detaches!
|
||||
local dt = Time.unscaledDeltaTime or 0.016
|
||||
if dt <= 0 or dt > 0.1 then dt = 0.016 end
|
||||
local smoothSpeed = 16.0
|
||||
local factor = math.min(1.0, dt * smoothSpeed)
|
||||
|
||||
if _G.__FC_LAST_TRACKED_ENTITY ~= _G.__FC_FACE_ENTITY or not _G.__FC_POS then
|
||||
local currentCamPos = cam.transform.position
|
||||
local distGap = (currentCamPos - desiredPos).magnitude
|
||||
|
||||
if distGap > 3.0 or _G.__FC_LAST_TRACKED_ENTITY ~= _G.__FC_FACE_ENTITY or not _G.__FC_POS then
|
||||
_G.__FC_LAST_TRACKED_ENTITY = _G.__FC_FACE_ENTITY
|
||||
cam.transform.position = desiredPos
|
||||
cam.transform.rotation = desiredRot
|
||||
else
|
||||
cam.transform.position = Vector3.Lerp(cam.transform.position, desiredPos, factor)
|
||||
local smoothSpeed = 22.0
|
||||
local factor = math.min(1.0, dt * smoothSpeed)
|
||||
cam.transform.position = Vector3.Lerp(currentCamPos, desiredPos, factor)
|
||||
cam.transform.rotation = CS.UnityEngine.Quaternion.Slerp(cam.transform.rotation, desiredRot, factor)
|
||||
end
|
||||
|
||||
@@ -514,7 +538,7 @@ local function update_mouse_gate()
|
||||
end
|
||||
|
||||
function _G.__FREECAM_TICK()
|
||||
pcall(function()
|
||||
local ok, tickErr = pcall(function()
|
||||
-- Render callbacks run for several cameras/passes in one frame. Consume
|
||||
-- key-down events and integrate movement only once per Unity frame.
|
||||
local frame = Time.frameCount
|
||||
@@ -529,13 +553,46 @@ function _G.__FREECAM_TICK()
|
||||
return
|
||||
end
|
||||
_G.__FC_INPUT_FRAME = frame
|
||||
if Input.GetKeyDown(KeyCode.F8) or Input.GetKeyDown(KeyCode.Insert) then
|
||||
_G.__FREECAM_ENABLED = not _G.__FREECAM_ENABLED
|
||||
|
||||
-- Hotkey Handling:
|
||||
-- F7: Toggle FaceCam directly
|
||||
-- F8: Toggle FreeCam directly
|
||||
-- Insert: Toggle active / last camera mode
|
||||
local toggleFaceCam = Input.GetKeyDown(KeyCode.F7)
|
||||
local toggleFreeCam = Input.GetKeyDown(KeyCode.F8)
|
||||
local toggleInsert = Input.GetKeyDown(KeyCode.Insert)
|
||||
|
||||
if toggleFaceCam or toggleFreeCam or toggleInsert then
|
||||
local settings = _G.__GAC_CAMERA_SETTINGS or {mode="freecam", distance=2}
|
||||
local defaultMode = settings.mode or "freecam"
|
||||
|
||||
if toggleFaceCam then
|
||||
if _G.__FREECAM_ENABLED and _G.__FC_ACTIVE_MODE == "facecam" then
|
||||
_G.__FREECAM_ENABLED = false
|
||||
else
|
||||
_G.__FREECAM_ENABLED = true
|
||||
_G.__FC_ACTIVE_MODE = "facecam"
|
||||
end
|
||||
elseif toggleFreeCam then
|
||||
if _G.__FREECAM_ENABLED and _G.__FC_ACTIVE_MODE == "freecam" then
|
||||
_G.__FREECAM_ENABLED = false
|
||||
else
|
||||
_G.__FREECAM_ENABLED = true
|
||||
_G.__FC_ACTIVE_MODE = "freecam"
|
||||
end
|
||||
elseif toggleInsert then
|
||||
_G.__FREECAM_ENABLED = not _G.__FREECAM_ENABLED
|
||||
if _G.__FREECAM_ENABLED and not _G.__FC_ACTIVE_MODE then
|
||||
_G.__FC_ACTIVE_MODE = defaultMode
|
||||
end
|
||||
end
|
||||
|
||||
_G.__FC_LAST_TIME = nil
|
||||
local cam = get_active_cam()
|
||||
local activeMode = _G.__FC_ACTIVE_MODE or defaultMode
|
||||
|
||||
if _G.__FREECAM_ENABLED then
|
||||
local settings = _G.__GAC_CAMERA_SETTINGS or {mode="freecam", distance=2}
|
||||
if settings.mode == "facecam" then
|
||||
if activeMode == "facecam" then
|
||||
_G.__FC_FACE_DISTANCE = tonumber(settings.distance) or 2
|
||||
local head, root
|
||||
if cam then head, root = facecam_target(cam) end
|
||||
@@ -550,13 +607,12 @@ function _G.__FREECAM_TICK()
|
||||
_G.__FC_EULER = cam.transform.eulerAngles
|
||||
set_cinemachine_enabled(cam, false)
|
||||
end
|
||||
if settings.mode == "facecam" then
|
||||
show_ui("<color=#00FF88><b>[FaceCam: ON]</b> | F8/Insert: Close</color>")
|
||||
if activeMode == "facecam" then
|
||||
show_ui("<color=#00FF88><b>[FaceCam: ON]</b> | F7/Insert: Close | Scroll: Dist | F8: FreeCam</color>")
|
||||
else
|
||||
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | RMB: Look | F8/Insert: Close</color>")
|
||||
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Height | RMB: Look | F8/Insert: Close | F7: FaceCam</color>")
|
||||
end
|
||||
else
|
||||
local settings = _G.__GAC_CAMERA_SETTINGS or {mode="freecam"}
|
||||
if cam then
|
||||
set_cinemachine_enabled(cam, true)
|
||||
end
|
||||
@@ -568,7 +624,7 @@ function _G.__FREECAM_TICK()
|
||||
_G.__FC_FACE_DISTANCE, _G.__FC_FACE_LOCAL_FORWARD = nil, nil
|
||||
_G.__FC_FACE_MISSING_SINCE = nil
|
||||
if _G.__FC_FACE_ANCHOR then CS.UnityEngine.Object.Destroy(_G.__FC_FACE_ANCHOR); _G.__FC_FACE_ANCHOR = nil end
|
||||
local label = settings.mode == 'facecam' and 'FaceCam' or 'FreeCam'
|
||||
local label = (activeMode == 'facecam') and 'FaceCam' or 'FreeCam'
|
||||
show_ui('<color=#00FF88><b>[' .. label .. ': OFF]</b></color>')
|
||||
end
|
||||
end
|
||||
@@ -591,16 +647,13 @@ function _G.__FREECAM_TICK()
|
||||
-- Immediately restore Cinemachine and deactivate FreeCam/FaceCam so the camera does not
|
||||
-- get pinned in the void (black screen) when returning to overworld scenes like Dewlight Pavilion.
|
||||
pcall(function()
|
||||
local flags = CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags), 60)
|
||||
local phaseField = _G.__FC_PHASE_FIELD or typeof(CS.RPG.Client.GamePhaseManager):GetField("_CurrentPhase", flags)
|
||||
_G.__FC_PHASE_FIELD = phaseField
|
||||
local manager = CS.RPG.Client.GlobalVars.s_GamePhaseManager
|
||||
local currentPhaseName = ""
|
||||
if manager then
|
||||
if manager.CurrentGamePhaseType then
|
||||
currentPhaseName = tostring(manager.CurrentGamePhaseType)
|
||||
end
|
||||
if currentPhaseName == "" and manager.PhaseContext then
|
||||
currentPhaseName = manager.PhaseContext:GetType().Name
|
||||
end
|
||||
end
|
||||
local currentPhase = manager and phaseField and phaseField:GetValue(manager)
|
||||
local currentPhaseName = currentPhase and currentPhase:GetType().Name or ""
|
||||
|
||||
if _G.__FC_LAST_PHASE_NAME and _G.__FC_LAST_PHASE_NAME ~= currentPhaseName and currentPhaseName ~= "" then
|
||||
local cam = get_active_cam()
|
||||
set_cinemachine_enabled(cam, true)
|
||||
@@ -642,7 +695,9 @@ function _G.__FREECAM_TICK()
|
||||
local cam = get_active_cam()
|
||||
if cam then
|
||||
local settings = _G.__GAC_CAMERA_SETTINGS or {mode="freecam", distance=2}
|
||||
if settings.mode == "facecam" then
|
||||
local activeMode = _G.__FC_ACTIVE_MODE or settings.mode or "freecam"
|
||||
|
||||
if activeMode == "facecam" then
|
||||
if not update_facecam(cam, settings) then
|
||||
set_cinemachine_enabled(cam, true)
|
||||
_G.__FREECAM_ENABLED = false
|
||||
@@ -658,8 +713,9 @@ function _G.__FREECAM_TICK()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
set_cinemachine_enabled(cam, false)
|
||||
|
||||
|
||||
if not _G.__FC_POS then
|
||||
_G.__FC_POS = cam.transform.position
|
||||
_G.__FC_EULER = cam.transform.eulerAngles
|
||||
@@ -671,32 +727,35 @@ function _G.__FREECAM_TICK()
|
||||
_G.__FC_LAST_TIME = now
|
||||
|
||||
-- Speed adjustment (+ / -)
|
||||
local cameraAdjusted = false
|
||||
if Input.GetKeyDown(KeyCode.Equals) or Input.GetKeyDown(KeyCode.KeypadPlus) then
|
||||
_G.__FREECAM_SPEED = math.min(300.0, _G.__FREECAM_SPEED * 1.5)
|
||||
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | RMB: Look | F8/Insert: Close</color>")
|
||||
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Height | RMB: Look | F8/Insert: Close | F7: FaceCam</color>")
|
||||
end
|
||||
if Input.GetKeyDown(KeyCode.Minus) or Input.GetKeyDown(KeyCode.KeypadMinus) then
|
||||
_G.__FREECAM_SPEED = math.max(0.5, _G.__FREECAM_SPEED / 1.5)
|
||||
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Up/Down | RMB: Look | F8/Insert: Close</color>")
|
||||
show_ui("<color=#00FF88><b>[FreeCam: ON]</b> Speed: " .. string.format("%.1f", _G.__FREECAM_SPEED) .. " | Arrows: Fly | PgUp/PgDn: Height | RMB: Look | F8/Insert: Close | F7: FaceCam</color>")
|
||||
end
|
||||
|
||||
-- Raw Unity mouse input remains available to the camera while
|
||||
-- InControl battle clicks are gated separately below.
|
||||
local cameraAdjusted = false
|
||||
-- RMB is held; our InControl mouse gate blocks gameplay clicks.
|
||||
if Input.GetMouseButton(1) then
|
||||
local rotX = Input.GetAxis("Mouse X") or 0
|
||||
local rotY = Input.GetAxis("Mouse Y") or 0
|
||||
cameraAdjusted = rotX ~= 0 or rotY ~= 0
|
||||
_G.__FC_EULER = Vector3(_G.__FC_EULER.x - rotY * 2.5,
|
||||
_G.__FC_EULER.y + rotX * 2.5, 0)
|
||||
local mouseX = Input.GetAxis("Mouse X") or 0
|
||||
local mouseY = Input.GetAxis("Mouse Y") or 0
|
||||
local rotSpeed = 3.0
|
||||
local euler = _G.__FC_EULER or cam.transform.eulerAngles
|
||||
local newPitch = euler.x - mouseY * rotSpeed
|
||||
if newPitch > 180 then newPitch = newPitch - 360 end
|
||||
newPitch = math.max(-89.0, math.min(89.0, newPitch))
|
||||
local newYaw = euler.y + mouseX * rotSpeed
|
||||
_G.__FC_EULER = Vector3(newPitch, newYaw, 0)
|
||||
cameraAdjusted = true
|
||||
end
|
||||
|
||||
-- Compute 3D direction vectors from camera Euler angles
|
||||
local radY = _G.__FC_EULER.y * (math.pi / 180.0)
|
||||
local radX = _G.__FC_EULER.x * (math.pi / 180.0)
|
||||
local cosX = math.cos(radX)
|
||||
|
||||
local fwd = Vector3(math.sin(radY) * cosX, -math.sin(radX), math.cos(radY) * cosX)
|
||||
local curEuler = _G.__FC_EULER or cam.transform.eulerAngles
|
||||
local radY = curEuler.y * math.pi / 180.0
|
||||
local radX = curEuler.x * math.pi / 180.0
|
||||
local fwd = Vector3(math.sin(radY) * math.cos(radX), -math.sin(radX), math.cos(radY) * math.cos(radX))
|
||||
local right = Vector3(math.cos(radY), 0, -math.sin(radY))
|
||||
|
||||
local move = Vector3(0, 0, 0)
|
||||
@@ -736,6 +795,10 @@ function _G.__FREECAM_TICK()
|
||||
end
|
||||
end
|
||||
end)
|
||||
if not ok and _G.__FC_LAST_TICK_ERROR ~= tostring(tickErr) then
|
||||
_G.__FC_LAST_TICK_ERROR = tostring(tickErr)
|
||||
CS.UnityEngine.Debug.LogError("[GaC FreeCam Error] " .. tostring(tickErr))
|
||||
end
|
||||
end
|
||||
|
||||
-- Multiple hooks for complete Unity & xLua render pipeline coverage
|
||||
|
||||
Reference in new issue
Block a user