Refactor and Modularize Character/-MovementComponent

As a BP User, Character is one hell of monolith that is difficult to customize.
Even with C++ it’s - in my opinion - almost impossible. Most of the time it requires writing a new class and copying the relevant stuff from Character/MovementComponent to your own class.

I have following suggestions to make, regarding the Character and CharacterMovementComponent:

  • NetworkedCharacterMovementComponent extends CharacterMovementComponent
  • Decouple MovementMode from the Character and MovementComponent. Instead use an interface (MovementModeInterface) which provides the (in current CMC) Phys() function. When switching MovementModes, replace the current MovementMode-Object.
    This way you can extend already existing MovementModes by inheriting them. Provide basic Modes (Walking,Falling,Swimming etc) like you do now (Default Land/Water Movement) but use class instead of ENUM
  • Decouple The MovementComponent and Character, I only took a glimpse into both classes, but it surely is not necessary to bind the Character to the CharacterMovementComponent maybe get rid of the CharacterClass completely, everything should be managed by the MovementComponent
  • I don’t know if it’s possible, but extract the Root Motion-Code and put it in it’s own Component
  • There’s no Condition customization whether the Character switches to Swimming Mode. Either provide a overiddable function, or a threshold (How much of the Capsule has to be in Water)

I’d like to have some feedback and if there’s a future update regarding Character at all.

Most of these things aren’t strictly possible, mainly the separation of chunks of code into other areas or components. You could have it spread accross multiple files, but that doesn’t simplify the problem.

I wrote a vehicle movement component a few months ago which is based on CMC, netcode etc is extremely intertwined at every point in the code. It’s the same as any other MP situation, if you’re going to implement networking support you need to do it from the offset. Tacking it on later is near enough impossible.

I posted this thread a while ago after writing my own component, whereby I’d divided up a lot of my code between classes to make it as reusable as possible. One of my only real gripes with the engine at times is how it’s geared towards biped movement, and breaking into other styles of movement with full support for things like AI etc is very difficult.

Why does the Character/MovementComponent have networking code? Doesn’t “Replicate Movement” in AActor do the same thing?

Because of predictions. If you would really only on replicating actor location, movement for local client wouldn’t be smooth enough.

Implementation inheritance is almost never the right choice.
Delegation is fine, if there exists a good seam where an interface can be defined.

Having built several characters in various engines over a long amount of time in the past, I can say that almost everything in the Unreal Character component is actually necessary for a well-behavior game character.
It’s simply a very messy, highly interdependent problem domain.
For any particular character in any particular game, especially for simpler characters in simpler games, it’s quite likely possible to cut some bits out, and refactor the code to be a little neater and a bit more spread out, but that re-factoring doesn’t really buy you very much.

Edit: The main strategy for re-factoring would be changing built-in flags and modes to interfaces. Instead of “can step up on things,” configure it with an ICharacterStepUp. Instead of “can crouch,” configure it with an ICharacterCrouch. All of those interfaces would have to have access to, and “poke at,” the internals of the character component state machine/data, though.
Unfortunately, when you do that, actually instantiating a character becomes much harder, because now you need an instance of all of the behaviors just to have the character work.
There could be default implementations of all these interfaces that come with the default character component, and you can override those with configured additional interfaces. But, at that point, the default class looks a lot like what you have now … The main gain would be the introduction of those interface seams; the main costs would be that configuring a character in the editor would be broken out into lots of separate components, and there would be more virtual calling overhead (really bad for branch predictors and cache.)

1 Like

Of course the building of a character becomes more complex, but at the moment we have “Have everything or nothing”.
Epic does not provide an alternative. There’s no Movement Component that implements Gravity or collision like the CharacterMovement does.
There’s no component that has the Networking Code. There’s no Component that provides Root Motion.
As soon as your Character has more than two legs, Character won’t work anymore, not to mention that we have an inaccurate Capsule as a collision and we can’t even change it or add additional collision.

There are some pretty tight integrations between collision, and how the character is supposed to react (step up, crouch, bounce back, etc.)
Those could probably be formalized and put a long a seam, and the movement component perhaps broken out. There is still the runtime cost of the seam to worry about, but it might be worth it.

Networking (at the level of entity replication) cannot be separated from the inner workings of the component. Networking must be designed-in at the base level, or it won’t be right. It is a networked character component, that means networking is a necessary requirement for it. That can’t reasonably be factored out.

By “component that provides Root Motion,” do you mean “something that reads the animation root motion track, and forwards it to the character movement component?”
If so, that seems to be so little code, it’s not worth it to put into a separate component to me. That’s literally, like, one line of code?

I don’t think that’s true. How many limbs are animated in your character is not of particular concern (except for perhaps things like IK foot fix-up.)

That’s a reasonable question: Should “character” mean a humanoid that can be approximated as a capsule, or should it be abstracted to ants, dragons, etc?
Many of the assumptions that go into a bipedal human character (swimming, running, falling, crouching, etc) aren’t guaranteed to make sense for other forms, so I think it’s totally OK to say “this class does humanoid characters” and leave the others to other classes. (There are classes for cars, for example.)

Also, you can create a physics asset for your skeleton, and use that as collision instead, which means you get collision for any kind of movement. I don’t know how far you could come with that if your character is a horse or whatever, but it might be worth a try.

Other than that, when you fall outside the assumptions of humanoid character, you really need to back down to Pawn and go from there. There’s a lot of movement components and animation support that’s pretty easy to re-use at that level. I don’t think the majority of the Character assumptions really can be abstracted to “arbitrary character” without losing a lot of what it provides for humanoids, so I understand the design trade-off EPIC has chosen.

Btw: When you build your horse/dragon/millipede character-analog class, I highly recommend building it with networking at the front and center. If you don’t, and you later need to support networked gameplay, it will be all pain, all the time!

1 Like