How do I keep raycast relative to the object its bound to? - c#

I have a car game and I would like to use raycast to detect other obstacles and do stuff if the ray hits.
The problem is after the car does a 90 degree turn the raycast move in a way I did not intend them to, like this:
But when I rotate the car by 90 degrees it looks like this:
I'd like the rays to remain as they are in the first image regardless of the rotation of the car.
Here is the code to cast the rays and to draw them:
private void OnDrawGizmos()
{
sensorStartPosition1 = transform.position + new Vector3(1f, 0.6f, 0f);
sensorStartPosition2 = transform.position + new Vector3(-1f, 0.6f, 0f);
Gizmos.DrawRay(sensorStartPosition1, transform.TransformDirection(Vector3.right) * 3.5f);
Gizmos.DrawRay(sensorStartPosition2, transform.TransformDirection(Vector3.left) * 3.5f);
}
private void RunSensors()
{
RaycastHit hit1;
sensorStartPosition1 = transform.position + new Vector3(1f, 0.6f, 0);
//dTS = detect traffic signs
Ray dTS = new Ray(sensorStartPosition1, transform.TransformDirection(Vector3.right));
if(Physics.Raycast(dTS, out hit1, sensorLen ))
{
}
Debug.DrawRay(sensorStartPosition1, transform.TransformDirection(Vector3.right) * 3.5f);
RaycastHit hit2;
sensorStartPosition2 = transform.position + new Vector3(-1f, 0.6f, 0);
// dLIT1 = detect left incoming traffic
Ray dLIT1 = new Ray(sensorStartPosition2, transform.TransformDirection(Vector3.left));
if(Physics.Raycast(dLIT1, out hit2, sensorLen))
{
}
Debug.DrawRay(sensorStartPosition1, transform.TransformDirection(Vector3.left) * 3.5f);
}
what am I doing wrong?.. thanks in advance

You calculate the two start positions with an offset along the global world space X axis regardless of the rotation in
sensorStartPosition1 = transform.position + new Vector3(1f, 0.6f, 0f);
//...
sensorStartPosition2 = transform.position + new Vector3(-1f, 0.6f, 0f);
instead rather calculate them like
sensorStartPosition1 = transform.position + transform.right * 1f + transform.up * 0.6f;
//...
sensorStartPosition2 = transform.position + transform.right * -1f + transform.up * 0.6f;
Or simply
sensorStartPosition1 = transform.position + transform.rotation * new Vector3(1f, 0.6f, 0f);
//...
sensorStartPosition2 = transform.position + transform.rotation * new Vector3(-1f, 0.6f, 0f);
Btw instead of
transform.TransformDirection(Vector3.right)
transform.TransformDirection(Vector3.left)
you can also simply use
transform.right
-transform.right

Related

Unity 3D instantly rotate in the direction of movement

I have a somewhat similar question to this one, I'm trying to rotate in the direction of movement but the issue is that I want to maintain my position and just instantly rotate because I have a procedural maze and there's not enough space to gradually swing around in the direction of movement in a wide turn. I'll post code attempts at the bottom, attempt two gives the error: viewing vector is zero.
If you look at all four of these screenshots, when the y-axis is rotated, the x-axis and z-axis jump to a new position. When I'm moving this ends up looking like I'm teleporting around the maze. Anyone know how to rotate while remaining in the same position?
Here's right:
Here's forward:
Here's left:
Here's back:
here's code attempt 1:
public void playerMovement()
{
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
Vector3 movementx = new Vector3(horizontalInput, 0f, 0f);
Vector3 movementy = new Vector3(0f, 0f, verticalInput);
movementy = transform.forward * verticalInput;
movementx = transform.right * horizontalInput;
if (horizontalInput > 0)
{
transform.eulerAngles = new Vector3(0f, 90f, 0f);
transform.Translate(-movementx.normalized * 0.1f, Space.Self);
}
if (horizontalInput < 0)
{
transform.eulerAngles = new Vector3(0f, 270f, 0f);
transform.Translate(-movementx.normalized * 0.1f, Space.Self);
}
if (verticalInput < 0)
{
transform.eulerAngles = new Vector3(0f, 180f, 0f);
transform.Translate(movementy.normalized * 0.1f, Space.Self);
}
if (verticalInput > 0)
{
transform.eulerAngles = new Vector3(0f, 0f, 0f);
transform.Translate(movementy.normalized * 0.1f, Space.Self);
}
}
Here's code attempt 2:
public void playerMovement2()
{
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
movement = transform.TransformDirection(movement);
transform.Translate(movement.normalized * 0.1f, Space.Self);
transform.rotation = Quaternion.LookRotation(movement);
}
I took your second attempt and modified it:
This way you will walk and look in the direction you
void Update()
{
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
transform.Translate(movement.normalized * 0.1f, Space.World);
transform.rotation = Quaternion.LookRotation(movement, Vector3.up);
}
However then the character will always "flip" back to forward
-> you can solve that by only moving when new input is given
For example like this:
void Update()
{
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
if (movement.magnitude > 0f)
{
transform.Translate(movement.normalized * 0.1f, Space.World);
transform.rotation = Quaternion.LookRotation(movement, Vector3.up);
}
}

Unity C# - How to check if random moving gameobject is within boundaries

I have a random moving gameobject moving in the scene with the attached code. The issue is that although there are boundary colliders with a rigid body, sometimes it passes right through them and falls.
How can I limit it that once it bumps into those colliders it does a 189 rotation and continues moving randomly?
The terrain width is 50f, the terrain Height is also 50f.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerTrainingMovement : MonoBehaviour {
public float speed;
private float randomizedSpeed = 0.0f;
private float nextActionTime = -1.0f;
private Vector3 targetPosition;
private Vector3 moveVector;
private void FixedUpdate() {
if(speed > 0.0f) {
Move2();
//Move();
}
}
private void Move2() {
if(Time.fixedTime >= nextActionTime) {
// Get a threshold time
nextActionTime = UnityEngine.Random.Range(50.0f, 200.0f);
// Pick a random target position
targetPosition = TrainingArea.ChooseRandomPosition(transform.parent.position, 0f, 270f, 2f, 10f);
// Rotate towards the target position
transform.rotation = Quaternion.LookRotation(targetPosition - transform.position, Vector3.up);
float timeToGetThere = Vector3.Distance(transform.position, targetPosition) / randomizedSpeed;
nextActionTime = Time.fixedTime + timeToGetThere;
// Get a movement vector
moveVector = speed * transform.forward;
//moveVector = speed * new Vector3(1.0f, 0.0f, 0.0f);
}
else {
// Check -X boundary
if(transform.position.x - 1.0f <= transform.parent.gameObject.transform.position.x) {
transform.position = transform.position + new Vector3(1.0f, 0.0f, 0.0f);
moveVector.x *= -1.0f;
}
// Check +X boundary
if(transform.position.x + 1.0f >= transform.parent.gameObject.transform.position.x + TrainingArea.terrainWidth) {
transform.position = transform.position + new Vector3(-1.0f, 0.0f, 0.0f);
moveVector.x *= -1.0f;
}
// Check -Z boundary
if(transform.position.z - 1.0f <= transform.parent.gameObject.transform.position.z) {
transform.position = transform.position + new Vector3(0.0f, 0.0f, 1.0f);
moveVector.z *= -1.0f;
}
// Check +Z boundary
if(transform.position.z + 1.0f >= transform.parent.gameObject.transform.position.z + TrainingArea.terrainHeight) {
transform.position = transform.position + new Vector3(0.0f, 0.0f, -1.0f);
moveVector.z *= -1.0f;
}
// Move
transform.position = transform.position + moveVector * Time.fixedDeltaTime;
// Update nextActionTime
nextActionTime--;
}
}
}
public static Vector3 RandomPositionOnTerrain(Vector3 origin) {
//Range(30.0f, terrainWidth - 30.0f);
float randomX = origin.x + UnityEngine.Random.Range(20.0f, terrainWidth);
float randomZ = origin.z + UnityEngine.Random.Range(20.0f, terrainHeight);
return new Vector3(randomX, 30.0f, randomZ);
}
You can use OnCollisionEnter to trigger a rotation when the unit bumps into other colliders.
something like this must do it:
void OnCollisionEnter(Collision col)
{
if(col.gameObject.tag(or name) = "a tag or name"){
//Do Stuff here
}
}
Here's the detailed documentation:
https://docs.unity3d.com/ScriptReference/Collider.OnCollisionEnter.html
another workaround is using NavMesh. It'll make the movements UnityEngine.AI based and pretty smooth but you'll need a whole new script.

Unity 3D how to rotate an object so that its oriented correctly based on the ground plane?

I'm using an asset called Dreamteck Splines to create a path, what I'm trying to do is make it so when I rotate the spline the game object the pink cube in this case, also rotates so it's oriented correctly on the path whether its upside down or sideways like a roller coaster. For some reason, I can only rotate the path to about 90 degrees before the cube stops rotating to align itself to the path.
GIFF
if (Input.GetKey(KeyCode.W))
{
vehicle.transform.Translate(0, 0, speed * Time.deltaTime);
}
if (Physics.Raycast(vehicle.transform.position, Vector3.down, out hit, 100))
{
Vector3 surfaceNormal = hit.normal; // Assign the normal of the surface to surfaceNormal
Vector3 forwardRelativeToSurfaceNormal = Vector3.Cross(vehicle.transform.InverseTransformDirection(vehicle.transform.right), surfaceNormal);
Quaternion targetRotation = Quaternion.LookRotation(forwardRelativeToSurfaceNormal, surfaceNormal); //check For target Rotation.
vehicle.transform.rotation = Quaternion.Lerp(vehicle.transform.rotation, targetRotation, Time.deltaTime * 20); //Rotate Character accordingly.
}
I found this which does what I was trying to achieve:
void Update()
{
/*Here we get user input to calculate the speed the ship will get*/
if (Input.GetKey(KeyCode.W))
{
/*Increase our current speed only if it is not greater than fwd_max_speed*/
current_speed += (current_speed >= fwd_max_speed) ? 0f : fwd_accel * Time.deltaTime;
}
else
{
if (current_speed > 0)
{
/*The ship will slow down by itself if we dont accelerate*/
current_speed -= brake_speed * Time.deltaTime;
}
else
{
current_speed = 0f;
}
}
/*We get the user input and modifiy the direction the ship will face towards*/
yaw += turn_speed * Time.deltaTime * Input.GetAxis("Horizontal");
/*We want to save our current transform.up vector so we can smoothly change it later*/
prev_up = transform.up;
/*Now we set all angles to zero except for the Y which corresponds to the Yaw*/
transform.rotation = Quaternion.Euler(0, yaw, 0);
RaycastHit hit;
if (Physics.Raycast(transform.position, -prev_up, out hit))
{
Debug.DrawLine(transform.position, hit.point);
/*Here are the meat and potatoes: first we calculate the new up vector for the ship using lerp so that it is smoothed*/
Vector3 desired_up = Vector3.Lerp(prev_up, hit.normal, Time.deltaTime * pitch_smooth);
/*Then we get the angle that we have to rotate in quaternion format*/
Quaternion tilt = Quaternion.FromToRotation(transform.up, desired_up);
/*Now we apply it to the ship with the quaternion product property*/
transform.rotation = tilt * transform.rotation;
/*Smoothly adjust our height*/
smooth_y = Mathf.Lerp(smooth_y, hover_height - hit.distance, Time.deltaTime * height_smooth);
transform.localPosition += prev_up * smooth_y;
}
/*Finally we move the ship forward according to the speed we calculated before*/
transform.position += transform.forward * (current_speed * Time.deltaTime);
}

Change value of a var in attached script when Instantiate C#

I can't apply anything to this 'bullet' I instantiate, i can't access the scipts on it. After instatiate, I want the var in scriptbullter attached to it to take the value of 1.
if (Input.GetMouseButton (1) && canFire) {
var mousePosition = FindObjectOfType<Camera> ().ScreenToWorldPoint (new Vector3 (Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.z - FindObjectOfType<Camera> ().transform.position.z));
GetComponent<Rigidbody2D> ().transform.eulerAngles = new Vector3 (0, 0, Mathf.Atan2 ((mousePosition.y - transform.position.y), (mousePosition.x - transform.position.x)) * Mathf.Rad2Deg - 90);
bullet bulletobj = Instantiate (bulletFired, transform.position + transform.forward * 2, Quaternion.identity) as bullet;
bulletobj.GetComponent<scriptbullet>().bulletDamages = 1; //this line doesn't work
bulletobj.GetComponent<Rigidbody2D> ().velocity = (mousePosition - transform.position).normalized * bulletSpeed * Time.smoothDeltaTime;
canFire = false;
}
Thanks in advance guys :D
The bulletobj.GetComponent<scriptbullet>() is failing because bulletobj is null. The object returned from Instantiate is not a bullet but instead is a GameObject, change that line of code to
GameObject bulletobj = Instantiate (bulletFired, transform.position + transform.forward * 2, Quaternion.identity) as GameObject;

How can I figure out the length in units from the player to the end of the camera view?

I'm working with Unity, in c#, and this is my setup.
The camera is facing down, to look at the cube. I'll be referring to the cube as the 'player' from now on.
The problem:
I want to spawn something where the camera cannot see it. This means I need to know exactly how far there is from the player to the end of the FOV, at the player's position.y (Which is always 0), in units.
The reason this cannot just be a constant variable is that the camera's position.y is not constant, and thus the player is allowed to see more of the game area the longer he plays.
There is extra info on what I've tried below, but if you have a better solution, I'll gladly take it.
Extra info:
This is the current script I'm sitting with:
using UnityEngine;
using System.Collections;
public class cameraFollowTarget : MonoBehaviour {
private Transform target;
private float defaultHeight = 3.0f;
private float cameraY;
private float playerSize;
private float moveSpeed = 2.0f;
//Playing with FOV//
private float verticalFOVrad;
private float verticalFOV;
private float cameraHeightAt1;
private float horizontalFOVrad;
private float horizontalFOV;
private float angleToPlayer;
private float defaultAngleToPlayer;
private Vector3 currentPos;
private Vector3 targetPos;
private Vector3 targetLookDir;
private Vector3 leftSideToCamera;
private Vector3 rightSideToCamera;
// Use this for initialization
void Start () {
target = GameObject.FindGameObjectWithTag ("Player").transform;
playerSize = target.GetComponent<playerCollect>().getPlayerSizeStart();
transform.position = new Vector3(transform.position.x, defaultHeight, transform.position.z);
cameraY = defaultHeight;
calculateAngleToPlayer();
defaultAngleToPlayer = angleToPlayer;
}
// Update is called once per frame
void FixedUpdate () {
if (transform.position != target.position) {
playerSize = GameObject.FindGameObjectWithTag("Player").GetComponent<playerCollect>().getPlayerSize();
currentPos = transform.position;
calculateAngleToPlayer();
while(angleToPlayer > defaultAngleToPlayer)
{
cameraY += 0.1f;
calculateAngleToPlayer();
}
targetPos = new Vector3(target.position.x, cameraY, target.position.z);
transform.position = Vector3.Slerp(currentPos, targetPos, moveSpeed * Time.deltaTime);
Camera.main.farClipPlane = cameraY + playerSize / 2;
}
if (transform.rotation != target.rotation) {
Vector3 targetRot;
//Not working
//targetRot = Vector3.Lerp (transform.eulerAngles, new Vector3(90, target.transform.eulerAngles.y, 0), turnSpeed * Time.deltaTime);
//Working but ugly
targetRot = new Vector3(90, target.transform.eulerAngles.y, 0);
transform.eulerAngles = targetRot;
}
//Playing with FOV//
getVerticalFOV ();
getHorizontalFOV ();
print ("Horizontal = " + getHorizontalFOV());
print ("Vertical = " + getVerticalFOV());
}
float getVerticalFOV()
{
horizontalFOVrad = Camera.main.fieldOfView * Mathf.Deg2Rad;
cameraHeightAt1 = Mathf.Tan(horizontalFOVrad * 0.5f);
verticalFOVrad = Mathf.Atan(cameraHeightAt1 * Camera.main.aspect) * 2;
verticalFOV = verticalFOVrad * Mathf.Rad2Deg;
return verticalFOV;
}
float getHorizontalFOV()
{
horizontalFOV = Camera.main.fieldOfView;
return horizontalFOV;
}
float calculateAngleToPlayer()
{
Vector3 imaginaryPos = new Vector3(target.position.x, transform.position.y + cameraY, target.position.z);
leftSideToCamera = new Vector3(target.position.x - (playerSize / 2.0f), target.position.y, target.position.z) - imaginaryPos;
rightSideToCamera = new Vector3(target.position.x + (playerSize / 2.0f), target.position.y, target.position.z) - imaginaryPos;
angleToPlayer = Vector3.Angle(leftSideToCamera, rightSideToCamera);
return angleToPlayer;
}
public float getScreenUnitHorizontal(GameObject target)
{
GameObject imaginaryPos = new GameObject ();
imaginaryPos.transform.position = target.transform.position;
imaginaryPos.transform.rotation = Camera.main.transform.rotation;
float screenUnitHorizontal = 0.0f;
Vector3 vecMidToCamera = Camera.main.transform.position - new Vector3 (Camera.main.transform.position.x, 0.0f, Camera.main.transform.position.z);
Vector3 vecImagiToCamera = Camera.main.transform.position - imaginaryPos.transform.position;
while (Vector3.Angle(vecMidToCamera, vecImagiToCamera) <= getHorizontalFOV() / 2) {
screenUnitHorizontal += 0.1f;
imaginaryPos.transform.position = target.transform.forward * screenUnitHorizontal * Time.deltaTime;
vecMidToCamera = Camera.main.transform.position - new Vector3 (Camera.main.transform.position.x, 0.0f, Camera.main.transform.position.z);
vecImagiToCamera = Camera.main.transform.position - imaginaryPos.transform.position;
}
Debug.DrawLine(Camera.main.transform.position, new Vector3 (Camera.main.transform.position.x, 0.0f, Camera.main.transform.position.z), Color.red);
Debug.DrawLine(Camera.main.transform.position, imaginaryPos.transform.position, Color.red);
Destroy (imaginaryPos);
return screenUnitHorizontal;
}
}
"GetScreenUnitHorizontal" is the main problematic function. It is working, but it is not doing what I was intending.
My intention:
Create a gameobject at the player's position. Then move the gameobject forward until it reaches an angle that is greater than the FOV.
This works fine, in (0, 0, 0). It does not if I move away, and rotate.
If I keep rotating one way, the gameobject seem to place itself in some kind of curve. Possibly a sinus curve.
The solution
31eee384 was the one who solved it for me.
The part of my code that is the solution looks like this:
public class cameraFollowTarget : MonoBehaviour {
private Ray lowerLeft;
private Ray lowerRight;
private Ray upperLeft;
private Ray upperRight;
void FixedUpdate () {
lowerLeft = Camera.main.ScreenPointToRay(new Vector3(0, 0));
lowerRight = Camera.main.ScreenPointToRay(new Vector3(Camera.main.pixelWidth, 0));
upperLeft = Camera.main.ScreenPointToRay(new Vector3(0, Camera.main.pixelHeight));
upperRight = Camera.main.ScreenPointToRay(new Vector3(Camera.main.pixelWidth, Camera.main.pixelHeight));
}
Vector3 getScreenCollisionPoint(Ray corner)
{
Plane spawnPlane = new Plane (new Vector3 (0, 1, 0), Vector3.zero);
float _Distance;
Vector3 collisionPoint;
if (spawnPlane.Raycast (corner, out _Distance) == true) {
collisionPoint = corner.GetPoint(_Distance);
}
Debug.DrawLine(transform.position, collisionPoint, Color.red);
return collisionPoint;
}
If the function getScreenCollisionPoint is called in Update(), it will keep showing where the corners are.
In my setup I also have:
print ("upperLeft = " + getScreenCollisionPoint (upperLeft).z + "," + getScreenCollisionPoint (upperLeft).z);
print ("upperRight = " + getScreenCollisionPoint (upperRight).z + "," + getScreenCollisionPoint (upperRight).z);
print ("lowerLeft = " + getScreenCollisionPoint (lowerLeft).z + "," + getScreenCollisionPoint (lowerLeft).z);
print ("lowerRight = " + getScreenCollisionPoint (lowerRight).z + "," + getScreenCollisionPoint (lowerRight).z);
so that I can see what the values of x and z are, for each of the corners.
Check out Camera.ScreenPointToRay. You can call that with four vectors to get a Ray at each corner of the screen (passing <0,0,0> <x,0,0> <0,y,0> <x,y,0> where x = pixelWidth and y = pixelHeight).
To do this with the main camera:
Ray upperLeft = Camera.main.ScreenPointToRay(new Vector3(0, Camera.main.pixelHeight, 0));
Then, you need to do a Plane.Raycast on a x-z (flat) plane to find the position of each corner where y = 0.
Plane spawnPlane = new Plane(new Vector3(0, 1, 0), Vector3.zero);
float distance;
if (spawnPlane.Raycast(upperLeft, out distance))
{
// The cast has collided! Now find out where it hit.
Vector3 collisionPoint = upperLeft.GetPoint(distance);
}
collisionPoint is then the point where y = 0 corresponding to the upper-left corner of the screen!
Doing this for each corner of the screen gives you the square where the camera can see if you connect up the points. (A trapezoid if you choose to rotate the camera.) You can use that "viewable x-z plane shape" to do whatever else you need to do!
To do what you're trying to do now directly, you can instead use ScreenPointToRay with <pixelWidth/2, pixelHeight, 0> to find the ray at the top-center of the camera view.
To see this at work, I made a script that draws debug lines onto the x-z origin plane using this technique. Copy this code into a new component and add it to the camera:
using System.Linq;
using UnityEngine;
public class Test : MonoBehaviour
{
void Update()
{
Camera cam = GetComponent<Camera>();
Ray[] rays = new[]
{
new Vector3(0, 0),
new Vector3(0, cam.pixelHeight),
new Vector3(cam.pixelWidth, 0),
new Vector3(cam.pixelWidth, cam.pixelHeight)
}.Select(ray => cam.ScreenPointToRay(ray)).ToArray();
Plane xz = new Plane(new Vector3(0, 1, 0), Vector3.zero);
foreach (Ray ray in rays)
{
float distanceAlongRay;
if (xz.Raycast(ray, out distanceAlongRay))
{
Vector3 intersect = ray.GetPoint(distanceAlongRay);
Debug.DrawLine(transform.position, intersect, Color.red);
}
}
}
}
Assuming the camera always stays exactly above the player position, the distance from the player position to the edge of the camera's field of view is:
tan(0.5 * FOV) * camera_height
Where FOV is the field of view in the direction you want to move the object, tan is the tangent (Mathf.Tan), and camera_height is the vertical distance from the player to the camera.

Categories