WL #949 마법책(9009) PD 지시 4건 — ① 책이 떠 있는 동안 손 무기(셉터) 렌더러 숨김(WLFloatingBook hideHandWeapon · 폴링마다 재적용 · 직업 표 수정 행 방식은 Set_Obj 재부착이 로비 컨트롤러를 끼워 폐기) ② 마지막 타(attack2) = Mage@RangeAttack2 f16~46(오른손 내지른 채 약 0.5 s 고정 · 발사 0.35 · 재굽기 → pcanim_summoner) ③ 책 추적: 평소 = 카메라 기준 캐릭터 뒤(화면 위 · 먼쪽 0.45 · 옆 0.35 · 높이 0.72) · 시전 중(클립 Cast1/Cast2/BookCharge/BookCast) = PC 정면 0.5 m(cameraSpace · castStates · cast* 신설) ④ 스킬 시전 NRE(Actor.Use_Skill:756 ServerInfo 없음 · 테스트 씬) 가드 1줄
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
10c42cc28a
commit
4f262b3b8e
|
|
@ -694,6 +694,49 @@ ModelImporter:
|
|||
maskType: 0
|
||||
maskSource: {instanceID: 0}
|
||||
additiveReferencePoseFrame: 0
|
||||
- serializedVersion: 16
|
||||
name: Cast2_S_10303
|
||||
takeName: Take 001
|
||||
internalID: -998496362607748373
|
||||
firstFrame: 16
|
||||
lastFrame: 46
|
||||
wrapMode: 0
|
||||
orientationOffsetY: 0
|
||||
level: 0
|
||||
cycleOffset: 0
|
||||
loop: 0
|
||||
hasAdditiveReferencePose: 0
|
||||
loopTime: 0
|
||||
loopBlend: 0
|
||||
loopBlendOrientation: 1
|
||||
loopBlendPositionY: 1
|
||||
loopBlendPositionXZ: 0
|
||||
keepOriginalOrientation: 1
|
||||
keepOriginalPositionY: 1
|
||||
keepOriginalPositionXZ: 1
|
||||
heightFromFeet: 0
|
||||
mirror: 0
|
||||
bodyMask: 01000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000
|
||||
curves: []
|
||||
events:
|
||||
- time: 0
|
||||
functionName: LookAtTarget
|
||||
data:
|
||||
objectReferenceParameter: {instanceID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
- time: 0.35
|
||||
functionName: Projectile
|
||||
data: Projectile_10303
|
||||
objectReferenceParameter: {instanceID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
transformMask: []
|
||||
maskType: 0
|
||||
maskSource: {instanceID: 0}
|
||||
additiveReferencePoseFrame: 0
|
||||
isReadable: 1
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
|
|
|
|||
|
|
@ -2349,7 +2349,7 @@ AnimatorState:
|
|||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 6641085478485924818, guid: e0919932c9c179142ad06cf4ee11f204,
|
||||
m_Motion: {fileID: -998496362607748373, guid: 7a1612ec6fd9fbf4b917575b7c1ecdcb,
|
||||
type: 3}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
|
|
|
|||
|
|
@ -753,7 +753,7 @@ public class Actor : MyCoroutine
|
|||
if (!testskill) m_Stat.Set_Stat(false, eStat.MP, skilldata.n_MP);
|
||||
else m_SkillData.SetSkillExtraType = true;
|
||||
Set_MagicCoolTime(skilldata.Get_CoolTime(m_Stat.Get_Stat(eStat.COOLTIME_Multiplier)), index);
|
||||
if (IsMainPC() && !testskill) ServerInfo.Ins.m_ServerData.Add_MissionCount(ePurpose.USE_SKILL_ALL);
|
||||
if (IsMainPC() && !testskill && !DSUtil.CheckNull(ServerInfo.Ins) && ServerInfo.Ins.m_ServerData != null) ServerInfo.Ins.m_ServerData.Add_MissionCount(ePurpose.USE_SKILL_ALL); // WL #949 — 스킬 테스트 씬(서버 객체 없음)에서 NRE 로 스킬이 안 나가던 것(실측 Actor.cs:756) · 서버 있으면 원본 그대로
|
||||
return Play_Skill(skilldata.s_Animation);
|
||||
}
|
||||
public virtual void Shoot_Skill()
|
||||
|
|
|
|||
|
|
@ -1153,26 +1153,29 @@ MonoBehaviour:
|
|||
intParameter: 0
|
||||
floatParameter: 0
|
||||
- stateName: attack2
|
||||
sourceFbxPath: Assets/ExplosiveLLC/Mage Warrior Mecanim Animation Pack/Animations/Mage@Attack3_S.FBX
|
||||
sourceFbxPath: Assets/ExplosiveLLC/Mage Warrior Mecanim Animation Pack/Animations/Mage@RangeAttack2.FBX
|
||||
subClipName: Cast2_S_{CID}
|
||||
sourceClipName:
|
||||
sourceClipName:
|
||||
incomingExitTime: 0.854
|
||||
createIfMissing: 0
|
||||
firstFrame: -1
|
||||
lastFrame: -1
|
||||
speed: 0
|
||||
firstFrame: 16
|
||||
lastFrame: 46
|
||||
speed: 1
|
||||
maskPath:
|
||||
bakeRootY: -1
|
||||
mirror: 0
|
||||
events:
|
||||
- functionName: LookAtTarget
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0
|
||||
stringParameter:
|
||||
stringParameter:
|
||||
intParameter: 0
|
||||
floatParameter: 0
|
||||
- functionName: Projectile
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0.407
|
||||
normalizedTime: 0.35
|
||||
stringParameter: Projectile_{CID}
|
||||
intParameter: 0
|
||||
floatParameter: 0
|
||||
|
|
|
|||
|
|
@ -7,9 +7,16 @@
|
|||
"prefab": "WL/Book/WL_Book_Magic",
|
||||
"count": 1,
|
||||
"scale": 0.9,
|
||||
"side": 0.55,
|
||||
"height": 0.68,
|
||||
"forward": 0.05,
|
||||
"side": 0.35,
|
||||
"height": 0.72,
|
||||
"forward": 0.45,
|
||||
"cameraSpace": 1,
|
||||
"castStates": [ "Cast1_S", "Cast2_S", "BookCharge", "BookCast" ],
|
||||
"castSide": 0.25,
|
||||
"castHeight": 0.62,
|
||||
"castForward": 0.5,
|
||||
"hideHandWeapon": 1,
|
||||
"_note_949": "#949 PD 「책이 캐릭터를 가리지 않게 가능한 캐릭터보다 뒤쪽에(추적 로직 수정) · 시전 동작 중엔 책이 캐릭터 모션 앞쪽으로 유지」 — 평소 자리 = 카메라 기준(cameraSpace 1 · forward + = 카메라에서 먼 쪽 = 화면 위 = 캐릭터 뒤 0.45 m · 옆 0.35 · 높이 0.72) · 시전 중(클립 이름 Cast1/Cast2/BookCharge/BookCast) = PC 정면 0.5 m · 옆 0.25 · 높이 0.62 · followLerp 로 오간다. 이전 = PC 오른쪽 0.55 · 앞 0.05(카메라에선 몸 앞을 가림)",
|
||||
"euler": [
|
||||
40.0,
|
||||
0.0,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ namespace WL.Combat.Weapon
|
|||
public float animLoopStartSec, animLoopEndSec;// 책 클립 반복 구간(초 · 끝 ≤ 시작 = 반복 안 함)
|
||||
public int matchPcLayer; // 1 = 책 레이어를 PC 와 같게(같은 카메라 경로로 그려지게)
|
||||
public float pollSec = 0.25f;
|
||||
// #949 PD 「책이 캐릭터를 가리지 않게 가능한 캐릭터보다 뒤쪽에 · 시전 동작 중엔 캐릭터 모션 앞쪽으로 유지」
|
||||
public int cameraSpace; // 1 = 평소 자리(side · forward)를 카메라 yaw 기준으로 — forward + = 카메라에서 먼 쪽(화면 위 · 캐릭터 뒤) · 0 = PC 정면 기준
|
||||
public string[] castStates = new string[0]; // PC 현재 클립 이름에 이 조각이 들면 「시전 중」 → castSide · castHeight · castForward(PC 정면 기준) 로
|
||||
public float castSide, castHeight, castForward;
|
||||
public int hideHandWeapon; // #949 1 = 책이 떠 있는 동안 PC 손 소켓(w_*) 아래 무기 렌더러를 끈다(지팡이·셉터 숨김 · 책이 사라지면 되돌림) — 직업 표(수정 행)로 비우면 Set_Obj 재부착이 로비 컨트롤러를 끼워 실패(실측)
|
||||
public string note = "";
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +103,28 @@ namespace WL.Combat.Weapon
|
|||
for (int i = 0; i < _books.Count; i++) if (_books[i] == null) { missing = true; break; }
|
||||
if (_books.Count > 0 && (!want || missing || !ReferenceEquals(pc, _pc))) Clear(!want ? "조건 해제" : missing ? "씬 정리됨" : "PC 교체");
|
||||
if (want && _books.Count == 0) Spawn(c, pc);
|
||||
if (want && c.hideHandWeapon != 0 && _books.Count > 0) HideHandWeapon(pc); // 매 폴링 — 원본이 무기 모델을 다시 붙여도(Set_Obj) 다시 끈다
|
||||
}
|
||||
|
||||
// #949 손 소켓(w_*) 아래 무기 렌더러 끄기 · 되돌리기
|
||||
readonly List<Renderer> _hiddenWeapon = new List<Renderer>();
|
||||
public static int WeaponHidden;
|
||||
void HideHandWeapon(PCActor pc)
|
||||
{
|
||||
foreach (var t in pc.GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
if (!t.name.StartsWith("w_")) continue;
|
||||
foreach (var r in t.GetComponentsInChildren<Renderer>(true))
|
||||
{
|
||||
if (r == null || !r.enabled || r is ParticleSystemRenderer) continue;
|
||||
r.enabled = false; _hiddenWeapon.Add(r); WeaponHidden++;
|
||||
}
|
||||
}
|
||||
}
|
||||
void RestoreHandWeapon()
|
||||
{
|
||||
for (int i = 0; i < _hiddenWeapon.Count; i++) if (_hiddenWeapon[i] != null) _hiddenWeapon[i].enabled = true;
|
||||
_hiddenWeapon.Clear();
|
||||
}
|
||||
|
||||
/// <summary>장착 무기(FR GroupNo)가 weaponIds 에 들거나, 그 무기의 공격표 class_id 가 classIds 에 드는가.</summary>
|
||||
|
|
@ -139,6 +166,7 @@ namespace WL.Combat.Weapon
|
|||
if (_books.Count == 0) return;
|
||||
for (int i = 0; i < _books.Count; i++) if (_books[i] != null) Destroy(_books[i].gameObject);
|
||||
_books.Clear(); _anims.Clear(); _pc = null;
|
||||
RestoreHandWeapon(); // #949
|
||||
Cleared++; LastLog = "책 정리 — " + why;
|
||||
}
|
||||
|
||||
|
|
@ -166,6 +194,12 @@ namespace WL.Combat.Weapon
|
|||
Quaternion faceYaw = pcYaw;
|
||||
if (c.faceCamera != 0) { var cam = Camera.main; if (cam != null) { var f = cam.transform.forward; f.y = 0f; if (f.sqrMagnitude > 1e-4f) faceYaw = Quaternion.LookRotation(f.normalized, Vector3.up); } }
|
||||
var e = c.euler != null && c.euler.Length >= 3 ? Quaternion.Euler(c.euler[0], c.euler[1], c.euler[2]) : Quaternion.identity;
|
||||
// #949 시전 중(현재 클립 이름에 castStates 조각) = PC 정면 기준 cast 자리 · 평소 = cameraSpace 면 카메라 yaw 기준(캐릭터 뒤 = 화면 위)
|
||||
bool casting = IsCasting(c);
|
||||
Casting = casting;
|
||||
float side = casting ? c.castSide : c.side, height = casting ? c.castHeight : c.height, forward = casting ? c.castForward : c.forward;
|
||||
Quaternion basis = casting || c.cameraSpace == 0 ? pcYaw : (c.faceCamera != 0 ? faceYaw : pcYaw);
|
||||
if (!casting && c.cameraSpace != 0 && c.faceCamera == 0) { var cam = Camera.main; if (cam != null) { var f = cam.transform.forward; f.y = 0f; if (f.sqrMagnitude > 1e-4f) basis = Quaternion.LookRotation(f.normalized, Vector3.up); } }
|
||||
int n = _books.Count;
|
||||
float kp = snap || c.followLerp <= 0f ? 1f : 1f - Mathf.Exp(-c.followLerp * Time.deltaTime);
|
||||
float kr = snap || c.turnLerp <= 0f ? 1f : 1f - Mathf.Exp(-c.turnLerp * Time.deltaTime);
|
||||
|
|
@ -176,13 +210,26 @@ namespace WL.Combat.Weapon
|
|||
var ring = Quaternion.Euler(0f, _orbit + 360f * share, 0f);
|
||||
float bob = Mathf.Sin((t * c.bobHz + share) * Mathf.PI * 2f) * c.bobAmp;
|
||||
float sw = Mathf.Sin((t * c.swayHz + share) * Mathf.PI * 2f);
|
||||
var pos = pt.position + pcYaw * (ring * new Vector3(c.side, 0f, c.forward)) + Vector3.up * (c.height + bob);
|
||||
var pos = pt.position + basis * (ring * new Vector3(side, 0f, forward)) + Vector3.up * (height + bob);
|
||||
var rot = faceYaw * ring * e * Quaternion.Euler(0f, sw * c.swayYawDeg, sw * c.swayRollDeg);
|
||||
if (kp >= 1f && kr >= 1f) b.SetPositionAndRotation(pos, rot);
|
||||
else b.SetPositionAndRotation(Vector3.Lerp(b.position, pos, kp), Quaternion.Slerp(b.rotation, rot, kr));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Casting; // 프로브용 — 마지막 배치가 시전 자리였는가
|
||||
|
||||
/// <summary>#949 PC 애니메이터 0층 현재 클립 이름에 castStates 조각이 들면 시전 중.</summary>
|
||||
bool IsCasting(WLFloatingBookJson c)
|
||||
{
|
||||
if (c.castStates == null || c.castStates.Length == 0 || _pc == null) return false;
|
||||
var an = _pc.m_animation; if (an == null) return false;
|
||||
var ci = an.GetCurrentAnimatorClipInfo(0); string name = ci.Length > 0 && ci[0].clip != null ? ci[0].clip.name : "";
|
||||
if (name.Length == 0) return false;
|
||||
for (int i = 0; i < c.castStates.Length; i++) if (!string.IsNullOrEmpty(c.castStates[i]) && name.Contains(c.castStates[i])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>펼친 뒤 넘김 구간 반복 — 상태 시간이 끝 초를 넘으면 시작 초로 되돌린다.</summary>
|
||||
void LoopAnim(WLFloatingBookJson c)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue