Raycasting only to a particular object - unity3d

I use the following to detect if something is in front of my avatar:
void Start()
{
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, fwd, 10))
Debug.Log("Something in front");
}
Now, I am trying to find out if only a specific object is in front, for example another avatar with the name Police in the hierarchy:
public class CharAnim : MonoBehaviour
{
police = GameObject.Find("Police");
void Start()
{
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, fwd, 10))
Debug.Log("Something in front");
}
}
However, from the documentation, I cannot see if it is possible to use this police variable to detect it through ray-casting, measuring distance to it, etc...
How do I implement this?

The last answer doesn't take into account that things can actually block the raycast from even reaching your desired object.
You must first give the object you want to detect a custom layer.
Then you have to shoot a raycast which will penetrate and ignore all layers except for the desired one, like so:
Please note: This example assumes you used custom layer #9:
float distance = 10;
int layer = 9;
int layerMask = 1<<layer;
if (Physics.Raycast(transform.position, fwd, distance, layerMask))
Debug.Log("Something in front");
You really shouldn't mark answers as accepted unless they have actually solved the problem, because questions with answers marked as accepted receive substantially less attention from people who would otherwise potentially be able to solve it for you.

RaycastHit hit;
if (Physics.Raycast(transform.position, fwd, hit,10) && hit.collider.gameObject.tag == "police" )
{
// do stuff here
}
Note that you need to set the tag of the gameObject from the editor. you could also use gameObject.name .

Related

How to create a Raycast that will allow a turret to see if a player is behind a wall?

so, basically, I have created a pretty simple turret script that basically just smoothly aims at the player, so long as the player is within a certain amount of range. The problem I am having, is that the Raycast I wrote that actually checks if the 'bullet' (which is nothing - it's just a raycast), would hit the target. This means that even if the player hides behind a wall, the turret can still shoot him.
My current raycast script allows the raycast to go straight through the wall, and since I am new to Unity, I have no idea how to make it check if the first object it hits is the player, so that it cannot go through walls.
Here is my current raycast script:
void Shoot()
{
//I think the problem is here - I want the raycast to return false if it hits a wall - which has the layer "ground", and true if it hits the player. Problem is, I need to make the turret return to resting position when the player is behind a wall.
//To do this, I can just set inRange = true; But I need to be able to determine when the player is behind a wall.
LayerMask layerMask = LayerMask.GetMask("Player");
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out RaycastHit hit, Mathf.Infinity, layerMask))
{
//This determines how much damage will the player take.
int damage = Random.Range(1, 5);
hit.collider.gameObject.GetComponent<playerMovement>().Shot(damage);
//I personally THINK this means that it only triggers collisions with the player, which is why it is not working.
// The player has layer "Player", and tag "Player", so if anyone who wants to help can figure out how to make it stop when it hits anything - and then only return true if it hit the player (meaning the player is not behind walls).
}
}
If you want to check if there is anything between the player and the Raycast, then simply remove the Layermask
Change this:
LayerMask layerMask = LayerMask.GetMask("Player");
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out RaycastHit hit, Mathf.Infinity, layerMask))
To this:
Ray ray = new Ray(transform.position, transform.TransformDirection(Vector3.forward));
if (Physics.Raycast(ray, out RaycastHit hit) {..}
You want to
remove the check for the layer in order to hit everything with the raycast
then you can use TryGetComponent to check whether the hit object has such component attached or not
and in general instead of
transform.TransformDirection(Vector3.forward)
simply use transform.forward ;)
So something like
void Shoot()
{
if (Physics.Raycast(transform.position, transform.forward, out var hit))
{
// Without the need for any tag or layer check
// Simply check if the object you hit has a playerMovement component
// GetComponent was improved a lot lately and now uses hashes
// it's not that performance intense anymore and almost as fast as CompareTag
if(hit.gameObject.TryGetComponent<playerMovement>(out var movement)
{
int damage = Random.Range(1, 5);
movement.Shot(damage);
}
}
}
All you'd need to do is cast from the turret to the player and detect what the raycast has hit. Your current code is setting a mask to only be on the player, so it will never hit a wall. You can change your code to something like this:
private LayerMask layerMask = (1 << LayerMask.NameToLayer("Player") | (1 << LayerMask.NameToLayer("ground")));
void Update () {
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out RaycastHit hit, Mathf.Infinity, layerMask))
{
if(hit.collider.gameObject.layer == LayerMask.NameToLayer("Player"))
{
// you can shoot as you see the player
}
else
{
// you hit the ground - player is behind a wall
}
}
}

Raycast2D hits only one side of Collider

I want to make sure that various objects moving at high speed cannot pass through walls or other objects. My thought process was to check via Raycast if a collision has occurred between two moments of movement.
So the script should remember the previous position and check via Raycast for collisions between previous and current position.
If a collision has occurred, the object should be positioned at the meeting point and moved slightly in the direction of the previous position.
My problem is that works outside the map not inside. If I go from inside to outside, I can go through the walls. From outside to inside not.
Obviously I have misunderstood something regarding the application with raycasts.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObsticalControll : MonoBehaviour
{
private Vector3 positionBefore;
public LayerMask collisionLayer = 9;
private Vector3 lastHit = new Vector3(0, 0, -20);
// Start is called before the first frame update
void Start()
{
positionBefore = transform.position;
}
private void OnDrawGizmos()
{
Gizmos.DrawCube(lastHit, new Vector3(.2f,.2f,.2f));
}
// Update is called once per frame
void Update()
{
Vector3 curruentMovement = transform.position;
Vector2 dVector = (Vector2)transform.position - (Vector2)positionBefore;
float distance = Vector2.Distance((Vector2)positionBefore, (Vector2)curruentMovement);
RaycastHit2D[] hits = Physics2D.RaycastAll((Vector2)positionBefore, dVector, distance, collisionLayer);
if(hits.Length > 0)
{
Debug.Log(hits.Length);
for (int i = hits.Length -1 ; i >= 0 ; i--)
{
RaycastHit2D hit = hits[i];
if (hit.collider != null)
{
Debug.Log("hit");
lastHit.x = hit.point.x;
lastHit.y = hit.point.y;
Vector3 resetPos = new Vector3(hit.point.x, hit.point.y, transform.position.z) + positionBefore.normalized * 0.1f;
transform.position = new Vector3(resetPos.x, resetPos.y, transform.position.z);
}
}
}
positionBefore = transform.position;
}
}
Theres a better way to deal with this that unity has built in.
Assuming the object thats moving at a high speed has a RigidBody(2d in your case) you can set its Collision Detection to Continuous instead of Discrete.
This will help collisions with high speed collision, assuming that its moving at high speed and the wall is not moving.
If for some reason you cannot apply this to your scenario, Ill try to help with the raycast solution.
However, I am still wondering about the collision behavior of my raycast script. That would be surely interesting, if you want to calculate shots or similar via raycast
Alright, so your initial idea was to check if a collision had occurred, By checking its current position and its previous position. And checking if anything is between them, that means a collision has occurred. And you would teleport it back to where it was suppose to have hit.
A better way todo this would be to check where the GameObject would be in the next frame, by raycasting ahead of it, by the distance that it will travel. If it does hit something that means that within the next frame, it would have collided with it. So you could stop it at the collision hit point, and get what it has hit. (This means you wouldn't have to teleport it back, So there wouldn't be a frame where it goes through then goes back)
Almost the same idea but slightly less complicated.
Problem would be that if another object were to appear between them within the next frame aswell, it could not account for that. Which is where the rigidbody.movePosition shines, And with OnCollisionEnter you can detect when and what it collided with correctly. Aswell as without the need to teleport it back

Precision difference between a raycast and OnMouseOver

I'm really surprised by what I see in my Unity3D project and was wondering if anyone has an explanation as I didn't find one after looking for it on google.
So I have a project where I instantiate multiples objects, which are basically cubes, with their colliders (not triggered) and where I want to know if the mouse is over one of them.
So I attached a very basic script taken from the unity documentation:
void OnMouseEnter()
{
Debug.Log("enter block " + this.name);
// Change the color of the GameObject to red when the mouse is over GameObject
m_Renderer.material.color = m_MouseOverColor;
}
void OnMouseOver()
{
Debug.Log("over block " + this.name);
// Change the color of the GameObject to red when the mouse is over GameObject
m_Renderer.material.color = m_MouseOverColor;
}
void OnMouseExit()
{
Debug.Log("exit block " + this.name);
// Reset the color of the GameObject back to normal
m_Renderer.material.color = m_OriginalColor;
}
And I don't seem to see any result. While looking for options I scaled the collider box to way bigger and can get some results, but they are not precise, the mouse needs to be slightly on the side of the cubes to work. It looks like a perspective effect caused by hte size of the collider by that would be annoying for the final user.
So I tried to use a simple raycast test in a script attached to the camera:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100000000, LayerMask.GetMask("TrackPartEnd")))
{
Debug.DrawRay(ray.origin, ray.direction * 100000000, Color.blue, 3);
myCursorObject.transform.position = hit.point;
}
else
{
if (Physics.Raycast(ray, out hit, 100000000, LayerMask.GetMask("Floor")))
{
Debug.DrawRay(ray.origin, ray.direction * 100000000, Color.green, 3);
myCursorObject.transform.position = hit.point;
}
else
{
Debug.DrawRay(ray.origin, ray.direction * 100000000, Color.red, 3);
}
}
And with this one I get a really good result, it's fast and really precise.
But I didn't find any explanation on why? To my knowledge, OnMouseOver uses raycast as well. I've double-checked and I don't see anything blocking the rays neither.
Does anyone have an explanation?

OnTrigger Events Work With Delay

I've got a bullet script with a particle system and a decal.
I think that it has something to do with these events not being able to fire in time or with fps in update. Not sure yet.
So, it's being late.
The ellow points are where the particles start to play. They should be right on these wooden walls. There should be three particles working and three bullet holes, kinda bullet penetrating one wall and getting destroyed on the second one.
THE QUESTION IS HOW TO MAKE IT WORK NORMAL, SO THAT THE TRIGGERS WORK WHEN NEEDED AS WELL AS THE PARTICLES AND THE DECALS? Maybe there's a way to excellerate the code to work on time? Or maybe there's another problem with that?
The screenshot:
The Code:
public class BulletScript : MonoBehaviour {
public bool isThrough = true;
public float BulletSpeed = 100;
public float CurrentDamage;
public float EnterLuft = -0.005f;
public float ExitLuft = 0.05f;
public GameObject woodParticle;
private ContactPoint CollisionPoint;
public GameObject BulletMarkPref;
Rigidbody bullet;
private void Start()
{
bullet = this.GetComponent<Rigidbody>();
}
void FixedUpdate () {
bullet.velocity = Vector3.forward * BulletSpeed;
//this.transform.Translate(Vector3.forward * BulletSpeed * Time.deltaTime);
}
private void OnTriggerEnter(Collider other)
{
Transform hitPoint = this.transform;
LevelObject.LvlObjectType objectType = other.gameObject.GetComponent<LevelObject>().objType;
if(objectType == LevelObject.LvlObjectType.obstacle)
{
if (isThrough)
{
Instantiate(woodParticle, hitPoint.localPosition, Quaternion.LookRotation(-hitPoint.forward)).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(this.transform, true);
}
else
{
Instantiate(woodParticle, hitPoint.localPosition, Quaternion.LookRotation(-hitPoint.forward)).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(hitPoint, true);
Destroy(this.gameObject);
}
}
else if(objectType == LevelObject.LvlObjectType.obstacle)
{
Destroy(this.gameObject);
}
else if(objectType == LevelObject.LvlObjectType.wall)
{
LeaveBulletMark(hitPoint, true);
Destroy(this.gameObject);
}
}
private void OnTriggerExit(Collider other)
{
Transform hitPoint = this.transform;
Instantiate(woodParticle, hitPoint.localPosition, hitPoint.rotation).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(hitPoint, false);
}
void LeaveBulletMark(Transform hitPoint, bool ifEnter)
{
GameObject TemporaryBulletMarkHandler;
TemporaryBulletMarkHandler = Instantiate(BulletMarkPref, hitPoint.localPosition, Quaternion.LookRotation(ifEnter ? hitPoint.forward : CollisionPoint.normal)) as GameObject;
isThrough = false;
TemporaryBulletMarkHandler.transform.Translate(hitPoint.forward * (ifEnter ? 0.005f : -0.005f));
}
}
I don't think your problem is something simple with the code. There is an inherent issue with calculating fast moving objects like bullets with true physics calculations especially if they are small. Often between physics updates, they pass through wall colliders completely without registering.
You have to think of it like this to see the problem. The bullet isn't tracked continuously along its trajectory. It has a starting location, a formula for its movement and it calculates a new location at each physics update. You could fire a bullet straight at a wall, and in one update the bullet would be several meters in front of the wall, and in the next, it would be several meters behind the wall without ever triggering a collision. This is why so many game use ray tracing to calculate bullet trajectories. The bullet movement isn't perfectly accurate, especially for long shots, but obstructions to the bullet path are registered.
By default unity's Physics Engine runs at 50 frames per second. A modern bullet travels between 1200 and 1700 m/s. That gives you a distance between 24 and 34 meters traveled between frames. Even a small object falling at terminal velocity (54 m/s) might pass through a collider unnoticed. If you made a 1-meter thick box collider, you would likely register a falling object but not a bullet.
I think you could do some clever combination of ray tracing and bullet physics to get the best of both worlds. Maybe you could ray trace from the bullet at each fixed update or there may be some better technique already invented for this exact situation that I don't know about.

Unity3D Raycasting with Oculus Rift Integration / Measuring the distance

I want to measure the distance between two points in Unity3D Game Engine using the Oculus Rift. The points are targeted by the user by looking at point A, pressing alpha1 on the keyboard and B, pressing alpha2 on the keyboard. I got this far:
#pragma strict
private var measuring = false;
private var startPoint : Vector3;
private var dist;
function Update() {
var hit : RaycastHit;
if (Input.GetKeyDown(KeyCode.Alpha1)) {
dist = 0.0f;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), hit)) {
// if (Physics.Raycast(transform.position, transform.forward, hit, 10)) {
measuring = true;
startPoint = hit.point;
}
}
if (measuring && Input.GetKeyDown(KeyCode.Alpha2)) {
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), hit)) {
// if (Physics.Raycast(transform.position, transform.forward, hit, 10)) {
dist = Vector3.Distance(startPoint, hit.point);
}
}
}
function OnGUI() {
if (measuring) {
GUI.Label(Rect(50,50,180,50), "Distance: " + dist.ToString());
}
}
My problem is, that this code only works with the standard main camera object, but I want to use the Oculus integrated OVRCameraRig. I get the following exception message:
NullReferenceException: Object reference not set to an instance of an object
MeasureInGame.Update () (at Assets/MeasureInGame.js:11)
I found a solution on this site: https://kaharri.com/unity-gaming-shootingaiming-part3-oculus/ I created a ShotSpawner object as a child of OVRCameraRig (this should act like a gun in front of the camera) and changed the Raycast to
Physics.Raycast(transform.position, transform.forward, hit, 10)
to get the users view. But it also doesn't seem to work.
How can I get the aiming done with the Oculus a Main Camera. And is it correct that I strictly need to have a collider on my objects to be measured or is there a solution without collider?
Greetings
First of all - are you really using the mouse with Oculus? Of course you can, but the standard way is to look at the selected object (cursor is in the center of viewport). Cast the ray from the "middle" eye in the oculus integration - it's the object that is the parent to the left and right eyes. Use this ray instead of the one from Camera.main.ScreenPointToRay:
// add a reference to the middleEyeGameobject
// in your class and link it in the inspector
var ray=new Ray(middleEyeGameobject.transform.position, middleEyeGameobject.transform.forward);
Also, you don't have to specify the distance with hit raycast (remove the fourth parameter to Physics.Raycast).
And for the additional question yes, everything has to have a collider with Physics.Raycast. And yes, there are other ways to do this, but the built in ones (2) still require colliders (or similar), although they can be virtual, not attached to objects.
It's best to use colliders, perhaps on their own layer.
The NullReferenceException you are getting is because you are using Camera.main.ScreenPointToRay(Input.mousePosition) , camera.main uses the MainCamera tag which is not set for your OVRCameraRig. You can set tag of this camera as mainCamera if OVRCameraRig is supposed to be your mainCamera or otherwise you can take reference of this camera (OVRCameraRig) and use cameraRef.ScreenPointToRay(Input.mousePosition). :)