I'm trying to implement a directional dash for a first person character controller. For those familiar with Unreal Tournament you know what I'm talking about. If I double tap a directional key I want the player to "dash" in that direction. The dash direction is relative to your facing (in my case head_basis) so if you double tap your backwards key you will dash backward. Also if you're holding down move forwards and double tap either left or right, you'll dash forward + left or right on a 45 degree. The dash is essentially 8 directional. It is also only done on x and z axis so looking up and down will not effect the movement. It could possibly be done as a vector2?

So that describes the effect I'm trying to achieve. The change in velocity needs to be similar to how my jump code is implement (maybe?). My velocity vector3 is broken down into velocity_y and velocity_xz so my jump code is "velocity_y.y = jump_value". So how can I apply something like this to my x and z axis movement relative to my head_basis? var head_basis = head.get_global_transform().basis var direction = Vector3() var velocity_xz = Vector3(velocity.x, 0, velocity.z) var velocity_y = Vector3(0, velocity.y, 0)

my movement code looks like this if Input.is_action_pressed("Move_Forward"): direction -= head_basis.z

And at the bottom direction = direction.normalized() velocity_xz = velocity_xz.linear_interpolate(direction * run_speed, run_acceleration * delta) velocity_y.y -= gravity * delta velocity = velocity_xz + velocity_y velocity = move_and_slide(velocity, Vector3.UP)

I have had many ideas on how to get this working but I can't quite figure it out. What I think I need to do is know what axis i'm dashing on relative to "direction and head_basis". I can't just modify my run speeds, I need to do something like "velocity_xz.x = dash_power". Any ideas?

Well you'll need a dash state, that way the movement is handled special for a short period of time. Get the dash direction vector from the controller and then transform in by the head basis. You can use xform.

https://docs.godotengine.org/en/3.2/classes/class_basis.html#class-basis-method-xform

That will give you the new dash direction in the space of the player. Then you can normalize it and multiply by the dash speed. Use a timer for 1 or 2 seconds, and on timeout switch the state back to normal and the dash will stop.

Thanks for the reply. I already have the timers and input part taken care of. I'm not sure what you mean by

@cybereality said: Get the dash direction vector from the controller and then transform in by the head basis.

What do you mean by controller?

I meant controller as in an Xbox gamepad, or could be a keyboard and mouse, whatever the player is using to control the game.

Still can't figure it out. Not sure if they way i'm trying is even the right way to implement this. Any suggestions on where to look?

var dash_dir = head_basis.xform(direction) when dashing dash_dir *= dash_speed

this doesn't work but am I on the right track?

The code is a bit of a mess, but the jam version of Dreams of Fire has a simple dash mechanic for the 3D character. The relevant bit of code is below:

# starting at line 99 of Player_3D.gd
if Input.is_action_just_pressed("Sprint"):
	if can_dash == true:
		is_dashing = true;
		dash_direction = dir;
		can_dash = false;
		dash_timer = 0;
		play_animation("Dash", -1, 3);

And:

# starting at line 153 of Player_3D.gd
dash_timer += delta;
if (dash_timer <= DASH_TIME):
	if (is_on_wall() == false):
		
		if (dash_direction.x == 0 and dash_direction.z == 0):
			dash_direction = -camera.global_transform.basis.z.normalized();
		
		dash_direction.normalized();
		
		vel = dash_direction * MAX_SPEED * 3;
		vel.y = 0;
		
		get_node("Scene Root").rotation_degrees.y = camera_holder_y.rotation_degrees.y;
		
else:
	is_dashing = false;
	play_animation("Jump_Mid_Air", 0.2, 1);

vel = move_and_slide(vel, Vector3(0,1,0), 0.05, 4, deg2rad(MAX_SLOPE_ANGLE))

I handled it a different way, as I didn't know how xform worked at the time. It might be helpful to look at as a reference. The entire project should still work with Godot 3.2, though it was designed for Godot 3.1 (I believe), so if you want to see it in action you can probably run the project. The code is hastily written though, as I made it for a game jam. Hopefully it can still be of some use though!

it looks like your code is for a sprint feature? Temporary increase in run speed? Not quite what i'm going for.

Not quite. It is for a quick dash in the direction the character is facing. However, when on the ground, pressing the shift key will cause the character to sprint rather than dash. Or at least, I think that's what I was trying to achieve when I programmed it. It's been around a year since I've last looked at the project.

I'll go double check and make sure what I wrote above is accurate.

extends KinematicBody

var run_speed = 25
var run_acceleration = 4#6
var run_decceleration = 6
var gravity = 90
var jump_power = 22
var dash_speed = 100
var mouse_sensitivty = 0.3
var ground_contact = false

onready var head = $Head
onready var camera = $Head/Camera

var velocity = Vector3()
var camera_x_rotation = 0
var dash_count = 0
var is_dash = false
var may_dash = true
	
func _physics_process(delta):
	var head_basis = head.get_global_transform().basis
	var direction = Vector3()
	var velocity_xz = Vector3(velocity.x, 0, velocity.z)
	var velocity_y = Vector3(0, velocity.y, 0)
	var dash_dir = head_basis.xform(direction)
	
	#run
	if Input.is_action_pressed("Move_Forward"):
		direction -= head_basis.z
	elif Input.is_action_pressed("Move_Backward"):
		direction += head_basis.z
	#dash
	if Input.is_action_just_pressed("Move_Forward"):
		$DashTimer.start()
		dash_count += 1
	elif Input.is_action_just_pressed("Move_Backward"):
		$DashTimer.start()
		dash_count += 1
	#run
	if Input.is_action_pressed("Move_Left"):
		direction -= head_basis.x
	elif Input.is_action_pressed("Move_Right"):
		direction += head_basis.x
	#dash
	if Input.is_action_just_pressed("Move_Left"):
		$DashTimer.start()
		dash_count += 1
	elif Input.is_action_just_pressed("Move_Right"):
		$DashTimer.start()
		dash_count += 1
		
	if ground_contact and Input.is_action_just_pressed("Jump"):
		velocity_y.y = jump_power
		
	if not direction.dot(velocity_xz) > 0:
		run_acceleration = run_decceleration
	
	#Dash
	if ground_contact and may_dash and dash_count >= 2:
		$DashCooldown.start()
		is_dash = true
		velocity_y.y = jump_power
		#dash_dir *= dash_speed
		may_dash = false
		
	print(dash_dir)
		
	direction = direction.normalized()
	velocity_xz = velocity_xz.linear_interpolate(direction * run_speed, run_acceleration * delta)
	velocity_y.y -= gravity * delta
	velocity = velocity_xz + velocity_y
	velocity = move_and_slide(velocity, Vector3.UP)
	
	
func _on_DashTimer_timeout():
	dash_count = 0
	is_dash = false
	pass
	
func _on_DashCooldown_timeout():
	may_dash = true
	pass

I deleted stuff I that was not relevant. Currently my dash/dodge isn't "done" so it doesn't check that you press the same direction twice but rather you can just hit any two direction keys twice to dodge, ignore that. Just trying to apply increase in velocity to correct x and z direction. Also dashing will give a less powerful jump at the same time.

@TwistedTwigleg said: Not quite. It is for a quick dash in the direction the character is facing.

I need to know where I'm facing but because that's how my regular movement is handled but in my case you can dash backwards while looking forwards.

This is what is supposed to happen when you push shift while in the air:

It's just a small dash. In Godot 3.0, the dash itself was not quite as pronounced, but I guess something with the physics changed since.

@ps15 said:

@TwistedTwigleg said: Not quite. It is for a quick dash in the direction the character is facing.

I need to know where I'm facing but because that's how my regular movement is handled but in my case you can dash backwards while looking forwards.

It shouldn't be an issue, you just need to change dash_dir according to the direction the player is intended to move towards. Looking at your code, I think I can figure out what needs to be done. Give me a second to look through the code and I'll see what I can do :+1:

Okay, I think I may have gotten it. I have not tested it, but I think the following should work. I added a few notes around what I changed, explaining what it does and/or why I changed it. If I understand the situation correctly, the code should now allow you to dash in all four directions (relative to the rotation of the player) and you can dash in a different direction than the one the player is facing.

extends KinematicBody

var run_speed = 25
var run_acceleration = 4#6
var run_decceleration = 6
var gravity = 90
var jump_power = 22
var dash_speed = 100
var mouse_sensitivty = 0.3
var ground_contact = false

onready var head = $Head
onready var camera = $Head/Camera

var velocity = Vector3()
var camera_x_rotation = 0
var dash_count = 0
var is_dash = false
var may_dash = true

# For performance reasons, we probably want to store dash_direction
# outside of the _physics_process function. Additionally, it will make it
# easier to make the player unable to change directions while dashing
# in the future, if you choose to implement it.
var dash_direction = Vector3.ZERO
	
func _physics_process(delta):
	var head_basis = head.get_global_transform().basis
	var direction = Vector3()
	var velocity_xz = Vector3(velocity.x, 0, velocity.z)
	var velocity_y = Vector3(0, velocity.y, 0)
	
	# if we have a node that is the direction the head is looking at, we can
	# instead use it directly to get the direction using basis.x, basis.y, and basis.z
	# (which will correspond to the directions of the red, green, and blue arrows
	# in the Godot editor when "local space", the box icon, is selected)
	
	#run
	if Input.is_action_pressed("Move_Forward"):
		direction -= head_basis.z
	elif Input.is_action_pressed("Move_Backward"):
		direction += head_basis.z
	#dash
	# (minor note: with this code, the player will dash whenever they move. Also,
	# you may want to check if the player is already dashing before staring a new dash.)
	if Input.is_action_just_pressed("Move_Forward"):
		$DashTimer.start()
		dash_count += 1
		# set the dash direction to so it points forward
		# (also, we optionally normalize so the scale of the head
		# node does not affect the dash speed)
		dash_direction = head_basis.z.normalized()
	elif Input.is_action_just_pressed("Move_Backward"):
		$DashTimer.start()
		dash_count += 1
		# set the dash direction to so it points backward
		dash_direction = -head_basis.z.normalized()
	#run
	if Input.is_action_pressed("Move_Left"):
		direction -= head_basis.x
	elif Input.is_action_pressed("Move_Right"):
		direction += head_basis.x
	
	#dash
	if Input.is_action_just_pressed("Move_Left"):
		$DashTimer.start()
		dash_count += 1
		# set the dash direction to so it points backward
		dash_direction = -head_basis.x.normalized()
	elif Input.is_action_just_pressed("Move_Right"):
		$DashTimer.start()
		dash_count += 1
		# set the dash direction to so it points backward
		dash_direction = head_basis.x.normalized()
		
	if ground_contact and Input.is_action_just_pressed("Jump"):
		velocity_y.y = jump_power
		
	if not direction.dot(velocity_xz) > 0:
		run_acceleration = run_decceleration
	
	#Dash
	if ground_contact and may_dash and dash_count >= 2:
		$DashCooldown.start()
		is_dash = true
		velocity_y.y = jump_power
		#dash_dir *= dash_speed
		may_dash = false
		
	print(dash_dir)
	
	# if we are dashing, then set the velocity to the dash direction!
	if (is_dash == true):
		# Set the X and Z velocity to the direction of the dash
		velocity.x = dash_direction.x * dash_speed
		velocity.z = dash_direction.z * dash_speed
	
	direction = direction.normalized()
	velocity_xz = velocity_xz.linear_interpolate(direction * run_speed, run_acceleration * delta)
	velocity_y.y -= gravity * delta
	velocity = velocity_xz + velocity_y
	velocity = move_and_slide(velocity, Vector3.UP)
	
	
func _on_DashTimer_timeout():
	dash_count = 0
	is_dash = false
	# optional: reset dash_direction
	dash_direction = Vector3.ZERO
	pass
	
func _on_DashCooldown_timeout():
	may_dash = true
	pass

Again, I have not tested it, but I think that should do it. At very least, hopefully it is somewhat helpful :smile:

Thanks for assistance, I don't have time to check it now either (bed time). Thank you again for the willingness to help.

I checked your script and it doesn't give errors but also doesn't seem to apply velocity. I'm going to mess around with it for a bit. Also should dash_direction = head_basis.z.normalized() under dash forward be -head_basis since forward is minus on z axis?

@ps15 said: Also should dash_direction = head_basis.z.normalized() under dash forward be -head_basis since forward is minus on z axis?

Yeah, if that is the direction the head faces. I just accidentally used the wrong value :smile:

If you replace velocity.x = dash_direction.x * dash_speed velocity.z = dash_direction.z * dash_speed with var velocity_xz = Vector3(velocity.x, 0, velocity.z) var velocity_y = Vector3(0, velocity.y, 0) then it seems to work but I haven't investigated in fully. Explain why velocity_xz.x .z works but velocity.x and .z does not?

@ps15 said: If you replace velocity.x = dash_direction.x * dash_speed velocity.z = dash_direction.z * dash_speed with var velocity_xz = Vector3(velocity.x, 0, velocity.z) var velocity_y = Vector3(0, velocity.y, 0) then it seems to work but I haven't investigated in fully. Explain why velocity_xz.x .z works but velocity.x and .z does not?

Sorry posted that wrong, replace with

velocity_xz.x = dash_direction.x * dash_speed velocity_xz.z = dash_direction.z * dash_speed

@ps15 said:

@ps15 said: If you replace velocity.x = dash_direction.x * dash_speed velocity.z = dash_direction.z * dash_speed with var velocity_xz = Vector3(velocity.x, 0, velocity.z) var velocity_y = Vector3(0, velocity.y, 0) then it seems to work but I haven't investigated in fully. Explain why velocity_xz.x .z works but velocity.x and .z does not?

Sorry posted that wrong, replace with

velocity_xz.x = dash_direction.x * dash_speed velocity_xz.z = dash_direction.z * dash_speed

Ah, That makes sense! In hindsight, I should have noticed that. velocity_xz is the horizontal velocity of your 3D character, and because you are setting the velocity to the horizontal velocity in line 101 (velocity = velocity_xz + velocity_y), modifying the X and Z axes in straight velocity will not do anything, because it will be overriden by velocity_xz later in the code.

If you were not manually separating the velocity to velocity_xz and velocity_y, manually setting the velocity like in the initial snippet for dashing would work, but because the velocity is deconstructed and then reconstructed. To make the dash work you need to modify velocity_xz, so when it is reconstructed it gets added in.