How to orient player properly in water properly like Ori ?
Asked Answered
G

12

1

Hi everyone ! I'm currently working on a 2d game and i would like to add a swimming logic similar to the one found in Ori and the will of the wisps. But i encounter big issues about flipping the character in water and orient the player upward while swimming in opposite direction. I've recorded what i want to achieve :

ori.zip
2MB

Can you help me please ?

Grooms answered 5/12, 2023 at 12:13 Comment(0)
S
1

Grooms

This code is an excerpt of the player CharacterBody2D script from one of my projects. It worked well enough with an analog controller for what I used it for but it can surely be optimised.

var swim_speed: float = 250.0
var swim_acceleration: float = 0.1
var water_resistance: float = 0.1

func _physics_process(delta):
       ##Get player input. Make sure you set these in the input maps
	var input_direction: Vector2 = Vector2(Input.get_axis("Left","Right"), Input.get_axis("Up","Down")).normalized()

	if input_direction.x > 0:
		input_direction.x = 1
	elif input_direction.x < 0:
		input_direction.x = -1

	if input_direction.length() > 0:
		## Add 75 degrees to accommodate for player rotation head to be in front
		var angle = input_direction.angle() + deg_to_rad(75)

                 ##Rotate agent to face input direction
		rotation = lerp_angle(rotation, angle, 0.2)

                ##Smoothly move player along the input direction with a slight acceleration
		velocity = lerp(velocity, input_direction * swim_speed, swim_acceleration)

		##Turn and flip agent depending on input direction
		if input_direction.length_squared() >= 1 and !is_input_direction_equal_agent_direction(input_direction.x):
			scale.x = input_direction.x
	else:
                ##If no input decelerate player movement
		velocity = velocity.lerp(Vector2.ZERO, water_resistance)

	set_velocity(velocity)
	move_and_slide()

func is_input_direction_equal_agent_direction(x_input_dir) -> bool:
        ##Check if player is currently facing input direction
	if x_input_dir == scale.x:
		return true
	else:
		return false
Statampere answered 5/12, 2023 at 18:1 Comment(0)
I
0

Grooms Can you show us your current movement code and explain (or show) what you already got working?

Inch answered 5/12, 2023 at 12:49 Comment(0)
S
1

Grooms

This code is an excerpt of the player CharacterBody2D script from one of my projects. It worked well enough with an analog controller for what I used it for but it can surely be optimised.

var swim_speed: float = 250.0
var swim_acceleration: float = 0.1
var water_resistance: float = 0.1

func _physics_process(delta):
       ##Get player input. Make sure you set these in the input maps
	var input_direction: Vector2 = Vector2(Input.get_axis("Left","Right"), Input.get_axis("Up","Down")).normalized()

	if input_direction.x > 0:
		input_direction.x = 1
	elif input_direction.x < 0:
		input_direction.x = -1

	if input_direction.length() > 0:
		## Add 75 degrees to accommodate for player rotation head to be in front
		var angle = input_direction.angle() + deg_to_rad(75)

                 ##Rotate agent to face input direction
		rotation = lerp_angle(rotation, angle, 0.2)

                ##Smoothly move player along the input direction with a slight acceleration
		velocity = lerp(velocity, input_direction * swim_speed, swim_acceleration)

		##Turn and flip agent depending on input direction
		if input_direction.length_squared() >= 1 and !is_input_direction_equal_agent_direction(input_direction.x):
			scale.x = input_direction.x
	else:
                ##If no input decelerate player movement
		velocity = velocity.lerp(Vector2.ZERO, water_resistance)

	set_velocity(velocity)
	move_and_slide()

func is_input_direction_equal_agent_direction(x_input_dir) -> bool:
        ##Check if player is currently facing input direction
	if x_input_dir == scale.x:
		return true
	else:
		return false
Statampere answered 5/12, 2023 at 18:1 Comment(0)
G
0

Thanks, i'm going to try this and i'll tell you if it works !

Grooms answered 5/12, 2023 at 18:48 Comment(0)
G
1

Inch
I'm currently using a hierarchical state machine
Here is the code and the result :


demo.zip
515kB
Grooms answered 5/12, 2023 at 20:10 Comment(0)
S
0

Grooms
Looks good. Happy to be of help. You can tweak with the rotation a bit so that it will always face the movement direction.

Statampere answered 6/12, 2023 at 9:24 Comment(0)
G
0

Statampere
Thanks !

Grooms answered 6/12, 2023 at 11:31 Comment(0)
G
0

Statampere
I've currently imlemented the code you've shown and it works quite well, but i've an issue.
When i'm swimming upward or downward, without applying any horizontal input the sprite disappears. Can you help me please ?

Grooms answered 8/12, 2023 at 15:10 Comment(0)
S
0

Grooms I'm not sure what the cause of this is unless I see your code.

Statampere answered 8/12, 2023 at 17:3 Comment(0)
G
0

Statampere
Here it is :


Grooms answered 8/12, 2023 at 21:40 Comment(0)
S
0

I'm not sure what's happening. You will need to investigate it. You should try logging the sprite positions to see what is happening.

Statampere answered 9/12, 2023 at 7:18 Comment(0)
G
0

Statampere
OK, i'm gonna try it

Grooms answered 9/12, 2023 at 8:46 Comment(0)
G
0

Statampere

I have finally achieved completing swimming movement :

class_name WaterState

extends PlayerMovementState

var input_direction := Vector2.ZERO
var is_swimming_fast := false
var exit_water := false

func enter():
	if player.facing_direction == -1:
		player.flip_controller(1)
		player.sprite.scale.x = -1
	elif player.facing_direction == 1:
		player.flip_controller(1)
		player.sprite.scale.x = 1
		

func update(_delta):
	if player.is_on_surface && (INPUT.JUMP || is_swimming_fast):
		switch_state("JumpPlayerState")
		
	set_swim_facing_direction(player.swim_facing_dir)
	
	
func physics_update(delta):
	set_anims()
	player.handle_surface(delta)
	
	set_input_normalized(delta)	
	
		
func exit():
	player.sprite.rotation = 0.0
	player.sprite.scale.x = 1
	is_swimming_fast = false
	exit_water = true


func set_swim_facing_direction(input: float) -> void:
	if input > 0 && player.velocity.x < 0:
		player.swim_facing_dir *= -1
	elif input < 0 && player.velocity.x > 0:
		player.swim_facing_dir *= -1


func set_anims():
	if (player.is_on_surface || player.can_jump_out_of_water()) && INPUT.X == 0:
		play_anim("swim_surf_idle")
	elif (player.is_on_surface || player.can_jump_out_of_water()) && INPUT.X != 0:
		play_anim("swim_surf")
	elif player.is_in_water() && INPUT.X == 0:
		play_anim("swim_idle")
	elif player.is_in_water() && INPUT.X != 0:
		play_anim("swim")


func set_input_normalized(delta : float):
	input_direction = Vector2(Input.get_axis("run_l", "run_r"), Input.get_axis("climb", "crouch")).normalized()	
	
	if input_direction.x > 0:
		input_direction.x = 1
	elif input_direction.x < 0:
		input_direction.x = -1
		
	if input_direction.length() > 0:
		var angle = input_direction.angle() + deg_to_rad(90.0)
		if !(player.is_on_surface || player.can_jump_out_of_water()):
			player.sprite.rotation = lerp_angle(player.sprite.rotation, angle, 0.1)
			
			if input_direction.length_squared() >= 1 and !is_input_direction_equal_agent_direction(input_direction.x):
				player.sprite.scale.x = player.swim_facing_dir
		else:
			player.sprite.scale.x = player.swim_facing_dir
			player.sprite.rotation = lerp_angle(player.sprite.rotation, 0.0, 0.4)

		set_swim_velocity(delta)	
	else:
		player.velocity = lerp(Vector2.ZERO, player.velocity, pow(2, -player.data.swim_control * delta))
		
	
func set_swim_velocity(delta : float):
	if player.is_on_surface || player.can_jump_out_of_water():
		player.apply_horizontal_velocity(player.data.swim_speed, delta)
		if INPUT.Y >= 0:
			player.reset_vertical_velocity()
		elif INPUT.Y < 0:
			player.velocity = lerp(player.velocity, input_direction * Data.swim_speed, Data.swim_accel * delta)
	elif player.is_in_water():
		if INPUT.SPRINT:
			is_swimming_fast = true
			player.velocity = lerp(player.velocity, input_direction * Data.swim_speed * 2, Data.swim_accel * delta)
		elif !INPUT.SPRINT || INPUT.SPRINT_END:
			is_swimming_fast = false
			player.velocity = lerp(player.velocity, input_direction * Data.swim_speed, Data.swim_accel * delta)
	
		
func is_input_direction_equal_agent_direction(x_input_dir) -> bool:
	##Check if player is currently facing input direction
	if x_input_dir == player.sprite.scale.x:
		return true
	else:
		return false
Grooms answered 21/12, 2023 at 22:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.