How to show a tracking line from where the model is placed in Augmented Reality? - unity3d

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.

Related

Stop outlining object when no longer looking at it?

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.

Unity: Problem with Index was outside the bounds of the array

I'm drawing using LineRenderer but I enable and disable the gameobject where the script is at different times.
The first enable it works perfect, after I disable and enable, I get this error
IndexOutOfRangeException: Index was outside the bounds of the array.
error shows to this line of code
Vector2 touchPos = Camera.main.ScreenToWorldPoint(Input.touches[0].position);
Here is all the code
public GameObject linePrefab;
private Line activeLine;
private void Update()
{
if (Input.touchCount > 0)
{
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
GameObject lineGO = Instantiate(linePrefab);
activeLine = lineGO.GetComponent<Line>();
}
if (Input.GetTouch(0).phase == TouchPhase.Ended)
{
activeLine = null;
}
}
if (activeLine != null)
{
Vector2 touchPos = Camera.main.ScreenToWorldPoint(Input.touches[0].position);
activeLine.UpdateLine(touchPos);
}
}
You're assuming the user is touching the screen in your code.
You need to check if there are any active touches before processing the touch position. Specifically, you can add to your if check whether or not Input.touchCount > 0

Change object on button down precisely at an object

I have a target object (cube), and a fake mouse that I created from an object (sphere) to be controlled by gamepad joystick. I want to bring out another object (let say; sphere), when I push the gamepad button and precisely hit the target object (cube).
Before, I have tried with a mouse click, and it succeeds, but when I controlled the fake mouse with a joystick, when I press the button even outside the target (cube) it still brings out another object. Here is the code, if anybody can help me to revise it. Thanks
function Start () {}
function Update () {
if (Input.GetButtonDown ("Fire1"))
{
var Cube = GameObject.FindGameObjectsWithTag ("Cube");
if (Cube[0].GetComponent(MeshRenderer).enabled){
var Circle1 = GameObject.FindGameObjectsWithTag ("Circle1");
Circle1[0].GetComponent(MeshRenderer).enabled = true;
Circle1[0].GetComponent(MeshRenderer).material.color = color.red;
}
}
}
[solved]
Thanks to EmreE and Draco18s for replying my question. I have solved it, I made a collision trigger indeed.
Here is my code after several trials.
if (Input.GetButtonDown ("Fire1") && isCollide)
{
var Cube = GameObject.FindGameObjectsWithTag ("Cube");
if (Cube[0].GetComponent(MeshRenderer).enabled){
var Circle1 = GameObject.FindGameObjectsWithTag ("Circle1");
Circle1[0].GetComponent(MeshRenderer).enabled = true;
Circle1[0].GetComponent(MeshRenderer).material.color = Color.red;
Debug.Log("Muncul");
}
}
function OnTriggerEnter (col : Collider)
{
Debug.Log(isCollide);
if(col.gameObject.name == "Mouse3DSphere")
{
isCollide = true;
}
}
function OnTriggerExit (col : Collider)
{
Debug.Log(isCollide);
if(col.gameObject.name == "Mouse3DSphere")
{
isCollide = false;
}
}

Moving object with raycast issues

I have written a script where a gameobject is intended to move to a raycast.point thrown from the player camera. For the most part this works fine, however there are times (approximately when camera is 45 degrees up from the object) when the object rapidly moves towards the camera (i.e. raycast source).
I have tried a number of approaches attempting to resolve this, however I can’t seem to dig out the root of this issue. Managed to prevent this from occurring by deactivating the collider attached to the object being moved. However I need the collider for various reasons so this approach is not appropriate.
If anyone can provide any pointers as to where I am going wrong I would be incredibly grateful.
NB: coding in uJS
Many thanks in advance, Ryan
function FixedUpdate() {
if (modObj != null && !guiMode) {
//Panel Control
if (!selectObjPanel.activeSelf && !modifySelectObjPanel.activeSelf) //if the selectpanel not open and modSelect not already activated
{
activateModSelectObjPanel(true); //activate it
} else if (selectObjPanel.activeSelf) {
activateModSelectObjPanel(false);
}
//Move
if (Input.GetKey(KeyCode.E)) {
if (Input.GetKeyDown(KeyCode.E)) {
// modObj.GetComponent(BoxCollider).enabled = false;
modObj.GetComponent(Rigidbody).isKinematic = true;
modObj.GetComponent(Rigidbody).useGravity = false;
//
initPos = modObj.transform.position;
var initRotation = modObj.transform.rotation;
}
moveObject(modObj, initPos, initRotation);
} else {
// modObj.GetComponent(BoxCollider).enabled = true;
modObj.GetComponent(Rigidbody).isKinematic = false;
modObj.GetComponent(Rigidbody).useGravity = true;
}
}
}
function moveObject(modObj: GameObject, initPos: Vector3, initRotation: Quaternion) {
//Debug.Log("Moving Object");
var hit: RaycastHit;
var foundHit: boolean = false;
foundHit = Physics.Raycast(transform.position, transform.forward, hit);
//Debug.DrawRay(transform.position, transform.forward, Color.blue);
if (foundHit && hit.transform.tag != "Player") {
//Debug.Log("Move to Hit Point: " + hit.point);
modifyObjGUIscript.activateMoveDisplay(initPos, hit.point);
var meshHalfHeight = modObj.GetComponent. < MeshRenderer > ().bounds.size.y / 2; //helps account for large and small objects
// Debug.Log("CurObj Mesh Min: " + meshHalfHeight);
// modObj.transform.position = hit.point; //***method 01***
// modObj.transform.position = Vector3.Lerp(initPos, hit.point, speed); //***method 02***
// modObj.transform.position = Vector3.SmoothDamp(initPos, hit.point, velocity, smoothTime); //***method 02***
var rb = modObj.GetComponent. < Rigidbody > ();
rb.MovePosition(hit.point); //***method 03***
modObj.transform.position.y = modObj.transform.position.y + meshHalfHeight + hoverHeight;
modObj.transform.rotation = initRotation;
}
}
Turns out the issue was being caused by the raycast hitting the object being moved. Resolved this by only allowing hits from the terrain to be used as points to move to.
if(foundHit && hit.transform.tag == "Terrain")

NullReferenceException and don't know how to fix it

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.