Top-down 3d character controls

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

Hello. I’m a bit stuck with implementing character movement for my little project.

I want pretty much the same controls as in Overcooked game. When a player presses UP key, the game character moves up, when a player presses DOWN key – the character moves down the screen. And that is very easy to implement with global translation.

I can’t handle the character rotation though. For example at some particular moment character looks down. At this moment player decides to send the character UP. At the very beginning of the movement, I want the character to rotate to direction of movement – UP.

And I want it to be a smooth transition.

I did some monstrous code which looks pretty much like this:

extends Spatial
# Declare member variables here. Examples:
# var a = 2
# var b = "text"

var ROTATION_SPEED = 5
var MOVE_SPEED = 6

# Called when the node enters the scene tree for the first time.
func _process(delta):
	var dx = 0
	var dy = 0
	
	if Input.is_action_pressed("ui_left"):
		dx += 1
	if Input.is_action_pressed("ui_right"):
		dx -= 1
	if Input.is_action_pressed("ui_up"):
		dy += 1
	if Input.is_action_pressed("ui_down"):
		dy -= 1

	var direction = Vector3(dx, 0, dy) * delta
	translate(direction * MOVE_SPEED)
		
	# |--------|
	# |ROTATION|
	# |--------|
	
	if dx != 0 or dy != 0:
		var targetRot = 0
		var currentRot = fposmod($Rotation.rotation.y, 2 * PI)
		
		if currentRot > PI:
			currentRot = - (2 * PI - currentRot)

		if dx == 1:
			targetRot = 0
		if dx == -1:
			targetRot = PI
		if dy == 1:
			targetRot = -PI/2
		if dy == -1:
			targetRot = PI/2
			
		print(currentRot, ' -> ', targetRot)
		
		if abs(targetRot - currentRot) > PI / 360:
			var delRot = 1 if currentRot < targetRot else -1
			var minRot = min(currentRot, targetRot)
			var maxRot = max(currentRot, targetRot)
			$Rotation.rotation.y = clamp(
				currentRot + delta * delRot * ROTATION_SPEED,
				minRot,
				maxRot
			)

I believe – there’s a better and less buggy solution than mine.

If all else fails you could do it with a simple animation. Last resort, It would be better to do it in code IMHO.

Millard | 2020-05-28 02:21

I’m not really sure how to apply animation in this case. I need an angle to rotate. But I’m not sure how to efficiently calculate it.

shirokoff | 2020-05-28 10:23

https://forum.godotengine.org/67337/how-do-make-enemy-look-at-player-when-following-it?show=67337#q67337

here’s a link to a similar question, maybe it can help. :slight_smile: I think you can get the mouse position with get_global_mouse_position()

Millard | 2020-05-28 18:43

I guess I’ve stated the problem with a lack of details. I don’t want mouse to be used.

shirokoff | 2020-05-28 20:50

I think this video might be useful
https://youtu.be/CeNXDSkv5v8

Ogeeice | 2020-05-29 18:08

oh, sorry, I got confused between several questions. oops. :slight_smile: I’m a bit new to Godot, so I’m not positive how to do this. Hopefully someone else will know. :slight_smile:

Millard | 2020-05-29 18:22

Thank you for the help.

I finally figured out how to solve it. I remembered a CS problem of building a convex hull for a shape. And there you would use the fact that vector cross product will be negative or positive depending on what side of vector A lais point B.

var cross = faceDirection.cross(direction)
var s = 1 if cross.y > 0 else -1
var angle = faceDirection.angle_to(direction) * delta * TURNING_SPEED * s
rotate(Vector3(0, 1, 0), angle)
faceDirection = faceDirection.rotated(Vector3(0,1,0), angle)

shirokoff | 2020-05-30 19:14