Trying to make a slippery surface

Godot Version 4…2

Question

I am trying to make a slippery surface for one of my tileset objects. I am still new to Godot, as this is my first game I am making in it. I tried making a Area2D and the collision was triggering, but realized that Area2D is more of a trigger collider than a physics collider. As my player was falling through the collider

I then tried making a Static object with a sprite of the tileset, but don’t know how to communicate the StaticBody2D to the Player object to trigger slippery physics.

Lastly, I tried to create a StaticBody2D and have a Area2D as the child. But that isn’t working. What is the best way to make a simple physics object that can trigger the player movement to have less friction?

Below is my current Area2D “Slippery Collider” Script:

extends Area2D

@onready var player = %Player

func _on_body_entered(body):
	if body.is_in_group("Player"):
		player.on_ice = true
	


func _on_body_exited(body):
	if body.is_in_group("Player"):
		player.on_ice = false

And this is my Player Script:

extends CharacterBody2D


const SPEED = 130.0
const JUMP_VELOCITY = -300.0
const ICE_FRICTION = 0.1
const NORMAL_FRICTION = 1.0

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

@onready var animated_sprite = $AnimatedSprite2D

var stuck_in_sticky_area = false
var on_ice = false

func _physics_process(delta):
	on_ice = false
	
	if stuck_in_sticky_area:
		velocity.x = 0
		velocity.y = 0
		if Input.is_action_just_pressed("jump"):
			velocity.y = JUMP_VELOCITY
			stuck_in_sticky_area = false
	# Add the gravity.
	else:
		if not is_on_floor():
			velocity.y += gravity * delta		

		# Handle jump.
		if Input.is_action_just_pressed("jump") and is_on_floor():
			velocity.y = JUMP_VELOCITY

		# Get the input direction and handle the movement/deceleration.
		# As good practice, you should replace UI actions with custom gameplay actions.
		var direction = Input.get_axis("move_left", "move_right")
	
		#Flip the sprite
		if direction > 0:
			animated_sprite.flip_h = false
		elif direction < 0:
			animated_sprite.flip_h = true;
	
		#Play Animation
		if is_on_floor():
			if direction == 0:
				animated_sprite.play("idle")
			else:
				animated_sprite.play("run")
		else:
			animated_sprite.play("jump")
		
		#Apply Movement
		if on_ice:
			print("On Ice")
			velocity.x = lerp(velocity.x, direction * SPEED, ICE_FRICTION)
		else:
			if direction:
				velocity.x = direction * SPEED
			else:
				velocity.x = move_toward(velocity.x, 0, SPEED)

	move_and_slide()

Not entirely sure, but I know some physics objects have a built-in friction modifier. The only I don’t know is how it would work with a tileSet.

That’s a great question but I’ve never heard about it being done on a tileset.

Should be possible on physics objects though - It would be good to know though for sure. Might need to make your own assets :thinking:.

This kind of behavior in a Character body would have to be coded yourself.

If you break down the problem, what you need to do to simulate a slippery surface is to apply velocity to the character body object and slow it down over time.

For the slippery surface, you need a way to tell the player that it’s on a slippery surface.

One way to do this is to add a custom data to the slippery tile that tells the player if it should slip or not. Adding custom data on a tile is a way to save info on tiles.

On the player script, all you need to do is to use a raycast to check the data of the current tile and if is_slippery is true, apply velocity to the player for a brief period before slowing them down.

A way to apply velocity over time while slowing down is

var friction: float = 0.1

func physics_process(delta):
     ##Slow down movement over time
     velocity.x = lerp(velocity.x, 0.0, friction)
     move_and_slide()

This code lerps the player x velocity over time until it’s zero. If you want a constant slip, then there is no need for this, all you need is to detect if the player is on a slippery tile.

The higher the friction, the faster the player slows down.

2 Likes

How would you go about doing that? Do you know of any YouTube tuts on that topic.

Would making custom assets eg sprites that look like tiles and placing them in a group say “slippery” work? Now that the topic has been brought up I would like to try doing it myself. Or would it be better to make ridigbody2D objects and change the friction modifier.

Regards.

This is an image of the custom data layer from Godot docs. You can add any data in there that you can check at run time.

Making custom sprites is also an option.

Depending on your game using rigid body can work. I just find them difficult to work with in 2d side scrollers.

1 Like

Can confirm, did something similar to this but with area2d to determine where it’s slippery as I was making a rather small project but I have to look into these custom tiles data :saluting_face:

2 Likes

Thankyou for replying, I might try the same with area2D as well.

Just for my benefit when I look at this in the upcoming days trying to figure out what to do:

  • the friction modifier in Area2D is - linear damping!

Regards.

1 Like

Well,

How can I say this … ummm …

I managed to get something working and when I hit the “Ice” surface the character goes normal then speeds up into a what looks like a speed boost whilst holding down the direction button - and stops dead when lifted. Doesn’t look like sliding, it does, look kinda cool.

The values for Velocity.X after the calulation is: 0, 117 or -117 and that’s it. I have a fair idea why but solving it …

But for now, I think I’ll keep it.

Ok, been playing around with the movement and need some help
I have these variables

var on_ice: bool = false;
var acceleration = 100;
var deceleration = 100;

the on_ice variable does change when the player hit s the “ice” so that isn’t a problem.

And I have two different code snippets that work one with a slow start and sliding and another code snippet that runs the hero ant the normal speeds I just can’t figure out how to run them together or should I say one or the other. I can only run one or the other properly, I’ve gotten close but there is a small mistake the player jumps onto the ice very quickly:

Both code snippets together as I have them in the code:

##control speed
	##normal movement
	if(!on_ice):
		if direction:
			velocity.x = direction * SPEED
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED)
	
	##on Ice movement
	if(on_ice):
		if direction:
			velocity.x = move_toward(velocity.x, direction*SPEED, acceleration*delta)
		else:
			velocity.x = move_toward(velocity.x, 0, deceleration * delta)

If you can see a better way to put this code together, and there must be, then your help would be greatly appreciated.

Regards.

if is_on_ice() == false:
		if direction:
			velocity.x =  move_toward(velocity.x, SPEED * direction, ACCELERATION * delta)
			
		else:
			velocity.x = lerpf(velocity.x, SPEED * direction, 10 * delta)
	if is_on_ice() == true:
		
		if direction:
			velocity.x = lerpf(velocity.x, SPEED * direction, 1 * delta)
		else:
			velocity.x = lerpf(velocity.x, SPEED * direction, 2 * delta)```
this is how i handled the movement, I am no expert but I think i can recommend you looking into lerp()/lerpf() for handling 'icy' movements. Hope this helps :)
2 Likes

Thankyou for the reply :grinning:.

I try it out and see how it goes. Mind you I am not completely unhappy how it is working. But it can always be better.

Regards.

I have used lerp before in Unreal for opening doors and bringing down bridges over moats in 3D before and it worked great, so, when you brought it up I was fully expecting this to work for the icy surface. But after putting in the code the character still sped onto the ice at full tilt without the ice any taking affect until I took my finger off the left movement key - to say I was annoyed with that was an understatement, it should work. I played around with the Ice scene for a long time trying to get it to work.

Then I said dammit, I’ll make the velocity.x equal zero when I hit the ice and see if that works, using a bool value and then turn it off again - the result was jarring, but, finally I had some control over the “hero” when entering the ice so I started playing around with the value (even going backward at one point not taking into account the direction) and got it to seem plausible. Then the lerp code worked fine.

Thankyou for posting.