GetPoint() in Ray but with Padding and Angle - c#

This might be an odd question, but as fairly new to Unity I don't know how to go about my problem, but I find the Ray() class as one of the most useful classes for all kinds of work. Anyhow, to compute a given point of a Ray it's easy to call .GetPoint(distance). - But is there a way to do a call like .GetPoint(distance, padding, angle)?
For instance, given the (3D) Ray from a to b
a--c----b
|
d
A call to .GetPoint(3) would return c, and a call to the wanted/new method .GetPoint(3, 2, 0) should return d. Further, calling .GetPoint(3, 2, 90) should return d when it's behind (or above) c.
I guess I should have paid more attention in math class...

If you don't mind Unity finding an arbitrary starting point, you can use Vector3.OrthoNormalize to get a starting point for you.
Then, you can use Quaternion.AngleAxis to rotate the point around the ray's direction (you have to offset the ray to/from the origin for the rotation operation).
Vector3 GetPaddedPoint(Ray ray, float distance, float padding, float angleInDegrees)
{
Vector3 rayDirection = ray.direction;
Vector3 startingOrtho;
Vector3.OrthoNormalize(ref rayDirection, ref startingOrtho);
// Find some point padding from ray at distance from origin
Vector3 axisPoint = ray.GetPoint(distance);
Vector3 startingPoint = padding * startingOrtho+ axisPoint;
// Find where startingPoint would be if the origin of the ray was at the origin
Vector offsetPoint = startingPoint - ray.origin;
// Rotate the offsetPoint around ray direction using Quaternion
Quaternion rotation = Quaternion.AngleAxis(angleInDegrees, rayDirection);
Vector3 rotatedOffsetPoint = rotation * offsetPoint;
// Add back in the ray's origin
return rotatedOffsetPoint + ray.origin;
}
If you find that you prefer a particular kind of direction for a starting point, you can pass in a startingOrtho. Keep the OrthoNormalize call to ensure that it startingOrtho becomes orthogonal to the ray and normalized if it isn't already.
Vector3 GetPaddedPoint(Ray ray, float distance, float padding, float angleInDegrees, Vector3 startingOrtho)
{
Vector3 rayDirection = ray.direction;
Vector3.OrthoNormalize(ref rayDirection, ref startingOrtho);
// Find some point padding from ray at distance from origin
Vector3 axisPoint = ray.GetPoint(distance);
Vector3 startingPoint = padding * startingOrtho+ axisPoint;
// Find where startingPoint would be if the origin of the ray was at the origin
Vector offsetPoint = startingPoint - ray.origin;
// Rotate the offsetPoint around ray direction using Quaternion
Quaternion rotation = Quaternion.AngleAxis(angleInDegrees, rayDirection);
Vector3 rotatedOffsetPoint = rotation * offsetPoint;
// Add back in the ray's origin
return rotatedOffsetPoint + ray.origin;
}

Related

Find out camera world width and height?

So here is my problem in a nutshell:
I want to know how spawn an object in the middle of the screen, which means I want find the screen's width and height values in World units.
Camera Position: 0,23,-10
Camera Rotation: 60,0,0
So I have tried this:
Vector3 screenWorldSize = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, -10));
// In the Update function
If(Input.GetKeyDown(Keycode.P))
{
GameObject tempObject = Instantiate(gamePrefab);
tempObject.transform.position = new Vector3(screenWorldSize.x, 1.5f, 10f);
}
But the tempObject position is not correct (Not in the middle), So I tried to Debug.Log(screenWorldSize) and I get this result (-10.3, 28.8, -20.0)
I tried using Camera.main.ViewPortToWorldPoint() with (0,0,-10) and (1,0,-10) and I got almost the same result.
Here is an image of the scene..
Is there something that I don't understand?
How to get the screen edges in World points?
Very simple. You don't need the width and height at all, you just need the direction.
// this is a vector matching the camera's direction
Vector3 cameraDirection = Camera.main.transform.rotation * Vector3.forward;
// this is the camera position
Vector3 cameraPosition = Camera.main.transform.position;
If you want to just create the object a certain distance from the camera, you can do this to get its position
Vector3 position = cameraPosition + cameraDirection * distance;
If you want to spawn the object on another object pointed at by the camera, you need this:
RaycastHit hit;
if (Physics.Raycast(cameraPosition, cameraDirection, out hit))
{
Vector3 position = hit.point;
[..]
}

How do I make an object rotate based on another object?

I have a cog that the user can turn to rotate a drawbridge. Currently I have the cog and the drawbridge rotating at the same rate, like so: https://gyazo.com/14426947599095c30ace94a046e9ca21
Here is my current code:
[SerializeField] private Rigidbody2D thingToRotate;
void OnMouseDrag()
{
Vector3 mousePosition = Input.mousePosition;
mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);
Vector2 direction = new Vector2(
mousePosition.x - transform.position.x,
mousePosition.y - transform.position.y
);
transform.right = direction;
thingToRotate.transform.up = transform.right;
}
I want it so that when the user turns the cog it only turns the object a little bit, so the user can turn the cog a few times before the drawbridges closes.
I've tried adding to the drawbridges euler angle. I've tried setting the drawbridges rotation to the cog rotation and dividing that rotation by 2.
Don't set fixed orientations but use the proper methods instead.
Mathf.SignedAngle to determine the angle difference between current transform.right and the direction
If using RigidBody2D use Rigidbody2D.MoveRotation instead of setting the rotation through the Transform component.
Then I would store the totalAngle that was rotated in order to e.g. invoke some event when enough rotation was done.
The thingToRotate you simply rotate only to totalAngle / factor.
// Adjust in the Inspector how often the cog thing has to be turned
// in order to make the thingToRotate perform a full 360° rotation
public float factor = 5f;
private float totalAngle = 0f;
[SerializeField] private Rigidbody2D thingToRotate;
private void OnMouseDrag()
{
var mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// this is shorter ;)
Vector2 direction = (mousePosition - transform.position).normalized;
// get the angle difference you will move
var angle = Vector2.SignedAngle(transform.right, direction);
// rotate yourselve correctly
transform.Rotate(Vector3.forward * angle);
// add the rotated amount to the totalangle
totalAngle += angle;
// for rigidBodies rather use MoveRotation instead of setting values through
// the Transform component
thingToRotate.MoveRotation(totalAngle / factor);
}

Player rotation and camera rotation

i want the player to look into the direction, the camera is looking.
The camera follows the player (3rd person game style).
I've tried it with
transform.localRotation = new Quaternion(transform.localRotation.x,
cameraMain.transform.localRotation.y,
transform.localRotation.z,
transform.localRotation.w);
but it doesn't work.
Sometimes the player starts rotating the other direction.
the following code will make the object (specified in the parameter) face in the direction of where the main camera is looking:
public void lookInDirectionOfCamera(Transform object) {
RayCastHit hit;
if (Physics.raycast(cameraMain.transform.position, cameraMain.transform.forward, out hit)) {
object.forward = hit.point - object.position;
}else { //if the raycast didnt hit anything, make the object face 100 units in front of the camera
Vector3 point = Camera.main.transform.position + Camera.main.transform.forward * 100;
object.forward = point - object.position;
}
}
This will make the player face the point that is forward to the camera. If you just want them to have the same rotation in the y-axis don't use Quaternions!
Instead, you can just do it with Euler angles:
transform.eulerAngles = new Vector3(transform.eulerAngles.x,
cameraMain.transform.eulerAngles.y,
transform.eulerAngles.y);
The reason not to use transform.localRotation is because that is a Quaternion. The y component in a Quaternion is not the same as the y-axis in a Euler angle (what you are used to seeing), Quaternions are very confusing so you should almost never set individual values in them. If you want to edit them only use the built-in methods.
Get the direction the camera is looking with cameraMain.transform.forward, make a copy with a y value of zero, then use Quaternion.SetLookRotation to look in that direction with the global up direction.:
Vector3 cameraDirection = cameraMain.transform.forward;
Vector3 characterLookDirection = new Vector3(cameraDirection.x,
0f,
cameraDirection.z);
Quaternion newRotation = new Quaternion();
newRotation.SetLookRotation(characterLookDirection, Vector3.up);
transform.rotation = newRotation;

How Quaternion gets multiplied with a vector?

I am working on a third person shooter. And I have found this code. But I cant make no sense with it. First it is multiplying Quaternion with "Vector3.forward" and compiler is showing nothing. And also can you make me clear about the main logic of this code. I know memorizing the code is not a good habit. So can you explain me the code. And what does that Quaternion.euler does, is that for changing euler to quaternion.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollow : MonoBehaviour {
[SerializeField]
Transform target;
[SerializeField]
float distance;
[SerializeField]
float targetheight;
private float x = 0;
private float y = 0;
void LateUpdate()
{
y = target.eulerAngles.y;
Quaternion rotation = Quaternion.Euler(x, y, 0);
Debug.Log(rotation);
transform.rotation = rotation;
var postion = target.position - (rotation *Vector3.forward* distance + new Vector3(0, -targetheight, 0));
transform.position = postion;
}
}
Let's break it down:
y = target.eulerAngles.y;
This is Transform.eulerAngles() and gives you the current rotation of the target as Quaternion.
Then that rotation is reapplied to the target, but with the rotation around the Z-axis removed.
Quaternion rotation = Quaternion.Euler(x, y, 0);
Debug.Log(rotation);
transform.rotation = rotation;
This is Quaternion.Euler(x,y,z) and gives you a rotation from three degree values.
Last is the following calculation:
var postion = target.position - (rotation *Vector3.forward* distance + new Vector3(0, -targetheight, 0));
transform.position = postion;
Multiplying a Vector3 by a Quaternion means to apply the rotation to the vector. So in this case you'd rotate the forward vector based on the rotation of the target (without the Z-axis rotation, since we set that to 0 earlier). Then that resulting forward vector is multiplied by a certain distance to give us a point somewhere in front of the target's current facing direction and lowered according to the targetheight variable.
Lastly that calculation is subtracted from our own current position, basically performing a point reflection with the target being the point. The result is that we actually end up with a position that has a positive height (originally we were using a negative targetheight) and are behind the target's facing direction.

XNA c# Lerping -- Make 3d object follow another 3d object

I am making a game where an enemy AI follows a player 3d object. However, my enemy object spasms sometimes when following the player and freaks out. I think my issue may be somewhere in my dot product or my cross product math. Here is my code from my enemy class:
//here I get the direction vector in between where I am pointing and where my enemy is located at
Vector3 midVector = Vector3.Normalize(Vector3.Lerp(Vector3.Normalize(world.Forward), Vector3.Normalize(Vector3.Subtract(p.position,this.position)), angleVelocity));
//here I get the vector perpendicular to this middle vector and my forward vector
Vector3 perp=Vector3.Normalize(Vector3.Cross(midVector, Vector3.Normalize(world.Forward)));
//here I am looking at my quaternion and I am trying to rotate it about the axis (my perp vector) with an angle that I determine which is in between where I am facing and the midVector
float angle=(float)Math.Acos(Vector3.Dot(world.Forward,midVector));
//Console.WriteLine(angle);
Quaternion quaternion2 = Quaternion.CreateFromAxisAngle(perp, angle);
quaternion = quaternion * quaternion2;
//here I am simply scaling my world matrix, implementing my quaternion, and translating it to my position
world = Matrix.CreateScale(scale) * Matrix.CreateFromQuaternion(quaternion) * Matrix.CreateTranslation(position);
//here i move myself forward in the direciton that I am facing
MoveForward(ref position, quaternion, velocity);
}
private void MoveForward(ref Vector3 position, Quaternion rotationQuat, float speed)
{
Vector3 addVector = Vector3.Transform(new Vector3(0, 0, -1), rotationQuat);
position += addVector * speed;
}
public override void update()
{
base.update();
}
}

Categories