Character Momentum

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By HabastianLynx

Hi, I’m just trying to make my own 2D game that’s similar to Sonic, but I don’t know how to code progressive momentum. If anyone knows how to do this, I’d be very thankful for the information.

:bust_in_silhouette: Reply From: godot_dev_

How I have coded momentum animations in the past is as follows, and it may help you. I started with defined the most basic object to define movement, a basicMovement. It has speed, angle, max/min speed, acceleration, and duration members. With these properties you can configure basic linear velocity. By combining them together, you can obtain more complex movement like gravity + horitontal walk speed. Add an additional basicMovement that sends the player into the air and now you have a running jump.
Every frame, in the _physics_process loop, all the basicMovement that their duration hasn’t ellapsed yet I aggregate all their speed properties (those basicMovement with acceleration dynamically increase their own speed every frame) into a single movement vector velocity, a Vector2D, and I apply move_and_slide using velocity to apply the movement.
To have even more control on movement animations, I created the object complexMovement, which is a parent that can have many basicMovement child nodes, and has 3 important properties startTime,type, and gravityEffect. The startTime just indicates when to activate all the basicMovement child nodes (e.g, startTime = 5 means 5 frames (or you could define an actual time and compare it to the time ellapsed since the movement animation started) after the movement animation (parent of the complexMovement) started, the movement properties of all basicMovement of the complexMovement activate and affect the player’s momentum. The next member is the type, fundamentally, there is NEW and ADD movement. NEW means any previous movement animations’ basicMovement are stopped prematurely, and ADD means the basicMovement already activated are unaffected and new basicMovements are applied. The gravityEffect memeber will have 3 values REPLAY,KEEP, and STOP. It indicates, when a complexMovement activates it’s basicMovements, whether to restart, keep, or stop the the current gravity (this assume you have a global basicMovement for gravity that is always applying to the player), respectively.

For example:

  • say the player is running forward. Playing the running backward animation should completely stop forward momentum and replace it with back momentum, so that type of animation would have at least one complexMovement with type = NEW and at least one of its basicMovement would be angled backward. The gravity should be kept as is since we remain on the ground, so the gravityEffect would be KEEP.
  • Now suppose you jump while running forward. You don’t want to lose your forward momentum and you want to gain upward momentum. The complexMovement to accomplish this would have type = ADD and gravityEffect = REPLAY. Without replaying the gravity, if gravity keeps accumulating even when on the floor, your jump upward momentum will barely lift you off the ground.

This is a complete solution to a nice movement system. For example, if you want to move in the air while keeping your upward momentum (like if you jump and try to A-D strafe in the air) this system can’t support this. You would need to add a new complexMovement type that support stopping the last activated basicMovements, so the upward momentum is unaffected but the left-right straffing (the last activated basicMovement gets stopped)

Hope this helps

Thank you for the reply, but do u think u could type out the coding so that I can get a better understanding of what goes where, or a picture of how it looks together. Sorry, I sometimes need to see how it’s done to fully understand how it works.

HabastianLynx | 2022-08-29 06:49

Here is pseudo code below illustrating my suggestion:


   MovementAniamtion Script
   var ellapsedTime=0
   var complexMovements=[]
   var activeMvm = [] 
   var gravity = null #this will be a reference to a basic movement node
var targetKinematicBody2d=null #thing we will move
func _physics_process(delta):
	ellapsedTime = delta
	#go over all complex movements involved in a movment animation
	for cm in complexMovements:
		#strat the complex movements that start late
		if cm.startTime <=ellapsedTime:
			#apply movement
			if cm.type == NEW:
				 #stop all basic movement 
				 for bm in activeMvm:
					bm.stop()
				activeMvm.clear()
				#replace with new movement
				for bm in cm.basicMovements:
					activeMvm.append(bm)
			elif cm.type == ADD: 
				#don't interupt the movement, add new movement
					#replace with new movement
				for bm in cm.basicMovements:
					activeMvm.append(bm)
					
		#apply gravity effect
		if cm.gravityEffect == REPLAY:
			#reset speed to 0 of gravity
			gravity.xSpeed =0
			gravity.ySpeed =0 
			gravity.set_physiscs_process(true)#make sure gravity enabled
		elif cm.gravityEffect == KEEP:
			pass #do nothing to gravity
		elif cm.gravityEffect == STOP:
			gravity.set_physiscs_process(false)#make sure gravity disabled
			
			
			
	var velocity = Vector2(0,0)
	#now apply the basic movement to get a final Vector2 that represents velocity
	for bm in activeMvm:
		if bm.is_physics_processing():
                   #the basic movements will need to keep track of their ellapsed time, and when the ellapsed time exceeds the duration, the basic movement stops physics processing (set_phsysics_process(false). That's why we check the is_physics_process here
			velocity.x =  velocity.x + bm.xSpeed #Xspeed is the speed (after acceleration, if any) normalized byt the bm.angle member
			velocity.y =  velocity.y + bm.ySpeed #yspeed is the speed (after acceleration, if any) normalized byt the bm.angle member
			
	#add the gravity (if active)
	if gravity.is_physics_processing():
		velocity.y = velocity.y + gravity.ySpeed
		
	#
	targetKinematicBody2d.move_and_slide(velocity,...)#here is twhre u call move and slide once per frame, make sure to pass the appropriate paramgeters
			

godot_dev_ | 2022-08-29 14:28

Thank u so much mate, this’ll make things a lot easier for me.

HabastianLynx | 2022-08-30 04:28

Hi, sorry for asking this late, but in which node should I put this coding, my root node, or my character node?

HabastianLynx | 2023-02-14 03:13

I recommend adding it as a child node (or the character node itself) of the character node, because if you ever have more than 1 game node you want to move around, adding the code to the root node will become messy

godot_dev_ | 2023-02-14 17:29

Hi, just one more question, should I use the coding you showed me to replace the pre-existing one I have, or be with it? Here’s the coding I have for my character node so far.
extends KinematicBody2D

var velocity = Vector2(0,0)
const SPEED = 140
const GRAVITY = 37
const JUMPFORCE = -670

func _physics_process(delta):
if Input.is_action_pressed(“right”):
velocity.x = SPEED
if Input.is_action_pressed(“left”):
velocity.x = -SPEED

velocity.y = velocity.y + GRAVITY

if Input.is_action_just_pressed("jump") and is_on_floor():
	velocity.y = JUMPFORCE

velocity = move_and_slide(velocity,Vector2.UP)

velocity.x = lerp(velocity.x,0,0.2)

HabastianLynx | 2023-04-01 22:01

1 Like

Hiii…
To code progressive motion you will need to handle slopes and surfaces differently to simulate the feeling of motion. For example, when your character moves downwards, their speed should increase, and vice versa, when moving upward their speed should decrease.