Tutorial: How to use UPhysicsConstraintComponent inside OnConstruction(FTransform) override function
I wanted to try contributing to the wiki with a tutorial. I noticed that had already made an article here in regards to Physics Constraint Components and creating them dynamically. All I wanted to do is to just extend that article, so it also touches on ObjectInitializer, why we shouldnāt initialize physics constraint components in OnConstruction(Transform) function (which I really donāt know), and other ways to dynamically create physics constraint components.
If I editās article, it will easily get reverted because I do not have a polished code at the moment. Therefore, I will be posting my extension here, and hopefully someone could give me tips. Feedback is welcomed.
[HR][/HR]
I wanted to be able to spawn actors and constrain the actors in a given space like a chain or a line from my base class to the end. I know that to spawn actors, I need to call on SpawnActor() any function that is not going to be called in the constructor. However, I am having trouble with the UPhysicsConstraintComponent where it would fail to be created when I call on CreateDefaultSubobject() in the OnConstruction(Transform) override function of my base class. The only thing I know that will allow me to set UPhysicsConstraintComponent successfully is by initializing it in the constructor.
I did another alternate method where I would store three TArrays in the base class, with each of them representing the subclass, physics constraint component, and the constraint names. The following code is placed in the base classā header file. I use APawn because, frankly, everything is in APawn.
UCLASS()
class TUTORIAL_API ABaseClass : APawn
{
GENERATED_BODY()
:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=CustomProperties) TArray<ASubClass*> SubClassArray;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=CustomProperties) TArray<UPhysicsConstraintComponent*> PhysicsConstraintArray;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=CustomProperties) TArray<FName> ConstraintNameArray;
...
...
}
Then to initialize the arrays, first you need to create the following components for the PhysicsConstraintArray and the ConstraintNameArray.
ABaseClass::ABaseClass(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
for (int32 i = 0; i < 4; i++){
//Names
FString Name = "Frame_" + FString::FromInt(i);
->ConstraintNameArray.Push(FName(*Name));
//Physics Constraints
UPhysicsConstraintComponent* PhysicsConstraint = ObjectInitializer.CreateDefaultSubobject<UPhysicsConstraintComponent>(, ->ConstraintNameArray*);
PhysicsConstraint->SetWorldLocation(->GetActorLocation());
->PhysicsConstraintArray.Push(PhysicsConstraint);
}
...
...
}
Then in the OnConstruction(Transform) override function, you would then start initializing the subclass array. Note that the ConstraintNameArray.Num() is then used as an array size counter, so you donāt have to define constants here and there.
void ABaseClass::OnConstruction(const FTransform& Transform){
Super::OnConstruction(Transform);
//First loop needs initial values passed in, so successive calls can then be swapped for last initialized actors.
FVector Height(0.0f, 0.0f, 100.0f); //How high from the initial world origin. Since the default floor mesh in a blank level is 80 UE4 units high, I set it to 100.
FActorSpawnParameters Params;
Params.Owner = ; //Setting the owner as the base class. When spawning actors, actors will have the owner as their parents.
USceneComponent* Scene = ->GetRootComponent();
UPrimitiveComponent* Primitive = ->BoxCollider;
//Initializing Subclass array.
for (int32 i = 0; i < ConstraintNameArray.Num(); i++){
ASubClass* Class = ->GetWorld()->SpawnActor<ASubClass>(ASubClass::StaticClass(), Height, ->GetActorRotation(), Params);
Class->BoxCollider->AttachTo(Params.Owner->GetRootComponent());
//Set whatever constraining limits you need here
UPhysicsConstraintComponent* Physics = ->PhysicsConstraintArray*;
Physics->AttachTo(Scene, NAME_None, EAttachLocation::KeepWorldPosition);
Physics->SetConstrainedComponents(Primitive, NAME_None, Class->BoxCollider, NAME_None);
Physics->SetAngularSwing1Limit(EAngularConstraintMotion::ACM_Locked, 0.0f);
Physics->SetAngularSwing2Limit(EAngularConstraintMotion::ACM_Locked, 0.0f);
Physics->SetAngularTwistLimit(EAngularConstraintMotion::ACM_Locked, 0.0f);
//Prepare for next iteration.
Params = {};
Params.Owner = Class;
Scene = Class->GetRootComponent();
Primitive = Class->BoxCollider;
}
...
...
}
That is all.