Bizarre bug with Debug.DrawLine and transform.forward

Hello. I have a very bizarre bug with Debug.DrawLine and transform.forward. While transform.forward works perfectly with AddForce(transform.forward * 15);, sending the cube forward, when added to Debug.DrawLine(transform.position, transform.forward, Color.red); the ray terminates one step forward from Vector3.zero instead. Which is to say, it paints a line a mile across the map to a fixed point that does not move with the cube.

This is very consistent, and works with an empty script on a blank cube.

Any ideas? This is a basically blank project, and I’m not setting transform.forward to anything.

Please check the documentation before posting:

It expects two positions, not a position and a direction.

2 Likes

Debug.DrawLine != Debug.DrawRay.

1 Like

I was expecting transform.forward to be a position one step in front of the target, and was acting based on the Debug.Drawline documentation. I guess I didn’t understand the way forward was working, but I did read both.

It’s a direction. It’s agnostic to any position. You can add them together to get what you expected.

Unity - Scripting API: Transform.forward
Returns a normalized vector representing the blue axis of the transform in world space.

A normalized vector only really makes sense as a direction. Picture a compass, you only need a position on the circumference of the compass to know which direction to go in. It’s the same here. A normalized vector has a length of 1.

2 Likes
static public void DrawDir(Vector3 position, Vector3 direction, float length, Color color) {
  Debug.DrawLine(position, position + length * direction, color);
}

Thank you. You wouldn’t know how to offset that by (float) degrees from centre would you?

When you say offset do you mean like rotate that vector around position?
If that’s the case in 2D, then you start with a 2D vector, in this case it’s just direction.
And you rotate it by doing

static public Vector2 rotateVector(Vector2 v, float rad) {
  var p = polar(rad);
  return new( p.x * v.x - p.y * v.y, p.y * v.x + p.x * v.y );
}

Where polar function returns new Vector2(MathF.Cos(rad), MathF.Sin(rad)) and the angle is in radians (hence rad).

If you want degrees, you can easily convert degrees to radians by multiplying with {\pi} and dividing by {180^{\circ}}, which is effectively the same as multiplying with {\pi}/{180^{\circ}} (conveniently at your disposal via Mathf.Deg2Rad constant), so you can call it like so

var rotated = rotateVector(direction, angleDegrees * Mathf.Deg2Rad);

In 3D, the problem is slightly harder, because rotation doesn’t mean much unless you can specify the axis of rotation. If you just want a rotation around a cardinal axis, you can make it yourself by doing either Quaternion.Euler(x, y, z) (insert your values) or Quaternion.AngleAxis(angle, axis) where axis is simply a direction vector like Vector3.up (or transform.up). Unity API assumes angles are in degrees.

Then applying this rotation is as simple as

static public Vector3 rotateVector(Vector3 v, Vector3 axis, float rad)
  => Quaternion.AngleAxis(rad * Mathf.Rad2Deg, axis) * v;

or

static public Vector3 rotateVector(Vector3 v, Vector3 axis, float angleDegrees)
  => Quaternion.AngleAxis(angleDegrees, axis) * v;

This is because multiplying Quaternion with a Vector3 practically rotates that vector around the origin. So you need now to offset (aka translate) this vector by some position if this was supposed to happen around that position. Do note that this works only when quaternion is on the left side of the multiplication, as you cannot multiply vector with a quaternion, that has no mathematical meaning (mathematically the operation is non-commutative).

I can also show you how to build this from scratch (i.e. without using AngleAxis and quaternion-vector multiplication), but I will spare you from technicalities in case you wanted something else instead.

Again, always make sure that your directions (that includes normals, axes atc) are of unit length (length of 1; so called normalized) or else you’ll get weird results.

In the end, a more complete rotation extension function might look like this

/// <summary> Rotates a 3D point around an arbitrary axis (going through center). (In radians.) </summary>
static public Vector3 rotate(this Vector3 point, float rad, Vector3 axis, Vector3 center = default)
  => mad(angleAxis(rad, axis), point - center, center);

Here mad stands for a compound multiply-add operation so this can be rewritten as

/// <summary> Rotates a 3D point around an arbitrary axis (going through center). (In degrees.) </summary>
static public Vector3 rotate(this Vector3 point, float degrees, Vector3 axis, Vector3 center = default)
  => ( Quaternion.AngleAxis(degrees, axis) * (point - center) ) + center;

This technically shifts the vector from the world space to a rotation space (centered around 0,0), applies the rotation, then shifts it back.

When rotating, it doesn’t matter how long the vector is (unless it’s beyond the optimal values for computing with 32-bit floating points, but that’s a different concern).

1 Like

Wow that seems really complicated to offset the angle of the ray by a few degrees. I can achieve a rough version by just adding transform.whateverDirection to forward. I just can’t get an exact angle.

Well, if you want to rotate a vector, then you want to rotate a vector.
There is exactly one reasonable way to do it, whether you like it or not.

Please don’t mistake the verbosity of my post with whatever “complicated” means, when I can distill the above to just three lines of code. I just can’t tell what exactly do you need, your descriptions are incredibly vague.

1 Like

There’s a few ways to rotate a vector. The methods below will all rotate transform.forward upwards by 10 degrees.

Vector3 v = Quaternion.AngleAxis(-10, transform.right) * transform.forward;
Vector3 v = Vector3.RotateTowards(transform.forward, transform.up, 10 * Mathf.Deg2Rad, 0);
Vector3 v = Vector3.Slerp(transform.forward, transform.up, 10f / 90f);
1 Like

Thanks a lot! That really helped!