Skip to content

Instantly share code, notes, and snippets.

@trentpolack
Last active August 4, 2024 09:42
Show Gist options
  • Save trentpolack/edefe1c72161f57dc188dd0c746a606e to your computer and use it in GitHub Desktop.
Save trentpolack/edefe1c72161f57dc188dd0c746a606e to your computer and use it in GitHub Desktop.
The "Fun" to be had (i.e. a big rant) in writing a general-purpose physical movement component (compound colliders representing static and skeletal meshes and more, but still interacting with the world as a physical entity). NOTE: All comments in this gist are by me.
// Comments are left by me, not from the code.
// Because why not have a default set or make the method abstract or, really, anything but this. Luckily no one would ever
// think to use ::GetMaxSpeed as a divisor or anything.
inline float UMovementComponent::GetMaxSpeed() const
{
return 0.f;
}
// FUN FACT: UMovementComponent defines a plethora of methods for managing the maximum speed of a component. It does not,
// however, actually have a maximum speed class member.
// I'm not sure this really handles an impact. Of any kind. Anywhere. Ever.
void UMovementComponent::HandleImpact(const FHitResult& Hit, float TimeSlice, const FVector& MoveDelta)
{
}
// Seriously, what is with just leaving methods empty with no obvious way to notify developers.
void UMovementComponent::AddRadialForce(const FVector& Origin, float Radius, float Strength, enum ERadialImpulseFalloff Falloff)
{ }
void UMovementComponent::AddRadialImpulse(const FVector& Origin, float Radius, float Strength, enum ERadialImpulseFalloff Falloff, bool bVelChange)
{ }
// Even UE4's basic humanoid movement controller (a 10k line CPP file) likes this whole "hope they don't use that and expect
// it do anything" philosophy.
void UCharacterMovementComponent::OnMovementUpdated(float DeltaTime, const FVector& OldLocation, const FVector& OldVelocity)
{
// empty base implementation, intended for derived classes to override.
}
// Yes, again, another silently empty method, but it's made more confusing since ::ShouldJumpOutOfWater and ::CheckWaterJump both
// are fully-implemented methods.
void UCharacterMovementComponent::JumpOutOfWater(FVector WallNormal) {}
// This is just accurate, no one should ever "catch air".
bool UCharacterMovementComponent::ShouldCatchAir(const FFindFloorResult& OldFloor, const FFindFloorResult& NewFloor)
{
return false;
}
// Anyway, there's more like this (and some generally horrifying code conventions), but the real issue is that UCharacterMovementComponent
// is essentially just a really, really overwrought implementation to handle a specific type of pawn/actor: basic primitives
// and humanoids. UCharacterMovementComponent is, really, just an overly complex state machine for each type of movement (running,
// ducking, falling, jumping, crouching, walking, swimming, etc.).
//
// This means that, really, deriving a custom movement component from it is not only bringing along a *lot* of additional friends
// for the ride, but also that its implementation is so rigid in its use cases that whatever it offers would have to be intensely
// modified anyway (as I've been doing. this is the third or fourth pass).
//
// TL;DR: As *wonderful* as UE4's codebase is on the core engine/renderer (and many, many other areas), as far as its "gameplay
// framework" is concerned, there's really just not much there (at least, in my mind) that makes life easier for C++ developers.
// Especially given the dearth of documentation on the more obscure classes/components/data structures you'll end up needing
// to use along the way.
@hyperrecursive
Copy link

1 person finds this funny

I was just going throug async physics character movement of UE5 and was buffled by the amount of TODOs and things left unimplemented. I will inherit from UPawnMovementComponent and hope for the best! 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment