Moving Objects (E.G Doors, Lifts Platforms) that push and crush objects

What are the best ways to have moving objects in unreal that correctly push other objects like players and physics objects but also detect when those objects can’t be pushed anymore because a wall is in the way?

Also if something is a crusher, the object can crush said objects instead of stopping.

What we get out of the box in Unreal is moving geometry that either stops the moment there’s a blocking hit, or doesn’t stop, and pushes things out of its way. But it can’t detect when the pushed objects are unmoveable due to a wall being in the way of the pushed object. So you’ll just get janky instability as objects start pushing into each other and teleporting into each other.

This would be pretty essential for sliding doors, for example, or crushing ceilings or other machinery.

It would be great to have moving objects like this bridge in DooM 3: Let's Play Doom 3 81: Spinning Bridge of Fun - YouTube
In fact, even just sliding doors are currently not that easy out of the box due to the collision detection involved.

“Unmovable” is relative. With enough force, anything can be moved!

What I would do is build these objects as physical simulated actors. I would add movement restriction joints to “lock” them to a particular movement axis if they are “on rails” and lock their rotation axis if they are not supposed to rotate.

Then, I would build a simple controller that has a target velocity for the actor, and applies a force proportional to the difference between the object and its current velcoity, with some clamped max force. This would make the object accelerate to the target velocity, then apply a small maintenance force, until it gets blocked, at which point it would attempt to accelerate again by applying more force. However, if it gets stuck against a wall, that force won’t move the wall, and the object would just keep trying to push with the appropriate force, without getting anywhere.

To apply crushing damage, you could mark the object as “damage on contact” and then scale the damage based on how much force the object is currently applying to itself. Or you could detect being hit from two opposing sides at the same time in the player character (with appropriate OnHit triggers) and calculate damage that way.

The Unreal InterpToMovementComponent is a great example of how incomplete the current moving object system is in Unreal. If you put a random physics affected object in the way, the things I’m describing below will happen. (Also try standing on top of the object, it’ll slowly sink into the ground. This may be a bug, I fixed it by adding MoveComponentFlags |= MOVECOMP_IgnoreBases; to the movement component so objects on top don’t push the object they’re standing on top of down)

In Unreal and physics engines in general, Kinematic Bodies are unmoveable and don’t have forces applied to them. They just magically move and push everything that is physics controlled. So a box that’s affected by physics won’t push back against a moving platform that is a kinematic actor. The moving platform will push the box, until that box itself runs into a wall. That moving platform will then happily just keep clipping through physics affected object that are rammed into a wall unless it detects the collision and stops. But detecting a collision and stopping means even the tiniest peice of debris can instantly stop a giant moving platform. Unreal’s Component Movement functions don’t take physics or mass into account, they simply sweep until a blocking hit. Characters are also Kinematic Bodies. So you could be playing as the tiniest character ever but nothing in Unreal can ever stop you from pushing an insanely massive object out of the way, unless the character collision stops short of bumping into the object in the first place.

I did start working on a Crush Detector component that can be added to any actor and it’s sortof working. It feels a bit hacky but basically I’m tracking the current accumulated impulses from hits this game tick. If the dot product of an incoming hit is the opposite of the current accumulated vector then it thinks it’s a crush. It’s far from perfect but as a prototype it kinda works.

I have an idea to make the crush detector component communicate with a moving platform movement component. If the platform should crush the object being crushed, it’ll destroy it. If the platform should keep pushing the object until there’s an obstruction, the platform will be told by the crusher detector that it’s being obstructed, otherwise, the moving platform will keep pushing the physics objects.



void URDBaseCrushDetectorComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    AccumulatedImpulse = FVector::ZeroVector;
}

void URDBaseCrushDetectorComponent::OnComponentHit(UPrimitiveComponent* HitComponent,
    AActor* OtherActor,
    UPrimitiveComponent* OtherComp,
    FVector NormalImpulse,
    const FHitResult& Hit)
{
    float IncomingImpulseSize = NormalImpulse.SizeSquared();
    float AccumulatedImpulseSize = AccumulatedImpulse.SizeSquared();

    if (IncomingImpulseSize > 0.f && AccumulatedImpulseSize > 0.f)
    {
        // Since we already have the size squared we can avoid the full normalize calculation

        FVector IncomingNormalizedImpulse = NormalImpulse * FMath::InvSqrt(IncomingImpulseSize);
        FVector AccumulatedNormalizedImpulse = AccumulatedImpulse * FMath::InvSqrt(AccumulatedImpulseSize);

        if (FVector::DotProduct(IncomingNormalizedImpulse, AccumulatedNormalizedImpulse) < -.8f)
        {
            //UE_LOG_SCREEN_ERROR(RDBase, TEXT("CRUSH IncomingSize: %f (%f) AccumulatedSize: %f (%f)"), IncomingImpulseSize, FMath::Sqrt(IncomingImpulseSize),  AccumulatedImpulseSize, FMath::Sqrt(AccumulatedImpulseSize));
        }
    }

    AccumulatedImpulse += NormalImpulse;
}