Fix actor hierarchy initialization when it gets modified by a script on the fly

#2940 #2623 #2751
This commit is contained in:
Wojtek Figat
2025-02-25 15:32:20 +01:00
parent 1c3d1b623d
commit ca6544204b
2 changed files with 37 additions and 0 deletions

View File

@@ -37,6 +37,10 @@
#define ACTOR_ORIENTATION_EPSILON 0.000000001f #define ACTOR_ORIENTATION_EPSILON 0.000000001f
// Start loop over actor children/scripts from the beginning to account for any newly added or removed actors.
#define ACTOR_LOOP_START_MODIFIED_HIERARCHY() _isHierarchyDirty = false
#define ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY() if (_isHierarchyDirty) { _isHierarchyDirty = false; i = -1; }
namespace namespace
{ {
Actor* GetChildByPrefabObjectId(Actor* a, const Guid& prefabObjectId) Actor* GetChildByPrefabObjectId(Actor* a, const Guid& prefabObjectId)
@@ -74,6 +78,7 @@ Actor::Actor(const SpawnParams& params)
, _isActiveInHierarchy(true) , _isActiveInHierarchy(true)
, _isPrefabRoot(false) , _isPrefabRoot(false)
, _isEnabled(false) , _isEnabled(false)
, _isHierarchyDirty(false)
, _layer(0) , _layer(0)
, _staticFlags(StaticFlags::FullyStatic) , _staticFlags(StaticFlags::FullyStatic)
, _localTransform(Transform::Identity) , _localTransform(Transform::Identity)
@@ -109,9 +114,11 @@ void Actor::OnEnableInHierarchy()
{ {
OnEnable(); OnEnable();
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
for (int32 i = 0; i < Children.Count(); i++) for (int32 i = 0; i < Children.Count(); i++)
{ {
Children.Get()[i]->OnEnableInHierarchy(); Children.Get()[i]->OnEnableInHierarchy();
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
} }
} }
} }
@@ -120,9 +127,11 @@ void Actor::OnDisableInHierarchy()
{ {
if (IsActiveInHierarchy() && GetScene() && _isEnabled) if (IsActiveInHierarchy() && GetScene() && _isEnabled)
{ {
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
for (int32 i = 0; i < Children.Count(); i++) for (int32 i = 0; i < Children.Count(); i++)
{ {
Children.Get()[i]->OnDisableInHierarchy(); Children.Get()[i]->OnDisableInHierarchy();
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
} }
OnDisable(); OnDisable();
@@ -165,6 +174,7 @@ void Actor::OnDeleteObject()
{ {
// Unlink from the parent // Unlink from the parent
_parent->Children.RemoveKeepOrder(this); _parent->Children.RemoveKeepOrder(this);
_parent->_isHierarchyDirty = true;
_parent = nullptr; _parent = nullptr;
_scene = nullptr; _scene = nullptr;
} }
@@ -173,6 +183,7 @@ void Actor::OnDeleteObject()
{ {
// Unlink from the parent // Unlink from the parent
_parent->Children.RemoveKeepOrder(this); _parent->Children.RemoveKeepOrder(this);
_parent->_isHierarchyDirty = true;
_parent = nullptr; _parent = nullptr;
_scene = nullptr; _scene = nullptr;
} }
@@ -289,6 +300,7 @@ void Actor::SetParent(Actor* value, bool worldPositionsStays, bool canBreakPrefa
if (_parent) if (_parent)
{ {
_parent->Children.RemoveKeepOrder(this); _parent->Children.RemoveKeepOrder(this);
_parent->_isHierarchyDirty = true;
} }
// Set value // Set value
@@ -298,6 +310,7 @@ void Actor::SetParent(Actor* value, bool worldPositionsStays, bool canBreakPrefa
if (_parent) if (_parent)
{ {
_parent->Children.Add(this); _parent->Children.Add(this);
_parent->_isHierarchyDirty = true;
} }
// Sync scene change if need to // Sync scene change if need to
@@ -388,6 +401,7 @@ void Actor::SetOrderInParent(int32 index)
// Change order // Change order
parentChildren.Insert(index, this); parentChildren.Insert(index, this);
} }
_parent->_isHierarchyDirty = true;
// Fire event // Fire event
OnOrderInParentChanged(); OnOrderInParentChanged();
@@ -912,11 +926,15 @@ void Actor::BeginPlay(SceneBeginData* data)
OnBeginPlay(); OnBeginPlay();
// Update scripts // Update scripts
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
for (int32 i = 0; i < Scripts.Count(); i++) for (int32 i = 0; i < Scripts.Count(); i++)
{ {
auto e = Scripts.Get()[i]; auto e = Scripts.Get()[i];
if (!e->IsDuringPlay()) if (!e->IsDuringPlay())
{
e->BeginPlay(data); e->BeginPlay(data);
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
}
} }
// Update children // Update children
@@ -924,7 +942,10 @@ void Actor::BeginPlay(SceneBeginData* data)
{ {
auto e = Children.Get()[i]; auto e = Children.Get()[i];
if (!e->IsDuringPlay()) if (!e->IsDuringPlay())
{
e->BeginPlay(data); e->BeginPlay(data);
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
}
} }
// Fire events for scripting // Fire events for scripting
@@ -963,19 +984,27 @@ void Actor::EndPlay()
Flags &= ~ObjectFlags::IsDuringPlay; Flags &= ~ObjectFlags::IsDuringPlay;
// Call event deeper // Call event deeper
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
for (int32 i = 0; i < Children.Count(); i++) for (int32 i = 0; i < Children.Count(); i++)
{ {
auto e = Children.Get()[i]; auto e = Children.Get()[i];
if (e->IsDuringPlay()) if (e->IsDuringPlay())
{
e->EndPlay(); e->EndPlay();
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
}
} }
// Inform attached scripts // Inform attached scripts
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
for (int32 i = 0; i < Scripts.Count(); i++) for (int32 i = 0; i < Scripts.Count(); i++)
{ {
auto e = Scripts.Get()[i]; auto e = Scripts.Get()[i];
if (e->IsDuringPlay()) if (e->IsDuringPlay())
{
e->EndPlay(); e->EndPlay();
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
}
} }
// Cleanup managed object // Cleanup managed object
@@ -1177,18 +1206,25 @@ void Actor::OnEnable()
CHECK_DEBUG(!_isEnabled); CHECK_DEBUG(!_isEnabled);
_isEnabled = true; _isEnabled = true;
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
for (int32 i = 0; i < Scripts.Count(); i++) for (int32 i = 0; i < Scripts.Count(); i++)
{ {
auto script = Scripts[i]; auto script = Scripts[i];
if (script->GetEnabled() && !script->_wasStartCalled) if (script->GetEnabled() && !script->_wasStartCalled)
{
script->Start(); script->Start();
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
}
} }
for (int32 i = 0; i < Scripts.Count(); i++) for (int32 i = 0; i < Scripts.Count(); i++)
{ {
auto script = Scripts[i]; auto script = Scripts[i];
if (script->GetEnabled() && !script->_wasEnableCalled) if (script->GetEnabled() && !script->_wasEnableCalled)
{
script->Enable(); script->Enable();
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
}
} }
} }

View File

@@ -37,6 +37,7 @@ protected:
uint16 _isActiveInHierarchy : 1; uint16 _isActiveInHierarchy : 1;
uint16 _isPrefabRoot : 1; uint16 _isPrefabRoot : 1;
uint16 _isEnabled : 1; uint16 _isEnabled : 1;
uint16 _isHierarchyDirty : 1;
uint16 _drawNoCulling : 1; uint16 _drawNoCulling : 1;
uint16 _drawCategory : 4; uint16 _drawCategory : 4;
byte _layer; byte _layer;