c sharp samples for using 3D transforms

This commit is contained in:
Kelly thomas
2018-04-23 01:32:49 +08:00
parent 5339a43652
commit bf817ddfc3

View File

@@ -95,7 +95,8 @@ A transform has a :ref:`class_Basis` (transform.basis sub-property), which consi
A default basis (unmodified) is akin to:
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
var basis = Basis()
# Contains the following default values:
@@ -103,6 +104,23 @@ A default basis (unmodified) is akin to:
basis.y = Vector3(0, 1, 0) # Vector pointing along the Y axis
basis.z = Vector3(0, 0, 1) # Vector pointing along the Z axis
.. code-tab:: csharp
// Due to technical limitations on structs in C# the default
// constructor will contain zero values for all fields.
var defaultBasis = new Basis();
GD.Print(defaultBasis); // prints: ((0, 0, 0), (0, 0, 0), (0, 0, 0))
// Instead we can use the Identity property.
var identityBasis = Basis.Identity;
GD.Print(identityBasis.x); // prints: (1, 0, 0)
GD.Print(identityBasis.y); // prints: (0, 1, 0)
GD.Print(identityBasis.z); // prints: (0, 0, 1)
// The Identity basis is equivalent to:
var basis = new Basis(Vector3.Right, Vector3.Up, Vector3.Back);
GD.Print(basis); // prints: ((1, 0, 0), (0, 1, 0), (0, 0, 1))
This is also an analog to a 3x3 identity matrix.
Following the OpenGL convention, ``X`` is the *Right* axis, ``Y`` is the *Up* axis and ``Z`` is the *Forward* axis.
@@ -129,34 +147,53 @@ Of course, transforms are not as straightforward to manipulate as angles and hav
It is possible to rotate a transform, either by multiplying its basis by another (this is called accumulation), or by using the rotation methods.
.. tabs::
.. code-tab:: gdscript GDScript
.. code-block:: python
# Rotate the transform in X axis
# Rotate the transform about the X axis
transform.basis = Basis(Vector3(1, 0, 0), PI) * transform.basis
# shortened
transform.basis = transform.basis.rotated(Vector3(1, 0, 0), PI)
.. code-tab:: csharp
// rotate the transform about the X axis
transform.basis = new Basis(Vector3.Right, Mathf.Pi) * transform.basis;
// shortened
transform.basis = transform.basis.Rotated(Vector3.Right, Mathf.Pi);
A method in Spatial simplifies this:
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
# Rotate the transform in X axis
rotate(Vector3(1, 0, 0), PI)
# shortened
rotate_x(PI)
.. code-tab:: csharp
// Rotate the transform about the X axis
Rotate(Vector3.Right, Mathf.Pi);
// shortened
RotateX(Mathf.Pi);
This rotates the node relative to the parent node.
To rotate relative to object space (the node's own transform) use the following:
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
# Rotate locally, notice multiplication order is inverted
transform = transform * Basis(Vector3(1, 0, 0), PI)
# shortened
# Rotate locally
rotate_object_local(Vector3(1, 0, 0), PI)
.. code-tab:: csharp
// Rotate locally
RotateObjectLocal(Vector3.Right, Mathf.Pi);
Precision Errors
================
@@ -166,19 +203,29 @@ If a transform is rotated every frame, it will eventually start deforming over t
There are two different ways to handle this. The first is to *orthonormalize* the transform after some time (maybe once per frame if you modify it every frame):
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
transform = transform.orthonormalized()
.. code-tab:: csharp
transform = transform.Orthonormalized();
This will make all axes have ``1.0`` length again and be ``90`` degrees from each other. However, any scale applied to the transform will be lost.
It is recommended you don't scale nodes that are going to be manipulated. Scale their children nodes instead (such as MeshInstance). If you absolutely must scale the node, then re-apply it at the end:
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
transform = transform.orthonormalized()
transform = transform.scaled(scale)
.. code-tab:: csharp
transform = transform.Orthonormalized();
transform = transform.Scaled(scale);
Obtaining Information
=====================
@@ -187,31 +234,57 @@ You might be thinking at this point: **"Ok, but how do I get angles from a trans
Imagine you need to shoot a bullet in the direction your player is facing. Just use the forward axis (commonly ``Z`` or ``-Z``).
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
bullet.transform = transform
bullet.speed = transform.basis.z * BULLET_SPEED
.. code-tab:: csharp
bullet.Transform = transform;
bullet.LinearVelocity = transform.basis.z * BulletSpeed;
Is the enemy looking at the player? Use the dot product for this (see the :ref:`doc_vector_math` tutorial for an explanation of the dot product):
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
# Get the direction vector from player to enemy
var direction = enemy.transform.origin - player.transform.origin
if direction.dot(enemy.transform.basis.z) > 0:
enemy.im_watching_you(player)
enemy.im_watching_you(player)
.. code-tab:: csharp
// Get the direction vector from player to enemy
Vector3 direction = enemy.Transform.origin - player.Transform.origin;
if (direction.Dot(enemy.Transform.basis.z) > 0)
{
enemy.ImWatchingYou(player);
}
Strafe left:
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
# Remember that +X is right
if Input.is_action_pressed("strafe_left"):
translate_object_local(-transform.basis.x)
translate_object_local(-transform.basis.x)
.. code-tab:: csharp
// Remember that +X is right
if (Input.IsActionPressed("strafe_left"))
{
TranslateObjectLocal(-Transform.basis.x);
}
Jump:
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
# Keep in mind Y is up-axis
if Input.is_action_just_pressed("jump"):
@@ -219,6 +292,15 @@ Jump:
velocity = move_and_slide(velocity)
.. code-tab:: csharp
// Keep in mind Y is up-axis
if (Input.IsActionJustPressed("jump"))
{
velocity.y = JumpSpeed;
}
velocity = MoveAndSlide(velocity);
All common behaviors and logic can be done with just vectors.
Setting Information
@@ -230,22 +312,47 @@ For such cases, keep the angles and rotations *outside* the transform and set th
Example of looking around, FPS style:
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
# accumulators
var rot_x = 0
var rot_y = 0
func _input(event):
if event is InputEventMouseMotion and ev.button_mask & 1:
if event is InputEventMouseMotion and event.button_mask & 1:
# modify accumulated mouse rotation
rot_x += ev.relative.x * LOOKAROUND_SPEED
rot_y += ev.relative.y * LOOKAROUND_SPEED
rot_x += event.relative.x * LOOKAROUND_SPEED
rot_y += event.relative.y * LOOKAROUND_SPEED
transform.basis = Basis() # reset rotation
rotate_object_local(Vector3(0, 1, 0), rot_x) # first rotate in Y
rotate_object_local(Vector3(1, 0, 0), rot_y) # then rotate in X
.. code-tab:: csharp
// accumulators
private float _rotationX = 0f;
private float _rotationY = 0f;
public override void _Input(InputEvent @event)
{
var mouseMotion = @event as InputEventMouseMotion;
if (mouseMotion != null)
{
// modify accumulated mouse rotation
_rotationX += mouseMotion.Relative.x * LookAroundSpeed;
_rotationY += mouseMotion.Relative.y * LookAroundSpeed;
// reset rotation
Transform transform = Transform;
transform.basis = Basis.Identity;
Transform = transform;
RotateObjectLocal(Vector3.Up, _rotationX); // first rotate about Y
RotateObjectLocal(Vector3.Right, _rotationY); // then rotate about X
}
}
As you can see, in such cases it's even simpler to keep the rotation outside, then use the transform as the *final* orientation.
Interpolating with Quaternions
@@ -255,7 +362,8 @@ Interpolating between two transforms can efficiently be done with quaternions. M
Converting a rotation to quaternion is straightforward.
.. code-block:: python
.. tabs::
.. code-tab:: gdscript GDScript
# Convert basis to quaternion, keep in mind scale is lost
var a = Quat(transform.basis)
@@ -265,6 +373,16 @@ Converting a rotation to quaternion is straightforward.
# Apply back
transform.basis = Basis(c)
.. code-tab:: csharp
// Convert basis to quaternion, keep in mind scale is lost
var a = transform.basis.Quat();
var b = transform.basis.Quat();
// Interpolate using spherical-linear interpolation (SLERP).
var c = a.Slerp(b, 0.5f); // find halfway point between a and b
// Apply back
transform.basis = new Basis(c);
The :ref:`class_Quat` type reference has more information on the datatype (it can also do transform accumulation, transform points, etc. though this is used less often). If you interpolate or apply operations to quaternions many times, keep in mind they need to be eventually normalized or they also may suffer from numerical precision errors.
Quaternions are very useful when doing camera/path/etc. interpolations, as the result will be always correct and smooth.