简体   繁体   中英

Ultimate struggle with a full 3d space controller

Sorry if i'm stupid or something, but i having a deep dread from a work on a "full 3d" space movement.

I'm trying to make a "space ship" KinematicBody controller which using basis vectors as a rotation point and have ability to strafe/move left,right,up,down based on it's facing direction.

The issue is i'm having that i want to use a Vector3 as a storage of all input variables, an input strength in particular, but i can't find a convenient way to orient or use this vector's variables to apply it to velocity.

I got a sort of cheap solution which i don't like with applying a rotation to an input vector so it will "corresponds" to one of the basis, but it's starting to brake at some angels.

Could please somebody suggest what i can change in my logic or maybe there is a way to use quaternion/matrix related methods/formulas?

I'm not sure I fully understand what you want to do, but I can give you something to work with.

I'll assume that you already have the input as a Vector3 . If not, you want to see Input.get_action_strength , Input.get_axis and Input.get_vector .

I'm also assuming that the braking situations you encountered are a case of gimbal lock . But since you are asking about applying velocity not rotation, I'll not go into that topic.

Since you are using a KinematicBody , I suppose you would be using move_and_slide or similar method, which work in global space. But you want the input to have to be based on the current orientation. Thus, you would consider your Vector3 which represents the input to be in local space. And the issue is how to go from that local space to the global space that move_and_slide et.al. need.


Transform

You might be familiar with to_local and to_global . Which would interpret the Vector3 as a position:

var global_input_vector:Vector3 = to_global(input_vector)

And the opposite operation would be:

input_vector = to_local(global_input_vector)

The problem with these is that since these consider the Vector3 to be positions, they will translate the vector depending where the KinematicBody is. We can undo that translation:

var global_vec:Vector3 = to_global(local_vec) - global_transform.orign

And the opposite operation would be:

local_vec = to_local(global_vec + global_transform.orign)

By the way this is another way to write the same code:

var global_vec:Vector3 = (global_transform * local_vec) - global_transform.orign

And the opposite operation would be:

local_vec = global_transform.affine_inverse() * (global_vec + global_transform.orign)

Which I'm mentioning because I want you to see the similarity with the following approach.


Basis

I would rather not consider the Vector3 to be positions. Just free vectors. So, we would transform it with only the Basis , like this:

var global_vec:Vector3 = global_transform.basis * local_vec

And the opposite operation would be:

local_vec = global_transform.affine_inverse().basis * global_vec

This approach will not have the translation problem.

You can think of the Basis as a 3 by 3 matrix, and the Transform is that same matrix augmented with a translation vector ( origin ).


Quat

However, if you only want rotation, let us se quaternions instead:

var global_vec:Vector3 = global_transform.basis.get_rotation_quat() * local_vec

And the opposite operation would be:

local_vec = global_transform.affine_inverse().basis.get_rotation_quat() * global_vec

Well, actually, let us invert just the quaternion:

local_vec = global_transform.basis.get_rotation_quat().inverse() * global_vec

These will only rotate the vector (no scaling, or any other transformation, just rotation) according to the current orientation of the KinematicBody .


Rotating a Transform

If you are trying to rotate a Transform, either…

Do this (quaternion):

transform = Transform(transform.basis * Basis(quaternion), transform.origin)

Or this (quaternion):

transform = transform * Transform(Basis(quaternion), Vector3.ZERO)

Or this (axis-angle):

transform = Transform(transform.basis.rotated(axis, angle), transform.origin)

Or this (axis-angle):

transform = transform * Transform.Identity.rotated(axis, angle)

Or this (Euler angles):

transform = Transform(transform.basis * Basis(pitch, yaw, roll), transform.origin)

Or this (Euler angles):

transform = transform * Transform(Basis(pitch, yaw, roll), Vector3.ZERO)

Avoid this:

transform = transform.rotated(axis, angle)

The reason is that this rotation is always before translation (ie this rotates around the global origin instead of the current position), and you will end up with an undesirable result.

And yes, you could use rotate_x , rotate_y and rotate_z , or set rotation of a Spatial . But sometimes you need to work with a Transform directly.


See also:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM