Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Devlog: from CharacterController to RigidBody

aka why I decided to remake the player movement a year into development

Project intro aka who am I, what am I doing here, and why you’re reading my rant

Right now you must be thinking:

Who are you? Mattia, freelance game programmer and the one writing this devlog (Incredible, I know, who would’ve thought). Nice to meet you!

What is an Arcadia? A 2.5D fast-paced Metroidvania with an anime-like aesthetic, and the subject of this devlog. Here's our trailer

 

Having decided on an enemyless approach to the game, with the only actual antagonists being giant mecha-birds that act more like environmental puzzles than actual enemies, the game heavily relies on movement for its gameplay.

Having also like 8 abilities, most of which are movement abilities, means the player’s movement system has to be the best possible.

For that reason, I decided to remake it, a year in development, after making the first closed alpha build.

Now you must be thinking:

  1. The old movement didn’t work/wasn’t fun
  2. I’m just nuts

The answer to both is: “eh not really, but you know…”

The movement was fine: it did work (apart from a couple of bugs I’ll explain later, which are part of the reasons I decided to switch to rigidbody) and, overall, I think I could’ve been pretty satisfied. We had some work and some polish to do, but we had something in our hands.

Point is, I didn’t want it to be fine: I wanted it to be fast, I wanted it to be responsive. I want it to be fun, and I could see the current system's limits. Not being the sole programmer anymore I decided to take a couple of weeks to prototype a new movement system and try to make it what I wanted it to be.

This is how I did it (It sounds like the start of an epic journey, doesn’t it? Kinda cool, huh?)

The before aka why I didn’t like the player movement system

THE BEAST, the old player controller inspector

How did the player move then? you must be thinking, enthralled by my masterful introduction.

Let me explain:

At the start of development, it was decided to try the unity character controller component because it gave us some interesting functions without having to rely on Unity’s physics system.

The CharacterController Component sounded like a good idea, at the time: we weren’t sure which mechanics to implement so I made a small prototype based on a couple of basic platform mechanics and it was nice.

It was basically my first Unity controller so there were a couple of things I’d spot now, but it worked as intended and was pretty fast to make.

Also, we didn’t need the player to move or affect other physics objects so it sounded like a good way to just have the basic collision and movement methods we needed (also having the step/slope mechanics already made sounded nice, tbh).

A non physics-based movement: We didn’t want to depend on the physics engine not because it’s not good or something, but because we wanted the game to feel tight and predictable movement. A physics-based movement meant unpredictability, as in the player can’t always predict things like the exact position the character will arrive to, at the end of a jump, for example, because it depends on factors not necessarily clear to them.

Basically, all the movement in the player controller was calculated to be precise and predictable.

I based the jump on the “build a better jump” talk with the Verlet function to integrate the player speed, an implementation inspired by this video by “iHeartGameDev”.

I made a state machine based on the Weimann implementation, dividing the various movement into different states.

At that point decided to add acceleration and deceleration only to the player’s run, clamped to a maximum speed, to have a more natural feeling to the movement. In the meantime, the rest of the mechanics were delineated, prototyped, and added to the state machine and during that period some problems came out.

 

 

PROBLEMS

Moving Platform and general external movement: the absolute biggest problem, player movement-wise, was the presence of moving platforms.

The Character controller presence meant I couldn’t just set the player as the platform child and just move its transform nor have the platform with a rigidbody and change its velocity.

Also, I had to use triggers on the platform, cause collisions wouldn’t be reliable, as the movement wouldn’t be in synch

The best solution I could find is giving the platform a rigidbody, calculating the new position each frame, and calling “movePosition”, then if there’s the player in its trigger it sends its velocity to the player controller. The speed will be added to the movement vector and the player will move accordingly to the sum of its internal and external “forces”.

Both movements were in fixedUpdate, to try to synchronize them.

It kinda worked, with a big enough trigger, I had the player reliably move with the platform whatever the direction, but the characterController wouldn’t always register as “onGround” meaning sometime it didn’t register jump inputs. Also if the trigger area wasn’t big enough or the speed was too high the pc wouldn’t be able to move in time and would be left behind (poor guy).

 

Polish

Honestly polish was the most pressing of the matters. As I said before, the controller was working, but I knew it could be better, so I looked around searching for things to add to have a better feeling controller.

I hope I don’t need to tell you what Celeste is and who’s Maddy Thorson which, in this thread, showed some of the game-feel elements present in Celeste, which was incredibly helpful (also if you haven’t played Celeste yet do yourself a favor and do it, it’s incredible).

Starting from that I went around youtube searching for other inspiration and ideas



in the end I wanted to add:

  • Horizontal movement with a more natural acceleration/deceleration.
  • Coyote time.
  • Halved gravity and horizontal movement augmented during the jump peak.
  • Jump buffer.
  • Glide with acceleration.
  • A lerped wall-jump horizontal movement (give back control to the player gradually after a wall-jump).
  • A better feeling wall-jump.
  • Make moving platforms, trampolines, and geysers feel better.

The solution aka I clearly don’t know physics

Honestly, most of those could’ve been implemented with the older controller, but it would’ve still been janky, as it would’ve been based on workaround things I didn’t like. Having another programmer with me and watching people play our first test build convinced me it was worth remaking it.

So I did.

I decided on using the rigidbody, mostly to have collisions with the environment (I may be stupid, but not “I’m gonna make a new collision system at this point in development” stupid, you know?).

 

Horizontal Movement

The jump math was fine, so it wouldn’t be changed, the horizontal movement was to be remade though

I based the horizontal movement on the one shown in the video based on the idea of calculating the force necessary for the rigidbody to get to the maximum velocity.

Basically, we calculate the target speed we want to achieve, then the acceleration rate.

We multiply the target speed and the acceleration rate. The target speed is then lerped by an amount given in input to the method.

At this point, we calculate the difference between the target and the current rigidbody velocity.

the final force applied to the rigidbody is the velocity difference multiplied by the acceleration rate.

 

The State Machine


Of course, I made a state machine for a prototype.

I made a super minimal implementation of it, based on an enum and a switch at the start of the fixedupdate.

The update does a round of checks like if the character is grounded, the coyote time checks then goes unto the switch at the center of the machine.

Each “State” has an “onEnter” part, that contains the state setup and returns the method.

Other than the “onEnter” check, the switch contains the various state checks.

At the end of the “state” there are the movement method calls.

Technically speaking it may have been an overkill, but I honestly didn’t want to deal with a thousand flags and check more, and trying to make a state machine as simple as possible and as fast as possible was a fun challenge in and of itself

 

From velocities to forces

Initially, I decided to just change the RigidBody velocity to re-use the old math when possible, but it would mean still having sync problems between the player update and the external force sources.

Just using addForce for the other classes wouldn’t work, clearly, and making an “addExternalForce” method again would just bring the same problems again, so we’re using forces now.

For that I needed to translate what I had, velocities, to what I needed, forces:


The verlet integration explained in this article

while using velocities, we used the Verlet integration to calculate the new player velocity


and to translate the results to the forces system:

Given

we can write the verlet formula as

we can see that by multiplying the acceleration given by 0.5 we can get to the same formula, showing an equivalent movement using forces.

We tested this the rigidBody-based prototype against an identical one, but characterController based to see if we’d actually get the same results.

We made a simple script taking the two controllers velocities and writing them in lateUpdate.


As you can see the two values start differing after the fourth decimal, most of the time, but it could be just a difference in approximation. So we think we can say that’s good enough to prove us right, right?

 

We’re using Forcemode.Force for the horizontal movement and the gravity simulation and impulse for the jump initial velocity.

That gives also the possibility to easily add external forces, such as trampolines and geysers, with the same reasoning.

And basically, that’s it.

The ending aka Where are they now?

  • Mattia survived and continued doing his thing, whatever that means.
  • The player controller feels snappier and cooler, now, and lived happily ever after.
  • The moving platforms aren’t the actual spawn of satan, anymore, they do charity work every Sunday.
  • We had to unify the colliders of the pavement tiles, with a small script for the scenes we already made, that takes all the children’s meshes and creates a big collider in the parent. Like a big hug!

  • Of course, most of the transition was fighting the old state machine, because state machines are a great tool with great power, but also a great responsibility.
  • Airi still jumps a lot and is pretty happy about it.
  • Enji is still the smart one of the gang and is helping with the creation of the game.
  • Tazpo is still Tazpo.

Support this post

Did you like this post? Tell us

Leave a comment

Log in with your itch.io account to leave a comment.