I derived a class “UPhysicsBallMovementComponent” from “UMovementComponent”. I would like to implement physics ball movement in this class (without enabling physics simulation on the actor).
I call “SafeMoveUpdatedComponent” in a while loop in the function TickComponent() to move the actor (in multiple small distances).
One difficulty I meet is handling the ball roll/slide a surface. In case that the ball is on a horizontal surface and with an initial velocity (x=100, y=0, z=0), the ball hits the surface very often (because gravity is set to 980) and “SafeMoveUpdatedComponent” returns false in 50%, I don’t know what to do when a move is failed.
The following is some code. PassedTime equals DeltaTime from TickComponent().
FHitResult Hit(1.f);
const FRotator NewRotation = ActorOwner->GetActorRotation();
float RemaintingTimeToBeCalculated = PassedTime;
float CalculateTime = 0.f;
uint32 Iterations = 0;
while (RemaintingTimeToBeCalculated > MIN_TICK_TIME && !ActorOwner->IsPendingKill())
{
UE_LOG(LogTemp, Warning, TEXT("RemaintingTimeToBeCalculated: %f, PreviousHitTime: %f"), RemaintingTimeToBeCalculated, PreviousHitTime);
Iterations++;
CalculateTime = RemaintingTimeToBeCalculated > MAX_TICK_TIME ? MAX_TICK_TIME : RemaintingTimeToBeCalculated;
const FVector OldVelocity = Velocity;
const FVector MoveDelta = ComputeMoveDelta(OldVelocity, CalculateTime);
bool bMoveResult = SafeMoveUpdatedComponent(MoveDelta, NewRotation, true, Hit);
//bool bMoveResult = MoveUpdatedComponent(MoveDelta, NewRotation, true, &Hit);
if (bMoveResult == true)
{
UE_LOG(LogTemp, Warning, TEXT("bMoveResult is true"));
}
else
{
UE_LOG(LogTemp, Warning, TEXT("bMoveResult is false"));
float SubTickTimeRemaining = CalculateTime;
Slide(Hit.Normal, SubTickTimeRemaining);
RemaintingTimeToBeCalculated -= (CalculateTime - SubTickTimeRemaining);
/* if (Iterations > 100)
break;
else
continue;*/
}
// If we hit a trigger that destroyed us, abort.
if (ActorOwner->IsPendingKill() || HasStoppedSimulation())
{
return;
}
if (Hit.bBlockingHit)
{
UE_LOG(LogTemp, Warning, TEXT(" Velocity: %s, OldVelocity: %s"), *Velocity.ToString(), *OldVelocity.ToString());
// Only handle the hit if events didn't change velocity (nothing changed in event) during the movement update.
if (Velocity == OldVelocity)
{
float SubTickTimeRemaining = CalculateTime;
HandleBlockingHit(Hit, CalculateTime, SubTickTimeRemaining);
RemaintingTimeToBeCalculated -= (CalculateTime - SubTickTimeRemaining);
}
else
{
// here, we also need to handle Hit.Time < KINDA_SMALL_NUMBER
if (Hit.Time < KINDA_SMALL_NUMBER)
{
RemaintingTimeToBeCalculated -= CalculateTime * 0.5f;;
}
else
{
RemaintingTimeToBeCalculated -= CalculateTime * Hit.Time;
}
PreviousHitTime = Hit.Time;
PreviousHitNormal = ConstrainNormalToPlane(Hit.Normal);
}
}
else
{
if (bIsSliding)
{
check(SlideSurfaceNormal.IsZero() == false);
ComputeRotation(SlideSurfaceNormal, MoveDelta);
}
check(Velocity == OldVelocity);
const FVector NewVelocity = ComputeVelocity(OldVelocity, CalculateTime);
UpdateVelocity(NewVelocity);
RemaintingTimeToBeCalculated -= CalculateTime;
PreviousHitTime = 1.f;
}
}
ActorOwner->AddActorWorldRotation(RotationFromThisFrame);