На основе сравнения трех скриптов (C1LineagePawn.uc, 196LineagePawn.uc и hfLineagePawn.uc), а также типичных проблем с Pawn в Unreal Engine 2 (UE2) из результатов поиска по форумам и документации (например, проблемы с видимостью SkeletalMesh, анимациями, которые не обновляются, если mesh скрыт или не в поле зрения, конфликты с bounds для больших моделей вроде Valakas, и отключение анимаций для производительности), я выявил, чего именно не хватает в hfLineagePawn.uc. Это объясняет, почему в C1 и HF mesh не движется/не отображается (анимации "замерзают" или mesh invisible), а в 196 работает нормально (как в стандартном Pawn из UE2 "из коробки").
Ключевые различия скриптов, влияющие на проблему
Все скрипты очень похожи (>90% кода идентичны), но вот критичные отличия, которые вызывают проблему в HF (и C1), но не в 196:
- Инициализация теней (ShadowProjector):
- В HF и C1: Тени спавнятся напрямую в PostBeginPlay (если bActorShadows=True):
text
<span><span>if ( bActorShadows ) // 0x0000004A : 0x0061</span></span><br><span><span>{</span></span><br><span><span> Shadow1 = Spawn(Class'ShadowProjector',self,'None',Location); // 0x00000051 : 0x006A</span></span><br><span><span> Shadow1.ShadowActor = self;</span></span><br><span><span> Shadow1.LightDirection = Normal(vect(1.00,1.00,5.00));</span></span><br><span><span> Shadow1.LightDistance = 380.0 * 4;</span></span><br><span><span> Shadow1.MaxTraceDistance = 380 * 4;</span></span><br><span><span> Shadow1.InitShadow();</span></span><br><span><span> Shadow1.UpdateShadow();</span></span><br><span><span>}</span></span>
Это может вызывать конфликт с рендерингом SkeletalMesh в UE2, особенно для больших моделей (как ваш Valakas с BoundRadius=511). ShadowProjector "прикрепляется" слишком рано, до полной инициализации mesh/анимаций, что приводит к тому, что mesh "замерзает" или становится невидимым (UE2 bug: проекторы теней иногда маскируют mesh, если спавн до SetDrawType или visibility check). Из поиска: Похожие проблемы в форумах UE, где ранний спавн проекторов/эффектов делает mesh invisible во время анимаций (см. и ).
- В 196: Нет спавна теней в PostBeginPlay. Вместо этого отдельные функции AttachShadowProjector (для спавна) и DestroyShadowProjector (для очистки), которые вызываются по необходимости (например, в Destroyed вызывается DestroyShadowProjector). AttachShadowProjector не вызвана автоматически в PostBeginPlay, так что теней по умолчанию нет — и mesh работает без конфликта. Это делает 196 ближе к "стандартному" Pawn UE2, где лишние эффекты не спавнятся рано.
Что не хватает в HF: Отдельных функций для отложенного спавна теней (как AttachShadowProjector в 196). Прямой спавн в PostBeginPlay — это ошибка, которая "ломает" рендеринг mesh/анимаций. Решение: Удалить спавн теней из PostBeginPlay или добавить вызов AttachShadowProjector позже (например, после PlayAnim).
- Опечатки в defaultproperties:
- В HF и C1: Опечатки в именах свойств:
text
<span><span>CanBeIngnoredCollision=True // Опечатка: должно быть CanBeIgnoredCollision</span></span><br><span><span>CanIngnoreCollision=True // Опечатка: CanIgnoreCollision</span></span><br><span><span>KayboardRotationRate=(Pitch=0,Yaw=10000,Roll=0) // Опечатка: KeyboardRotationRate</span></span>
Эти кастомные свойства (вероятно, для игнора коллизии и ротации от клавиатуры) не работают из-за опечаток — UE2 не распознает их, что может привести к ошибкам коллизии (pawn "застревает" и не обновляет анимации) или ротации (mesh не поворачивается, выглядит "замерзшим").
- В 196: Правильные имена:
text
<span><span>CanBeIgnoredCollision=True</span></span><br><span><span>CanIgnoreCollision=True</span></span>
Нет Kayboard — возможно, не нужно, но отсутствие опечаток делает скрипт стабильным.
Что не хватает в HF: Правильных имен свойств. Опечатки вызывают silent errors, из-за чего pawn может игнорировать обновления (анимации не играют, mesh не движется). Из поиска: Похожие проблемы с коллизией приводят к невидимому mesh (см. и ).
- Инициализация анимаций и GetCurWaitAnimName:
- В HF: В GetCurWaitAnimName добавлены проверки на CurWeaponType для swim-анимаций (например, SwimWaitAnimName[CurWeaponType]):
text
<span><span>if ( (Physics == 3) && (SwimWaitAnimName[CurWeaponType] != 'None') )</span></span>
Это может вызывать ошибку, если CurWeaponType не инициализирован (index out of bounds), что "замораживает" анимации.
- В 196: Простая проверка без CurWeaponType (GetSwimWaitAnimName()).
Что не хватает в HF: Защиты от ошибок в индексах массивов (CurWeaponType может быть undefined, вызывая crash анимаций). Из поиска: Анимации не играют, если mesh не visible или ошибки в AnimBP (аналогично в UE2, см. и ).
- Общие проблемы из поиска (применимо к HF/C1):
- Анимации не обновляются, если bPhysicsAnimUpdate=False в T3D (конфликт со скриптом, где True) — mesh "замерзает" (см. , ).
- Для больших mesh (Valakas): Включить "Use Parent Bounds" в свойствах mesh, иначе невидим за пределами view (см. ).
- Mesh невидим, если actor hidden (bHidden=True) или не в game view (см. , ).
- Pawn не possessed (нет Controller) — анимации не стартуют (см. для AI).
Что добавить/исправить в hfLineagePawn.uc, чтобы работал как в 196
Вот модифицированный код для hfLineagePawn.uc (добавьте/измените разделы). Это сделает его похожим на 196: Отдельное управление тенями, исправленные опечатки, защита анимаций. Скомпилируйте (ucc make) и протестируйте с вашим T3D (измените Class=LineagePawn на ваш, если нужно).
unrealscript
//================================================================================// LineagePawn (исправленная версия hf для видимости/анимаций mesh).//================================================================================class LineagePawn extends Pawn NoNativeReplication Config(User);// ... (Остальной код без изменений, включая функции SetAnimAction, GetDebugName и т.д.)// Добавьте эти функции из 196 для теней (чтобы избежать конфликта в PostBeginPlay)simulated event AttachShadowProjector(){ if (Shadow1 == None) { Shadow1 = Spawn(Class'ShadowProjector',self,'None',Location); Shadow1.ShadowActor = self; Shadow1.LightDirection = Normal(vect(1.00,1.00,5.00)); Shadow1.LightDistance = 380.0 * 4; Shadow1.MaxTraceDistance = 380 * 4; Shadow1.InitShadow(); Shadow1.UpdateShadow(); }}simulated event DestroyShadowProjector(){ if (Shadow1 != None) { Shadow1.DetachProjector(True); Shadow1.NDestroy(); Shadow1 = None; }}simulated event Destroyed(){ DestroyShadowProjector(); // Вызов вместо прямого Destroy if (Mark != None) { Mark.DetachProjector(True); Mark.NDestroy(); } Super.Destroyed();}// В PostBeginPlay: Удалите спавн Shadow (чтобы избежать конфликта с mesh)function PostBeginPlay(){ Super.PostBeginPlay(); TurnLeftAnim = GetWalkAnimName(); TurnRightAnim = GetWalkAnimName(); MovementAnims[0] = GetCurMoveAnimName(); MovementAnims[1] = GetCurMoveAnimName(); MovementAnims[2] = GetCurMoveAnimName(); MovementAnims[3] = GetCurMoveAnimName(); PlayAnim(GetCurWaitAnimName()); // Добавьте явную инициализацию mesh/анимаций (для фикса видимости) SetDrawType(DT_Mesh); // Явно установить тип рендеринга bHidden = False; // Явно visible // Если нужны тени — вызовите отложено (не в PostBeginPlay) if (bActorShadows) { SetTimer(0.1, False); // Отложить спавн теней на 0.1 сек }}// Добавьте Timer для отложенного спавна теней (фикс конфликта)simulated event Timer(){ AttachShadowProjector(); Super.Timer(); // Если есть в родителе}// В GetCurWaitAnimName: Добавьте защиту от ошибок CurWeaponTypefunction name GetCurWaitAnimName(){ if (Controller != None) { // ... (остальной код) if (Controller.bAutoAttacking) { if ((Physics == 3) && (CurWeaponType < ArrayCount(SwimAttackWaitAnimName)) && (SwimAttackWaitAnimName[CurWeaponType] != 'None')) return SwimAttackWaitAnimName[CurWeaponType]; // Защита от out-of-bounds else return GetAtkWaitAnimName(); } // Аналогично для SwimWaitAnimName } return GetWaitAnimName();}// Исправьте опечатки в defaultpropertiesdefaultproperties{ CanBeIgnoredCollision=True // Исправлено CanIgnoreCollision=True // Исправлено DamageEffect=Class'LineageEffect.p_u002_a' AirSpeed=120.00 bPhysicsAnimUpdate=True BaseMovementRate=120.00 bActorShadows=True bUnlitCheck=True RotationRate=(Pitch=0,Yaw=65000,Roll=0) KeyboardRotationRate=(Pitch=0,Yaw=10000,Roll=0) // Исправлено // ... (остальные свойства)}
Шаги по тестированию
- Скомпилируйте: ucc make в System-папке.
- Обновите T3D: Добавьте bHidden=False, bPhysicsAnimUpdate=True (чтобы совпадало со скриптом), и поднимите Z в Location (как я предлагал раньше, чтобы не под землей).
- В UnrealEd: Импортируйте T3D, выберите Pawn -> Properties -> Rendering -> Visible=True, Use Parent Bounds=True (для большого mesh).
- Тест: Play Map. Если mesh visible, но не движется — вызовите анимацию через консоль (~): causeevent ChangeAnimation.
- Если тени нужны: Они спавнятся отложено через Timer — это фиксит конфликт.