Architecture advice needed: Designing a Gravity Receiver for an Outer Wilds style gravity system

Hi everyone,

I’m currently working on an Outer Wilds clone in UE. I’ve designed a custom gravity system using a UGravitySourceComponent and a UGravitySubsystem. The subsystem maintains containers (arrays) to keep track of all gravity sources and receivers.

However, I’m struggling with the architecture for the receiver side of things. The main challenge is that planets, the player, and regular objects all need to apply the received gravity differently:

  • Planets: Need to be moved kinematically.

  • Player: Needs to integrate the gravity with the CharacterMovementComponent.

  • Regular Objects: Just need a simple AddForce call, letting the physics engine handle the rest.

I currently have three approaches in mind:

  1. Use an Interface: Implement an IGravityInterface directly on the respective Actors, letting each actor define how it handles the gravity vector.

  2. Component Subclassing: Create a base UGravityReceiverComponent and derive three specific subclasses (e.g., for Kinematic, Character, and Physics).

  3. Single Component with Dispatch Logic: Write a single UGravityReceiverComponent and handle the branching/dispatch logic internally based on what type of Actor owns it.

Does anyone have any advice on which approach would be the most robust and idiomatic for Unreal? Or is there a completely better way to architect this that I haven’t considered?

Thanks in advance!

Hi @YanMo_Liu Welcome to the forums!

I think your approach already in the right direction. Its a fun system to create.

  • A Subsystem to rule them all, probably a UWorldSubsystem or UGameInstanceSubsystem. This has all the major functions ApproximateGravityAtDistance, RegisterSource, ReleaseSource, Events and so on.

  • UGravityReceiverComponent : An actor component that can handle various communication between subsystem and the owner to do things. Helper functions, UpdateGravitySource, GetCurrentGravity, EventsOnMajor changes etc.

    • To your question, Component subclassing is the cleanest solution. Simply you create a base and for different needs you can have
      UPhysicsGravityReceiverComponent
      UCharacterGravityReceiverComponent
      UKinematicGravityReceiverComponentEach overrides how gravity is applied. No internal branching needed.
  • An interface for smaller things that need to handle stuff, like a simulated simple actor can use also can be utilized further to serve other things.Why not only interface? Because interface can get bloated in these kind of systems since you also need state handling, caching and source tracking. For smaller things its better so small actors don’t have to initialize a full component. If its going to ask just what is my gravity it can simply query the subsystem or planet component.

  • A UGravitySourceComponent : An actor component which is the source of the gravity. Can be a planet, a blackhole or something else.

What I think additionally (optional if system grows) is making this not only “Component” driven but also data driven. Example you have some data defining:

Name, Position, Category, Mass, Radius, Maybe a FallCurve.

Example workflow:

  • Planet streamed in → UGravitySourceComponent starts run, calculates its specs from data table and quickly registered at subsystem.
  • Objects are constructed and predefined physics behaviour is set.
  • When approaching a planet, AGravityReceiverComponent finds most relevant gravity source via subsystem. Not just radius but mass, falloff curve and dominance. This becomes important in multi-body and small planetery systems (black hole + stars + planets) so a dominance resolver would be needed, which source of gravity in charge (Can be just addition or a magnitude/distance resolve). Model of resolve a bit gameplay dependent, moving objects how close they are etc.
  • When relevant source changes we cache and calculate gravity at distance efficiently, preferably on tick.

Edit : As addition, I wouldn’t go this side on this system but

IF the system scales or gravity shapes become more complex (non-spherical bodies, designer-authored zones), you could consider representing gravity as a sampled field instead of resolving every source dynamically. For mostly static bodies this can even be prebaked in local space and moved kinematically with the actor, then receivers simply sample the field. That trades flexibility for lookup simplicity and stability, so depends on your gameplay needs.