Getting to the point: How can I set up an object so that it cannot rotate more than a given measurement in a frame on all axis in 3D?

Adding context: I'm trying to design a homing missle (albeit I think that this question is applicable to many sorts of moving objects). I can design a missle that can track its target perfectly simply by setting look_at every frame, but this results in something that is not very much fun because it cannot be dodged.

Edit: Also, to clarify, in the specific case, I'm working with a KinematicBody, albeit I reckon that this question could also apply to RigidBodies

How can I give it a maximum per-frame turning rate?

Godot version 3.2.3
in Engine
edited

You did not specify whether your missile is a physical object. I planned to answer a Force applied answer so wait for whether current answer is accepted or not?

The answer provided at the moment seems to leave out the part I was actually asking, so I'd certainly appreciate your input. Also, to answer your question, I'm using a kinematic body, but I imagine that this question also applies to rigidbodies

You'll need to code your own custom method for this, `look_at` does what it says on the tin and snaps the obj to the target on the negative z.

Several ways to do this. You could take the target's vector and instead of setting the missile's vector directly as `look_at` does you could lerp towards it. Linear interpolation is a bit on the advanced side though if you're a beginner. So I'd recommend just comparing the direction to the target against your heading and rotating the obj accordingly checking each frame how aligned they are.

Getting the unit vector to the target is done with `target_vector`minus `missile_vector`and then normalising the result (bringing the values between zero and one, so just considering the direction, not the scale). This:

``````var target_vec = (target_obj.global_transform.origin-missile_obj.global_transform.origin).normalized()
``````

``````var missile_heading = -missile_obj.global_transform.origin.z.normalized()
``````

If you don't know, dot product is a calculation where if two unit vectors are aligned then it outputs 1 and if they're at right angles you get zero. Opposite way outputs -1. This way you can make the missile lose track in a realistic way if it's outside a "viewing cone". You'll need to take orthogonal dot products to establish the bearing (the dot product says they're not aligned, it doesn't say which way) and then you can just `rotate_object_local` the missile according to these values with a maximum rotation set.

Finally, you just need to set a member variable with your max turn rate and put that in your `rotate_object_local`call so that it never turns but more than however many radians you set.

Dot product is about the most useful thing in games so it's well worth looking at a few tutorials.

Hope that helps.

by (2,156 points)

Why is `var missile_heading = -missile_obj.global_transform.origin.z.normalized()` the missile heading? And what is this `z` .. I'm a noob regarding global_transform still ;-)

Ah, the wonderful world of linear algebra. :)

Let's do 2d to keep it simple: imagine you have something at Vector2(2, 0) and you add [move it by] Vector2(2, 1). You get Vector2(4, 1). Try to imagine this on graph paper with the missile moving.

Well, in this case you're just doing this working backwards: your missile is at Vector2(2, 0), the target is at Vector(4, 1). What you want is the vector arrow pointing from the object to the target.

Just minus the target from the object. Vector2(4, 1)-Vector2(2, 0) = Vector2(2, 1). Exactly the same maths as before.

Now, you just want the heading so you normalise it: Vector2(2, 1).normalized(). Dot product won't work as expected if things aren't normalised. So, now we know the target is in the positive x direction and a bit on the y direction. (print this to console to see what the actual result is)

Btw, there's a video course on maths for game devs on humble bundle for 1 EUR atm which covers this. I recommend it.

+1 vote

Using a `VehicleBody` I came with `Rocket.gd`

``````extends VehicleBody

var speed = 100.0

func _physics_process(delta):
var front:Vector3 = front_of_rocket.global_transform.origin
var me:Vector3 = global_transform.origin
var him:Vector3 = target.global_transform.origin

var dir:Vector3 = (him - me).normalized()
var rot_towards:Vector3 = (him - front).normalized()
var front_dir:Vector3 = (front - me).normalized()

# We want to rotate towards the target
var torque = -rot_towards.cross(front_dir)

# Make sure angular_velocity has damping ~ 1.0

# Engine is always pushing the rocket forward
# Make sure lineair_velocity has some damping
``````

The Scene tree looks like

``````Spatial
Rocket: VehicleBody
CollisionShape
Cylinder
CSGSphere
Target
``````

The torque force is normalised and the used multiplier/limiter is dependent on `angular_velocity damping`. Same goes for `linear_velocity damping`. Experimental I used the values below.

``````func _ready():
angular_damp = 1.0
linear_damp = 0.2
``````
by (642 points)

Changing the mass from 40kg to 10 kg helps too. I'm still puzzled about how to fly the rocket when `Gravity Scale <> 0.0`?