I’m trying to make a rotation controls similar to Worms games, but I need help. Basically I just need a simple bug-free aim solution where you only use directional keys (like arrows or WASD): pressing up/down rotates aim within 180 degree vertical angle and pressing left/right flips horizontally to the other side.
I’m planning to make mouse implementation too, but after the core mechanics with keys is working I can continue from there. I’ve been trying to do this for years, but everytime I just get messed up with degrees, radians or whatsoever. I suck at maths.
My current aim node implementation is below. It’s messy and buggy, a guide book example of how to NOT do it. Do you know any better approach?
Summary
@export var aim_speed: float = 6.0
var flip_h: bool = true
var aim_direction: float = 0.0:
set(value):
aim_direction = value
aimed.emit(aim_direction)
get:
return wrapf(aim_direction,-90.0,270.0)
signal aimed(direction)
func side(check: int) -> void:
var prev_side = flip_h
if check > 0: flip_h = true
elif check < 0: flip_h = false
if prev_side != flip_h: aim_direction = 180 - aim_direction
func aim(axis: float, direction: float, speed_factor: float = 3.0) -> void:
var d: float = axis * (aim_speed / speed_factor)
if d != 0.0: #if pressed either up or down
if !flip_h: aim_direction = clamp(aim_direction - d,90,270)
elif aim_direction <= 90: aim_direction = clamp(aim_direction+d,-90,90)
elif aim_direction >= 270: aim_direction = clamp(aim_direction+d,270,450)
Here’s a suggestion, probably not the cleaner solution, but when people say they struggle with maths, I tend to suggest using the engine tools directly instead of hard coding some maths you’ll have trouble iterating on.
So, you could split up your character tree into multiple nodes, like this:
Aim_Flip is centered, and its scale will be used to flip the aim.
Aim_Pivot_Rotation is also centered, and its rotation will be used to adjust the angle in a 180° cone (it does not need to handle flip, as its parent will do it for him).
Weapon is just a placeholder sprite to show the result.
The code I wrote is attached on Aim_Flip and looks like this (please note that I wrote it pretty quickly so there may be some obvious improvements left):
aim(), that handles up/down keys and clamps rotation between -90 and 90. I’m using rotation_degrees, since you don’t like maths, I guessed degrees were easier to understand than radians.
flip(), that handles left/right keys to scale the weapon.
Once both functions are called, you can access the weapon global rotation to get the current aim, and do whatever you need to do with it (fire bullets, display a preview curve, etc.).
Let me know if that works for you and if you need more explanations/a different suggestion.
This works perfectly, thank you! I implemented the idea quite heavily to my architecture where the aim code is already in a separate aimer module. Anyway, that’s great idea to limit rotation always within 180 degrees and handle the rest with flipping. When I tried to use the whole 360 degrees, it always had these weird edge cases where you had to guess when the degree wraps take in place: at 0, -90, 270, 360 or somewhere else? Between -90 and 90 there’s less hassle to deal with. Now I wish I had came up with this simple trick by myself