Drawing a line in the direction a player is moving - unity3d

For debugging purposes I am trying to draw 2 debug lines. One in the direction that the character is facing and one in the direction that the character is moving.
I have the following function that is called in the update method.
void DrawDirectionLines()
{
var wishDir = transform.position + transform.forward;
var movementDir = Quaternion.LookRotation(rb.velocity).eulerAngles;
Debug.DrawLine(transform.position, transform.position + transform.forward * 5, Color.red);
Debug.DrawLine(transform.position, transform.position + movementDir * 5, Color.blue);
}
The red direction debug line works perfectly however the blue line representing the actual movement direction of the player seems to always misbehave and never points in the direction the player is moving in.
Does anybody know how I can fix this?

The problem is because your movemendDir is a Vector3 with the euler angles, and not a direction vector.
The good news is that rb.velocity is the direction you only need. The documentation says: Unity velocity also has the speed in X, Y, and Z defining the direction.
But if you use that velocity vector the lenght of the line will depends on the velocity. The solution is normalize that vector.
You need to replace:
var movementDir = Quaternion.LookRotation(rb.velocity).eulerAngles;
to:
var movementDir = rb.velocity.normalized;

Related

Unity 3D. How to find out where the movement of an object is directed relative to its rotation?

I need to make a 3d top down character controller and animate it.
To begin with, I made a code for the character's movement relative to the camera. And this code works, the character walks and turns well.
Vector3 inputDirection = new Vector3(_input.GetAxis.x, 0, _input.GetAxis.y);
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + _camera.rotation.eulerAngles.y;
float angle = Mathf.SmoothDampAngle(transformPlayer.eulerAngles.y, targetAngle, ref _turnVelocity, TurnSmoothTime);
transformPlayer.rotation = Quaternion.Euler(0f, angle, 0f);
Vector3 moveDirection = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
moveDirectionNormalized = moveDirection.normalized;
_characterPlayer.Move(moveDirectionNormalized * SpeedMultiplier * Time.deltaTime);
The character model is a child object of _characterPlayer.
And if, when the character moves, an enemy gets into his radius, then the player's model will turn towards the enemy, and the character himself will go further along the moveDirectionNormalized.
For turns, I wrote the following code:
if (NearestEnemyAtAttackRadius != null)
{
Quaternion rotation = Quaternion.LookRotation(NearestEnemyAtAttackRadius.transform.position - MeshCharacter.position);
MeshCharacter.rotation = Quaternion.RotateTowards(MeshCharacter.rotation, rotation, 800f * Time.deltaTime);
}
else
{
MeshCharacter.localRotation = Quaternion.RotateTowards(MeshCharacter.localRotation, Quaternion.Euler(Vector3.zero), 800f * Time.deltaTime);
}
Now if there is an enemy nearby, my character swings towards the enemy, and if there is no enemy or the enemy has left the radius, then the initial state is returned.
And here my problem begins. I wanted to add an animation of the movement. The character has 4 animations: movement with a tilt to the right, left, forward and backward.
In the Animator, I made a Blend Tree (2d simple direction) with 4 animations. Added 2 Float values MoveDirectionX, MoveDirectionY correctly configured for all motion pos x and pos y.
And if I am in MoveDirectionX, MoveDirectionY will feed the vector moveDirectionNormalized, then it does not work correctly. The values of moveDirectionNormalized do not depend on the repetition of my model in any way, and if my character moves away from the enemy, he looks at the enemy but in fact goes backwards, the animation should turn on as he leans back. But now moveDirectionNormalized does not depend on the repetition of my model, and the character tilt animations randomly switch depending on moveDirectionNormalized. As I understand it, it is necessary to create a new vector that will take into account the rotation of my model, but I do not understand how to do this.
As a result, I want to get a vector where x - shows where the character is moving to the left or right (from -1 to 1) and y - shows the movement forward, backward (also from -1 to 1), and this vector should take into account the rotation of the player. At the moment, in the character model, only y changes in rotation. moveDirectionNormalized returns a value that indicates where the movement is directed relative to world space. And I need to make a vector that will return directions relative to the player's rotation. That is, if the character's gaze is directed at the enemy, and the character himself retreats from him, I would like to receive a vector (0, -1). And if the player's gaze is directed at the enemy and he goes to the right (I remind you that the player is constantly looking towards the enemy, that is, you can walk around the enemy and the character will constantly turn in his direction), then I would like to get a vector (1,0)
I am not 100% sure I understand, but if I do, this should help:
You can use Quaternion.Inverse() on the rotation of your player, and multiply that by the vector.
Vector3 yourVector;
Quaternion inverse = Quaternion.Inverse([player rotation]);
return inverse * yourVector;
Multiplying a quaternion by a vector rotates it with the quaternion, so if you invert it it will cancel out the rotation of the player.

what is the purpose of multiplying Quaternion with Vector3 [duplicate]

Could someone please help me understand the result of the following multiplications?
In the Unity VR samples project, the following two lines are used:
Quaternion headRotation = InputTracking.GetLocalRotation(VRNode.Head);
TargetMarker.position = Camera.position + (headRotation * Vector3.forward) * DistanceFromCamera;
I can understand the first line - how the user's head rotation is calculated and stored in headRotation which is a Quaternion.
I can also understand that the TargetMarker's position should be calculated by adding the Camera's position to something. What is this something?
Most importantly, how does the result of (headRotation * Vector3.forward) * DistanceFromCamera is a position?
headRotation * Vector3.forward return a Vector3 in the direction forward of your Quaternion headRotation. (So the direction you are facing)
As Vector3.forward is the vector normalized (0, 0, 1) when you multiply it by your Quaternion you have a vector of length 1 with the same direction of your head.
Then when you multiply it by the distance between your marker and your camera you now have a vector of the same length and direction that between your camera and your marker.
Add it to your current camera position and you now have the position of your marker.

Rotate Object around point and move it along sine function

First off: I am very new to Unity, as in VERY new.
I want to do the following: I want to rotate a cube around a stationary point (in my case a camera) with a radius that is adjustable in the inspector. The cube should always have its Z-axis oriented towards the camera's position. While the cube is orbiting around the camera, it should additionally follow a sine function to move up and down with a magnitude of 2.
I have some working code, the only problem is an increase in distance over time. The longer the runtime, the higher the distance between the cube and the camera.
Here is what I currently have:
void Awake()
{
cameraPosition = GameObject.FindGameObjectWithTag("MainCamera").transform;
transform.position = new Vector3(x: transform.position.x,
y: transform.position.y,
z: cameraPosition.position.z + radius);
movement = transform.position;
}
I instantiate some variables in the Awake()-method and set the cube's position to where it should be (do you instantiate in Awake()?). I'll use the Vector3 movement later in my code for the "swinging" of the cube.
void Update()
{
transform.LookAt(cameraPosition);
transform.RotateAround(cameraPosition.position, cameraPosition.transform.up, 30 * Time.deltaTime * rotationSpeed);
MoveAndRotate();
}
Here I set the orientation of the cube's z-axis and rotate it around the camera. 30 is just a constant i am using for tests.
void MoveAndRotate()
{
movement += transform.right * Time.deltaTime * movementSpeed;
transform.position = movement + Vector3.up * Mathf.Sin(Time.time * frequency) * magnitude;
}
To be quite frank, I do not understand this bit of code completely. I do however understand that this includes a rotation as it moves the cube along it's x-axis as well as along the world's y-axis. I have yet to get into Vector and matrices, so if you could share your knowledge on that topic as well I'd be grateful for that.
It seems like I have found the solution for my problem, and it is an easy one at that.
First of all we need the initial position of our cube because we need to have access to its original y-coordinate to account for offsets.
So in Awake(), instead of
movement = transform.position;
We simply change it to
initialPosition = transform.position;
To have more readable code.
Next, we change our MoveAndRotate()-method to only be a single line long.
void MoveAndRotate()
{
transform.position = new Vector3(transform.position.x,
Mathf.Sin(Time.time * frequency) * magnitude + initialPosition.y,
transform.position.z);
}
What exactly does that line then? It sets the position of our cube to a new Vector3. This Vector consists of
its current x-value
our newly calculated y-value (our height, if you want to say so) + the offset from our original position
its current z value
With this, the cube will only bop up and down with distancing itself from the camera.
I have also found the reason for the increase in distance: My method of movement does not describe a sphere (which would keep the distance the same no matter how you rotate the cube) but rather a plane. Of course, moving the cube along a plane will automatically increase the distance for some points of the movement.
For instantiating variables in Awake it should be fine, but you could also do it in the Start(){} Method that Unity provides if you wanted to.
For the main problem itself I'm guessing that calling this function every frame is the Problem, because you add on to the position.
movement += transform.right * Time.deltaTime * movementSpeed;
Would be nice if you could try to replace it with this code and see if it helped.
movement = transform.right * Time.deltaTime * movementSpeed;

Rotate object to target, while being rotated to match the terrain slope

In the image above
the red vector is the spider's forward vector
the blue vector is the vector representing the direction between the spider and it's target
In the code below, orientation is a vector that's representing the normal of the terrain, so that the spider gets aligned to it:
Vector3 orientation = GetTerrainNormal();
Quaternion rotationNeeded = Quaternion.FromToRotation(Vector3.up, orientation);
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
rotationNeeded,
RotationSpeed * Time.deltaTime
);
My issue is that I cannot manage to make the spider face its target... When I add any code that would make it rotate towards it, then it's not aligned with the terrain's normals anymore, it says straight...
So basically, how can I make the spider rotate on the Y world axis (I think), while still then being rotated to match the slope?
Full answer
In case it helps someone else, here's the full answer:
Vector3 orientation = GetTerrainNormal();
Vector3 directionToTarget = (target.position - transform.position).Y(0);
float d = Vector3.Dot(directionToTarget, orientation);
directionToTarget -= d * orientation;
if (directionToTarget.sqrMagnitude > 0.00001f) {
directionToTarget.Normalize();
Quaternion rotationNeeded = Quaternion.LookRotation(directionToTarget, orientation);
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
rotationNeeded,
xRotationSpeed * Time.deltaTime
);
}
This answer on the unity forums was extremely helpful: https://forum.unity.com/threads/look-at-object-while-aligned-to-surface.515743/
Try this
Vector3 directionToTarget = target.transform.position - transform.position;
Quaternion rotationNeeded = Quaternion.LookRotation(directionToTarget, orientation);
First of all, I'm not sure why you need a code to orient the spider manually to the terrain. You can make the spider a Rigidbody and the Unity engine will take care of it for you.
Regardless, you want to rotate the spider around the local Y-Axis (this will keep the current orientation).
You can do this using transform.LookAt() (referring to the blue vector in the picture) (documented here) and passing the up vector as the 2nd argument.

Find point at distance along a vector

Given a known direction and range I'm trying to calculate the 3D position along the vector at that range from the starting point.
To do this I'm basically following the Unity manual and the solution offered in various other questions about this topic:
Take your direction, normalise it and multiply by the required
distance.
This isn't working for me but I can't figure out what's wrong. Here's my code that should draw a line from the starting point towards the mouse cursor position, ending at the given distance from the starting point:
IEnumerator DrawLineToMouse(float range)
{
RayCastHit hit;
Vector3 endPos;
float rangeSquared = Mathf.Sqrt(range);
while (Input.GetButton("Fire1"))
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 2000))
{
endPos = (hit.point - startPos).normalized * rangeSquared; // startPos is the starting point of the line to be drawn
Debug.DrawLine(startPos, hit.point, Color.green, Time.deltaTime);
Debug.DrawLine(startPos, endPos, Color.red, Time.deltaTime);
}
yield return null;
}
}
For some reason the direction seems off. In this picture the green Debug.Draw line is direct from the starting point to the mouse position, the red line is to the calculated vector:
I've tried both perspective and orthographic cameras, and I've tried varying the starting point, the problem is the same. I don't know what to try regarding the code because everything I've read suggests it should work.
What could be the problem?
Currently your endPos is at the vector distance and direction but starting from 0,0,0
Instead it should rather be
endPos = startPos + (hit.point - startPos).normalized * rangeSquared;
or for better understanding
var distance = rangeSquared;
var direction = (hit.point - startPos).normalized;
endPos = startPos + direction * distance;