Here’s this enemys hierarchy, it inherits from Pawn, the mesh component is simulating physics and being moved around by the movement component with forces. The skeletal mesh is set to ignore all until it goes into ragdoll and during that time the mesh component is set to ignore all instead.
Here’s the code for our custom FloatingPawnMovementComponent:
void UEnemyMovement::TickComponent(float DeltaTime, enum ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
if (ShouldSkipUpdate(DeltaTime))
{
return;
}
UPawnMovementComponent::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!PawnOwner || !UpdatedComponent)
{
return;
}
AController* Controller = PawnOwner->GetController();
ABasePawn* basePawn = Cast<ABasePawn>(PawnOwner);
if (Controller && Controller->IsLocalController() && basePawn)
{
// apply input for local players but also for AI that's not following a navigation path at the moment
if (Controller->IsLocalPlayerController() == true || Controller->IsFollowingAPath() == false ||
bUseAccelerationForPaths)
{
ApplyControlInputToVelocity(DeltaTime);
}
// if it's not player controller, but we do have a controller, then it's AI
// (that's not following a path) and we need to limit the speed
else if (IsExceedingMaxSpeed(basePawn->MoveSpeed.Get() * 10) == true)
{
Velocity = Velocity.GetUnsafeNormal() * basePawn->MoveSpeed.Get() * 10;
}
LimitWorldBounds();
bPositionCorrected = false;
AAIController* AIController = Cast<AAIController>(Controller);
AActor* FocusActor = nullptr;
if (AIController)
{
FocusActor = AIController->GetFocusActor();
}
float CosValue = FVector::DotProduct(Velocity.GetSafeNormal(), Controller->GetPawn()->GetActorForwardVector());
if(!FocusActor)
{
if (CosValue < 0 && !FocusActor)
Velocity = FVector(0, 0, 0);
else
Velocity *= CosValue; // Nice curve for speed / rotational difference from travel vector
}
// Rotate actor
FQuat TargetRotation = DecideTargetRotation(Controller);
FQuat Rotation = FQuat::Slerp(OldRotation, TargetRotation, TurningBoost / 2 * DeltaTime);
RotationSpeed = Rotation.Rotator() - OldRotation.Rotator();
// Move actor
FVector Delta = Velocity * DeltaTime;
if (!Delta.IsNearlyZero(1e-6f) || !Rotation.IsIdentity(1e-6f))
{
const FVector OldLocation = UpdatedComponent->GetComponentLocation();
FHitResult Hit(1.f);
APawn* pawn = Controller->GetPawn();
UPrimitiveComponent* pc = Cast<UPrimitiveComponent>(pawn->GetRootComponent());
if (pc)
{
ABasePawn* BasePawn = Cast<ABasePawn>(pawn);
if ((BasePawn && BasePawn->IsMovementEnabled() && !Delta.IsNearlyZero(1e-6f)))
{
pc->AddForce(Delta * basePawn->MoveSpeed.Get() * 10, NAME_None, true);
ABaseEnemy* enemy = Cast<ABaseEnemy>(pawn);
if(enemy)
{
enemy->PlayMoveSound();
}
}
if ((BasePawn && BasePawn->IsRotationEnabled()))
{
pawn->SetActorRotation(FRotator(Rotation));
OldRotation = Rotation;
OldRotation.Normalize();
}
}
if (Hit.IsValidBlockingHit())
{
HandleImpact(Hit, DeltaTime, Delta);
}
// Update velocity
// We don't want position changes to vastly reverse our direction (which can happen due to penetration fixups etc)
if (!bPositionCorrected)
{
const FVector NewLocation = UpdatedComponent->GetComponentLocation();
Velocity = ((NewLocation - OldLocation) / DeltaTime);
}
}
// Finalize
UpdateComponentVelocity();
}
}
I personally do not understand everything going on here as a lot of it is copied from the in engine FloatingPawnMovement, we did however change the actual movement input to an AddForce() to allow the enemies movement to be affected by various physics interactions. I tried to highlight the specific code mention, just imagine it’s one block of code.
I read about depenetration and played with some settings, I changed the “Collision Max Push Out Velocity” to 0,001 and saw no difference in the problematic behaviour which is very confusing to me.
I have been too busy today but tomorrow I will try adding another collider which is not simulating physics specifically to absorb the projectile collisions.
Did I miss any important details?
Anyway, thanks for your time good sir!