void USimpleConstructionScript::ExecuteScriptOnActor(AActor* Actor, const FTransform& RootTransform, bool bIsDefaultTransform) { if(RootNodes.Num() > 0) { TSet<UActorComponent*> AllComponentsCreatedBySCS; TInlineComponentArray<UActorComponent*> InstancedComponents; for(auto NodeIt = RootNodes.CreateIterator(); NodeIt; ++NodeIt) { USCS_Node* RootNode = *NodeIt; if(RootNode != nullptr) { // Get all native scene components TInlineComponentArray<USceneComponent*> Components; Actor->GetComponents(Components); for (int32 Index = Components.Num()-1; Index >= 0; --Index) { USceneComponent* SceneComponent = Components[Index]; if (SceneComponent->CreationMethod == EComponentCreationMethod::Instance) { Components.RemoveAt(Index); } else { // Handle the native sub-component of an instance component case USceneComponent* ParentSceneComponent = SceneComponent->GetTypedOuter<USceneComponent>(); if (ParentSceneComponent && ParentSceneComponent->CreationMethod == EComponentCreationMethod::Instance) { Components.RemoveAt(Index); } } } // Get the native root component; if it's not set, the first native scene component will be used as root. This matches what's done in the SCS editor. USceneComponent* RootComponent = Actor->GetRootComponent(); if(RootComponent == nullptr && Components.Num() > 0) { RootComponent = Components[0]; } // If the root node specifies that it has a parent USceneComponent* ParentComponent = nullptr; if(RootNode->ParentComponentOrVariableName != NAME_None) { // Get the Actor class object UClass* ActorClass = Actor->GetClass(); check(ActorClass != nullptr); // If the root node is parented to a "native" component (i.e. in the 'Components' array) if(RootNode->bIsParentComponentNative) { for(int32 CompIndex = 0; CompIndex < Components.Num(); ++CompIndex) { // If we found a match, remember the index if(Components[CompIndex]->GetFName() == RootNode->ParentComponentOrVariableName) { ParentComponent = Components[CompIndex]; break; } } } else { // In the non-native case, the SCS node's variable name property is used as the parent identifier UObjectPropertyBase* Property = FindField<UObjectPropertyBase>(ActorClass, RootNode->ParentComponentOrVariableName); if(Property != nullptr) { // If we found a matching property, grab its value and use that as the parent for this node ParentComponent = Cast<USceneComponent>(Property->GetObjectPropertyValue_InContainer(Actor)); } } } // Create the new component instance and any child components it may have UActorComponent* InstancedComponent = RootNode->ExecuteNodeOnActor(Actor, ParentComponent != nullptr ? ParentComponent : RootComponent, &RootTransform, bIsDefaultTransform); if(InstancedComponent != nullptr) { InstancedComponents.Add(InstancedComponent); } // get list of every component SCS created, in case some of them aren't in the attachment hierarchy any more (e.g. rigid bodies) TInlineComponentArray<USceneComponent*> ComponentsAfterSCS; Actor->GetComponents(ComponentsAfterSCS); for (USceneComponent* C : ComponentsAfterSCS) { if (Components.Contains(C) == false) { AllComponentsCreatedBySCS.Add(C); } } } } // Register all instanced SCS components once SCS execution has finished; sorted in order to register the scene component hierarchy first, followed by the remaining actor components (in case they happen to depend on something in the scene hierarchy) InstancedComponents.Sort([](const UActorComponent& A, const UActorComponent& B) { return A.IsA<USceneComponent>(); }); for(auto InstancedComponent : InstancedComponents) { RegisterInstancedComponent(InstancedComponent); } // now that the instanced components in the attachment hierarchy are registered, register any other components that SCS made but aren't in the attachment hierarchy for whatever reason. for (auto C : AllComponentsCreatedBySCS) { if (C->IsRegistered() == false) { C->RegisterComponent(); } } } else if(Actor->GetRootComponent() == NULL) // Must have a root component at the end of SCS, so if we don't have one already (from base class), create a SceneComponent now { USceneComponent* SceneComp = NewObject<USceneComponent>(Actor); SceneComp->SetFlags(RF_Transactional); SceneComp->CreationMethod = EComponentCreationMethod::SimpleConstructionScript; SceneComp->SetWorldTransform(RootTransform); Actor->SetRootComponent(SceneComp); SceneComp->RegisterComponent(); } }
UActorComponent* USCS_Node::ExecuteNodeOnActor(AActor* Actor, USceneComponent* ParentComponent, const FTransform* RootTransform, bool bIsDefaultTransform) { check(Actor != nullptr); check((ParentComponent != nullptr && !ParentComponent->IsPendingKill()) || (RootTransform != nullptr)); // must specify either a parent component or a world transform auto ActualBPGC = Cast<UBlueprintGeneratedClass>(Actor->GetClass()); UActorComponent* ActualComponentTemplate = GetActualComponentTemplate(ActualBPGC); // Create a new component instance based on the template UActorComponent* NewActorComp = Actor->CreateComponentFromTemplate(ActualComponentTemplate, VariableName.ToString()); if(NewActorComp != nullptr) { NewActorComp->CreationMethod = EComponentCreationMethod::SimpleConstructionScript; // SCS created components are net addressable NewActorComp->SetNetAddressable(); // Special handling for scene components USceneComponent* NewSceneComp = Cast<USceneComponent>(NewActorComp); if (NewSceneComp != nullptr) { // If NULL is passed in, we are the root, so set transform and assign as RootComponent on Actor if (ParentComponent == nullptr || (ParentComponent && ParentComponent->IsPendingKill())) { FTransform WorldTransform = *RootTransform; if(bIsDefaultTransform) { // Note: We use the scale vector from the component template when spawning (to match what happens with a native root) WorldTransform.SetScale3D(NewSceneComp->RelativeScale3D); } NewSceneComp->SetWorldTransform(WorldTransform); Actor->SetRootComponent(NewSceneComp); } // Otherwise, attach to parent component passed in else { NewSceneComp->AttachTo(ParentComponent, AttachToName); } } // Call function to notify component it has been created NewActorComp->OnComponentCreated(); if (NewActorComp->GetIsReplicated()) { // Make sure this component is added to owning actor's replicated list. NewActorComp->SetIsReplicated(true); } // If we want to save this to a property, do it here FName VarName = GetVariableName(); if (VarName != NAME_None) { UClass* ActorClass = Actor->GetClass(); if (UObjectPropertyBase* Prop = FindField<UObjectPropertyBase>(ActorClass, VarName)) { Prop->SetObjectPropertyValue_InContainer(Actor, NewActorComp); } else { UE_LOG(LogBlueprint, Log, TEXT("ExecuteNodeOnActor: Couldn't find property '%s' on '%s"), *VarName.ToString(), *Actor->GetName()); #if WITH_EDITOR // If we're constructing editable components in the SCS editor, set the component instance corresponding to this node for editing purposes USimpleConstructionScript* SCS = GetSCS(); if(SCS != nullptr && (SCS->IsConstructingEditorComponents() || SCS->GetComponentEditorActorInstance() == Actor)) { EditorComponentInstance = NewSceneComp; } #endif } } // Determine the parent component for our children (it's still our parent if we're a non-scene component) USceneComponent* ParentSceneComponentOfChildren = (NewSceneComp != nullptr) ? NewSceneComp : ParentComponent; // If we made a component, go ahead and process our children for (int32 NodeIdx = 0; NodeIdx < ChildNodes.Num(); NodeIdx++) { USCS_Node* Node = ChildNodes[NodeIdx]; check(Node != nullptr); Node->ExecuteNodeOnActor(Actor, ParentSceneComponentOfChildren, nullptr, false); } } return NewActorComp; }