I'm using Raycast for picking up objects, whenever the player looking directly at a pickable object, the reticle colors turns to red meaning that it can be picked up. However, objects are still pickable even if there is a barrier between the raycast and the pickable object. like this image
as you can see the drawer is closed and the pickable object is inside, but the player still can pick it up!. it should pe pickable only when the object is exposed and the raycast hits it directly like this:
if (PickingUp)
{
if (heldObj == null) //if currently not holding anything
{
//perform raycast to check if player is looking at object within pickuprange
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, pickUpRange) && hit.transform.GetComponent<canPickUp_>() != null)
{
//make sure pickup tag is attached
// if (hit.transform.gameObject.tag == "canPickUp")
Debug.Log("Color should be changed");
if (hit.transform.CompareTag("TargetObj") && !targetObjectsList.Contains(hit.transform.gameObject))
{
/* if (aSource)
{
aSource.Play();
}*/
targetObjectsList.Add(hit.transform.gameObject);
}
PickUpObject(hit.transform.gameObject);
}
}
else
{
if (canDrop == true)
{
StopClipping(); //prevents object from clipping through walls
DropObject();
}
}
private void PickUpObject(GameObject pickUpObj)
{
if (pickUpObj.GetComponent<Rigidbody>()) //make sure the object has a RigidBody
{
heldObj = pickUpObj; //assign heldObj to the object that was hit by the raycast (no longer == null)
heldObjRb = pickUpObj.GetComponent<Rigidbody>(); //assign Rigidbody
heldObjRb.isKinematic = true;
heldObjRb.transform.parent = holdPos.transform; //parent object to holdposition
heldObj.layer = LayerNumber; //change the object layer to the holdLayer
//make sure object doesnt collide with player, it can cause weird bugs
Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), true);
reticle.color = new Color(1, 1, 1, 0.75f);
reticle.enabled = false;
//transform.localScale = new Vector3(1 / heldObj.transform.localScale.x, 1 / heldObj.transform.localScale.y, 1 / heldObj.transform.localScale.z);
}
}
void DropObject()
{
//re-enable collision with player
Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), false);
heldObj.layer = 0; //object assigned back to default layer
heldObjRb.isKinematic = false;
heldObj.transform.parent = null; //unparent object
heldObj = null; //undefine game object
reticle.color = new Color(1, 1, 1, 0.75f);
reticle.enabled = true;
}
How would I prevent this from happening?
As discussed in the comments the issue was that the "barrier" objects had their layers set to "Ignore Raycast", preventing the Physics.Raycast from reporting them as the hit.
Changing the layer to default (or any other layer that is not "Ignore Raycast") fixes the issue, as the "barrier" will be reported as the hit instead of the pickable object
Related
For picking-up objects, I chose to make the picked-up object float in the middle of the screen in front of the camera following a certain point.
raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
if (Physics.Raycast(raycastPos, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
lookObject = hit.collider.transform.gameObject;
reticle.color = new Color(1, 0, 0, 0.75f);
}
else
{
lookObject = null;
reticle.color = new Color(1, 1, 1, 0.75f);
}
if (PickingUp)
{
if (currentlyPickedUpObject == null)
{
if (lookObject != null)
{
if (hit.distance >= minDistance)
{
PickUpObject();
}
}
}
}
public void PickUpObject()
{
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
// currPicked = true;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
}
I'm facing an issue with this, Whenever the player is standing over a pickable object and picks it up (while standing on it), the player flies on top of the object like this:
]
Here is another image from the Scene window :
You can see clearly here what's happening when the player picks up an object underneath him.
How can I prevent that from happening?
Put the picked up object in a new layer. Then disable any physics interaction between player and said layer. You will then observe that the picked up object and the player may intersect. To fix this, you can project the object outside of your player collider using a few more ray casts. Given that the object's collider is convex, calculations should be fairly simple.
I'm trying to make a pickup system and I thought it would be cool to do an outline around the item when you're looking at it. The issue I'm facing though is when you're no longer looking at the object I need to disable the outline. I ended up doing an odd solution and would like to get some help improving it.
public class PlayerCamera : MonoBehaviour
{
public Transform playerBody;
public Transform cameraHolder;
public float sensitivity;
public float currentY;
void Update()
{
MoveCamera();
LookingAtObject();
}
Outline objectOutline;
void LookingAtObject()
{
if(Physics.Raycast(cameraHolder.transform.position, cameraHolder.transform.forward, out var hit, Mathf.Infinity))
{
var obj = hit.collider.gameObject;
var outline = obj.GetComponent<Outline>();
if (obj && outline)
{
objectOutline = hit.transform.GetComponent<Outline>();
if (objectOutline)
objectOutline.OutlineWidth = 7;
}
else if (objectOutline)
objectOutline.OutlineWidth = 0;
}
}
}
You can store the outlined object in a variable, and whenever you hit a different outline object or hit nothing, set the outline back to zero.
Outline objectOutline;
void LookingAtObject()
{
if (Physics.Raycast(...))
{
var outline = hit.collider.GetComponent<Outline>();
// Make sure the hit object is not the same one we already outlined
//
if (outline != objectOutline)
{
// Remove the outline from our previously viewed object
//
if (objectOutline != null)
{
objectOutline.OutlineWidth = 0;
}
// Store the new outline object
//
objectOutline = outline;
// Since outline could be null, we need to check null before outlining
//
if (objectOutline != null)
{
objectOutline.OutlineWidth = 7;
}
}
}
// If we have an object we outlined and we didnt hit anything,
// remove the outline and reset the variable
//
else if (objectOutline != null)
{
objectOutline.OutlineWidth = 0;
objectOutline = null;
}
}
You need two events to solve the problem. Input frame and ray output frame. This code detects which raycast event is by recording the previous raycastHit frame and comparing it to the current hit, and sets the outline accordingly.
private RaycastHit lastHit;
void Update()
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Physics.Raycast(ray, out var hit);
//Physics.Raycast(cameraHolder.transform.position, cameraHolder.transform.forward, out var hit, Mathf.Infinity);
if (hit.transform != lastHit.transform)
{
if (hit.transform) // when raycast Begin
{
var outline = hit.transform.GetComponent<Outline>();
outline.OutlineWidth = 7;
}
else if (lastHit.transform) // when raycast out
{
var outline = lastHit.transform.GetComponent<Outline>();
outline.OutlineWidth = 0;
}
}
lastHit = hit;
}
Hint: I commented on your raycast code for testing. If you want to change the raycast code as before.
In my level, I have a water collider where if you fall in, it triggers a splash effect and water sounds. However, because there is already an object inside the water, whenever I start the level, the water collider triggers and splash and water sounds despite the object already being in the collider.
So, even with the object deep inside the water collider, it creates the splash sound and water effect as if it just fell in.
How do I prevent this?
My code involves OnTrigger2D functions. But how do I make Unity check if an object is already colliding before level load?
Code:
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
gravityoriginal = playerrigidbody.gravityScale;
massoriginal = playerrigidbody.mass;
playerrigidbody.gravityScale = 0.1f;
playerrigidbody.mass = other.GetComponent<Rigidbody2D>().mass + 2f;
splash.Play(); //Plays the initial splash if velocity is high
underwaterbool.IsUnderwater = true; //stop dust particle
mainmusic.enabled = true;
powerupmusic.enabled = true;
deathmusic.enabled = true;
}
else if (other.tag == "Snatcher")
{
masssnatcheroriginal = snatcherrigidbody.mass;
gravityoriginalsnatcher = snatcherrigidbody.gravityScale;
snatcherrigidbody.gravityScale = 0.1f;
snatcherrigidbody.mass = other.GetComponent<Rigidbody2D>().mass + 2f;
splashsnatcher.Play();
snatchersounds.enabled = true;
}
else if (other.tag != "Player" && other.tag != "Snatcher" && other.GetComponent<Rigidbody2D>() != null)
{
gravityoriginalbox = other.GetComponent<Rigidbody2D>().gravityScale;
massoriginalbox = other.GetComponent<Rigidbody2D>().mass;
other.GetComponent<Rigidbody2D>().mass = other.GetComponent<Rigidbody2D>().mass + 2f;
other.GetComponent<Rigidbody2D>().gravityScale = 0.1f;
other.GetComponent<ParticleSystem>().Play(false);
splashaudio.Play();
Splashparticlesforbox.IsUnderwaterBox = true;
}
if(other.GetComponent<Rigidbody2D>() != null)
{
other.GetComponent<Rigidbody2D>().velocity = new Vector2(0f, -0.5f);
}
if (!cooldown)
{
splashaudio.Play();
}
cooldown = true;
StartCoroutine(waittime());
}
Can you post your OnTrigger2D functions code please? Normally Unity does not trigger the OnTriggerEnter method if an object is already inside the trigger when the scene beggins but the OnTriggerStay is executed every frame.
Anyway... one option (not the best one I think) would be to put a boolean propoerty in the trigger that is initialized true and use it to prevent the OnTriggerFunctions to do anything til the fame ends. Then in the LateUpdate method you can set the property as false.
bool m_FirstFrame = true;
void onEnable()
{
m_FirstFrame = true;
}
void OnTriggerEnter2D(Collider2D collision)
{
if(m_FirstFrame){
return;
}
.... //Rest of code
}
//Same for the other OnTrigger2D methods you use
void LateUpdate()
{
m_FirstFrame = false;
}
I hope it helps! Tell me if you need something more, and please, post your code, that way is easier for us to fint where is the issue and get how to fix it.
Good luck ^^
I am looking to show a line in my app from where the model is placed so that the user knows position where the model is kept in real world. When user changes device camera away from model the line gets turned on to show where the model is. Similarly it turns off when model is detected. I have attached images to show from a similar app white dotted lines show the path. Notice how the lines disappear when the model is detected.
LineRenderer lins;
public GameObject Lineprefab;
private GameObject newline;
public Transform startpoint;
public Renderer m_rend1;
bool HitTestWithResultType (ARPoint point, ARHitTestResultType resultTypes)
{
List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface ().HitTest (point, resultTypes);
if (hitResults.Count > 0 && check==true)
{
foreach (var hitResult in hitResults)
{
Debug.Log ("Got hit!");
//obj.Hideplane();
Genplanes.SetActive(false);
if (Select == 0) {
Debug.Log("hit-zero!");
Instantiate(Instaobj[0], ForSelect);
check = false;
}
if (Select == 1) {
Debug.Log("hit-one!");
Instantiate(Instaobj[1], ForSelect);
check = false;
}
if (Select == 2) {
Debug.Log("hit-two!");
Instantiate(Instaobj[2], ForSelect);
check = false;
}
m_HitTransform.position = UnityARMatrixOps.GetPosition (hitResult.worldTransform);
m_HitTransform.rotation = UnityARMatrixOps.GetRotation (hitResult.worldTransform);
Debug.Log (string.Format ("x:{0:0.######} y:{1:0.######} z:{2:0.######}", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z));
obj.StopPlaneTracking();
}
}
return false;
}
private void Start()
{
spawngenerator();
newline.SetActive(false);
m_rend1 = GetComponent<MeshRenderer>();
}
void spawngenerator()
{
GameObject newline = Instantiate(Lineprefab);
lins = newline.GetComponent<LineRenderer>();
}
private void LateUpdate()
{
lins.SetPosition(0, startpoint.position);
lins.SetPosition(1, m_HitTransform.position);
if( m_rend1.isVisible==true)
{
Debug.Log("Render is Visible");
newline.SetActive(false);
}
else if( m_rend1.isVisible==false)
{
newline.SetActive(true);
Debug.Log("It is InVisible");
Debug.Log("Render is InVisible");
}
}
void Update () {
#if UNITY_EDITOR //we will only use this script on the editor side, though there is nothing that would prevent it from working on device
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
//we'll try to hit one of the plane collider gameobjects that were generated by the plugin
//effectively similar to calling HitTest with ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent
if (Physics.Raycast (ray, out hit, maxRayDistance, collisionLayer)) {
//we're going to get the position from the contact point
m_HitTransform.position = hit.point;
Debug.Log (string.Format ("x:{0:0.######} y:{1:0.######} z:{2:0.######}", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z));
//and the rotation from the transform of the plane collider
m_HitTransform.rotation = hit.transform.rotation;
}
}
#else
if (Input.touchCount > 0 && m_HitTransform != null )
{
var touch = Input.GetTouch(0);
if ((touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved) && !EventSystem.current.IsPointerOverGameObject(touch.fingerId))
{
var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);
ARPoint point = new ARPoint {
x = screenPosition.x,
y = screenPosition.y
};
// prioritize reults types
ARHitTestResultType[] resultTypes = {
//ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingGeometry,
ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent,
// if you want to use infinite planes use this:
//ARHitTestResultType.ARHitTestResultTypeExistingPlane,
//ARHitTestResultType.ARHitTestResultTypeEstimatedHorizontalPlane,
//ARHitTestResultType.ARHitTestResultTypeEstimatedVerticalPlane,
//ARHitTestResultType.ARHitTestResultTypeFeaturePoint
};
foreach (ARHitTestResultType resultType in resultTypes)
{
if (HitTestWithResultType (point, resultType))
{
return;
}
}
}
}
#endif
}
.
First, I 'd start with checking if the model is within the bounding box of the camera https://docs.unity3d.com/ScriptReference/Renderer-isVisible.html
if the object is not visible (isVisible == false), create a line renderer from object position to wherever it should end.
The end point could be a camera child place just in front of it, so it looks like it starts from the user to the object.
Okay, I am making a simple game mechanic where you are a ball rolling along a small panel. On the edge of the panel are 8 child objects. 4 of them are triggers on the edges of the panel, and 4 of them are empty game objects 1 unit away from each edge of the panel for the location of the next panel prefab to spawn at. The ball has a trigger on it that detects the location of the empty game objects to tell the panel prefab where to spawn. When the ball enters a specific trigger frm the panel the ball is suppose to instantiate a panel prefab on the location that I assign based on the trigger the ball enters. Here is my code:
public GameObject panelPrefab;
Transform frontSpawn;
Transform backSpawn;
Transform leftSpawn;
Transform rightSpawn;
private bool allowSpawn;
void Awake()
{
allowSpawn = true;
}
void OnTriggerStay(Collider spawn)
{
if (spawn.gameObject.tag == "FrontSpawn")
{
frontSpawn = spawn.transform;
}
else if (spawn.gameObject.tag == "BackSpawn")
{
backSpawn = spawn.transform;
}
else if (spawn.gameObject.tag == "LeftSpawn")
{
leftSpawn = spawn.transform;
}
else if (spawn.gameObject.tag == "RightSpawn")
{
rightSpawn = spawn.transform;
}
}
void OnTriggerEnter (Collider other)
{
if (other.gameObject.tag == "Front" && allowSpawn == true)
{
Instantiate (panelPrefab, frontSpawn.transform.position, Quaternion.identity);
allowSpawn = false;
}
else if (other.gameObject.tag == "Back" && allowSpawn == true)
{
Instantiate (panelPrefab, backSpawn.transform.position, Quaternion.identity);
allowSpawn = false;
}
else if (other.gameObject.tag == "Left" && allowSpawn == true)
{
Instantiate (panelPrefab, leftSpawn.transform.position, Quaternion.identity);
allowSpawn = false;
}
else if (other.gameObject.tag == "Right" && allowSpawn == true)
{
Instantiate (panelPrefab, rightSpawn.transform.position, Quaternion.identity);
allowSpawn = false;
}
}
void OnTriggerExit (Collider other)
{
allowSpawn = true;
}
My issue is on each of the Instantiate calls, I am getting a NullReferenceException. I have the panelPrefab assigned in the unity editor, and I don't know what could be causing this! If anyone can help me here it would be GREATLY appreciated... So thank you in advance!
OnTriggerEnter is called before OnTriggerStay. The error is not due to the panelPrefab object. It might happen that your rightSpawn, leftSpawn etc. objects are null and hence cannot access the transform property of a null object.
Before instantiating verify if rightSpawn etc. is null or not and then access it's position.