Gun turret Aim at player with only 8 rotating directions

Godot Version

4.2

Question

I have a turret that can only rotate in 8 directions (eg. every 45degrees). How can I get it to rotate to the direction corresponding with the “zone” that the player is in?

Also, how to make turret smoothly rotate between those angles as well.

Thank you!

Example scenario:
A 3D top down shooter. P = player T= turret. A turret is facing Vector3.FORWARD and rotate on Vector3.UP

Code used to rotate:

if Input.is_action_just_pressed(“left”):
degree = 45
if Input.is_action_just_pressed(“right”):
degree = -45
transform.basis = transform.basis.rotated(axis, deg_to_rad(degree))


here is a 2D version of the function that you might want

func _process(delta):
	var diff = to_local(aimpointer.position).angle_to_point(to_local(player.position))
	if diff > -2.5 && diff <0:
		aimpointer.rotate(deg_to_rad(45))
	if diff < 2.5 && diff >0:
		aimpointer.rotate(deg_to_rad(-45))

	turret.rotation_degrees = move_toward(turret.rotation_degrees, aimpointer.rotation_degrees, 0.5)
2 Likes

The video is exactly what I want to achieve but I am struggling to convert it to 3D world. Especially this:

var diff = to_local(aimpointer.position).angle_to_point(to_local(player.position))

  1. Where is aimpointer.position located?
  2. angle_to_point is not available in Vector3. I tried angle_to and signed_angle_to but it produces same angles on both side of turret, which results in turret spinning wildly.
  1. [aimpointer] can be a node3D sitting in the middle of the turret
  2. We only use x and z since y is high and we don’t need to use it in your case. angle_to is for vector so if you want to use it you have to convert your player position into a vector ( player.position - turret.position )

First, create a node3D in the middle of the turret


We will use this as a pointer point to where the turret should be (the turret needs to catch up)
Then add this code in and replace all the placeholders. I will modify it for 3D

func _process(delta):
	var diff = Vector2(to_local(node3D.position).x,to_local(node3D.position).z).angle_to_point(Vector2(to_local(player.position).x,to_local(player.position).z))
	if diff > -2.5 && diff <0:
		node3D.rotate_y(deg_to_rad(45))
	if diff < 2.5 && diff >0:
		node3D.rotate_y(deg_to_rad(-45))

	turret.rotation_degrees.y = move_toward(turret.rotation_degrees.y, node3D.rotation_degrees.y, 0.5)

I haven’t tested it yet so it might break

It is still spinning wildly. Using aboved code what’s happening is that when player is in Zone A the turret is stationary. If the player go to Zone B the turret is spinning wildly. I find that when player is in Zone B the turret keeps rotating between Zone A and B as if it can’t decide which way to rotate to.

The code is in the world’s script.


@onready var player = $Player
@onready var turret = $Area3D/Turret
@onready var node3D = $Area3D/Node3D

func _process(delta):
	var diff = Vector2(to_local(node3D.position).x,to_local(node3D.position).z).angle_to_point(Vector2(to_local(player.position).x,to_local(player.position).z))
	print(diff)
	if diff > -2.5 && diff < 0.0 :
		print("ZONE A")
		node3D.rotate_y(deg_to_rad(0))
	if diff < 2.5 && diff > 0.0:
		print("BBB")
		node3D.rotate_y(deg_to_rad(1))
	turret.rotation_degrees.y = move_toward(turret.rotation_degrees.y, node3D.rotation_degrees.y, 0.5)

@onready var player = $Player
@onready var turret = $Area3D/Turret
@onready var node3D = $Area3D/Node3D

func _process(delta):
	var diff = rad_to_deg(Vector2(to_local(node3D.position).x,to_local(node3D.position).z).angle_to_point(Vector2(to_local(player.position).x,to_local(player.position).z))) #let turn rad into deg for easy visualization
	print(diff)
	if (diff >= 45 && diff <= -90) : #2,3,4
		node3D.rotate_y(deg_to_rad(45)) #45deg is for the 8 sections of the aim pointer
	if (diff =< 90 && diff >= -180) || (diff >= 180 && diff <= 135): #5,6,7
		node3D.rotate_y(deg_to_rad(-45))
	turret.rotation_degrees.y = move_toward(turret.rotation_degrees.y, node3D.rotation_degrees.y, 0.5) #0.5 deg is the turning speed of the turret

my turret was originally pointed to the left

It’s still not working for me. The turret (Node3D) keeps spinning once the game starts. I instanced my turret node at world center, pointing toward -z. and the player at z - 3.
Node3D (pink block) is a child in turret node that keeps spinning.

Screenshot 2024-07-01 145259

Is it possible for me to have a strip-down version of the scene

https://file.io/4Yx9halMJXLY

Here is the minimal project. Thanks for having a look.

Hey thanks for showing me useful suggestions.
In the end I use look_at and brute force it. Using rotate_toward also gives me the shortest rotation to.
I’d still love to see a more elegant way to do this though.

func _process(delta):
	var node3d_rotation = node3D.global_rotation_degrees.y
	var rotate_target = 0
	node3D.look_at(player.global_transform.origin,Vector3.UP)
	if (node3d_rotation < 25 and node3d_rotation > -25):
		rotate_target = 0
	elif node3d_rotation > 25 and node3d_rotation < 75 :
		rotate_target = PI/4
	elif node3d_rotation > 75 and node3d_rotation < 112 :
		rotate_target = PI/2
	etc...
	turret.rotation.y = rotate_toward(turret.rotation.y, rotate_target, 2 * delta)