Here is the UnityARHitTestExample.cs document. My aim is to have the object be placed once (one hit). After that I don't want the object to be moved after the first hit. Any help/suggestions will be helpful.
using System;
using System.Collections.Generic;
namespace UnityEngine.XR.iOS
{
public class UnityARHitTestExample : MonoBehaviour
{
public Transform m_HitTransform;
public float maxRayDistance = 30.0f;
public LayerMask collisionLayer = 1 << 10; //ARKitPlane layer
bool HitTestWithResultType (ARPoint point, ARHitTestResultType resultTypes)
{
List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface ().HitTest (point, resultTypes);
if (hitResults.Count > 0) {
foreach (var hitResult in hitResults) {
Debug.Log ("Got hit!");
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));
return false;
}
}
return false;
}
// Update is called once per frame
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 ()) {
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)
{
var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);
ARPoint point = new ARPoint {
x = screenPosition.x,
y = screenPosition.y
};
// prioritize reults types
ARHitTestResultType[] resultTypes = {
ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent,
// if you want to use infinite planes use this:
//ARHitTestResultType.ARHitTestResultTypeExistingPlane,
ARHitTestResultType.ARHitTestResultTypeHorizontalPlane,
ARHitTestResultType.ARHitTestResultTypeFeaturePoint
};
foreach (ARHitTestResultType resultType inresultTypes)
{
if (HitTestWithResultType (point, resultType))
{
return;
}
}
}
}
#endif
}
}
}A
create a bool to check if object is placed already.
bool placed = false;
Execute codes inside Update() only if not placed:
void Update () {
if (!placed){
//code
}
}
now set placed to true after placing object
if (HitTestWithResultType (point, resultType))
{
placed = true;
return;
}
Related
So I'm trying to write a script, it should work in a way that I'm able to put a game object in the inspector, have that object be recognised when looked at with a camera, and when looked, it should also enable an animation and image sprite. I want to use this script without tags as it doesn't single out the specific aniamtion I want played, but it's almost impossible to get it working right and there's almost no tutorials on identifying RayCast.hit with GameObject instead of Tag.
Is there a way to do it, or can this only be accomplished with tags?
using UnityEngine;
public class selectionManager : MonoBehaviour
{
public Animator outlineAnim;
public GameObject image;
public GameObject hitObject;
public Vector3 collision = Vector3.zero;
public LayerMask layer;
private void Update()
{
var ray = new Ray(this.transform.position, this.transform.forward);
RaycastHit hit;
if (Physics.Raycast(ray, out hit) && hit.collider.gameObject.name(hitObject))
{
if (hitObject != null)
{
outlineAnim.SetBool("outlineOff", true);
outlineAnim.SetBool("outlineOn", false);
}
outlineAnim.SetBool("outlineOn", true);
image.SetActive(true);
}
else if (hitObject != null)
{
outlineAnim.SetBool("outlineOff", true);
outlineAnim.SetBool("outlineOn", false);
}
}
If you can use no more filters (layers, tags) etc but you solely want to rely on the GameObject reference (note that the name is quite unreliable) then I would rather use Physics.RaycastAll and do e.g.
using System.Linq;
public GameObject hitObject;
private void Update()
{
var ray = new Ray(this.transform.position, this.transform.forward);
var hits = Physics.RaycastAll(ray);
var hittingTheHitObject = hits.Any(hit => hit.transform.gameObject == hitObject);
outlineAnim.SetBool("outlineOff", hittingTheHitObject);
outlineAnim.SetBool("outlineOn", !hittingTheHitObject);
image.SetActive(hittingTheHitObject);
}
where hits.Any(hit => hit.transform.gameObject == hitObject) basically equals doing
private bool Any(RaycastHit[] hits)
{
foreach(var hit in hits)
{
if(hit.transform.gameObject == hitObject) return true;
}
return false;
}
If you change the if condition, for example, like this
private void Update()
{
var ray = new Ray(this.transform.position, this.transform.forward);
RaycastHit hit;
if (Physics.Raycast(ray, out hit) && hit.collider.gameObject != null)
{
hitObject = hitInfo.collider.gameObject;
if (hitObject != null)
{
outlineAnim.SetBool("outlineOff", true);
outlineAnim.SetBool("outlineOn", false);
}
outlineAnim.SetBool("outlineOn", true);
image.SetActive(true);
}
else if (hitObject != null)
{
outlineAnim.SetBool("outlineOff", true);
outlineAnim.SetBool("outlineOn", false);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OnMouseOverEvent : MonoBehaviour
{
public GameObject[] objects;
private Vector3[] originalpos;
private void Start()
{
originalpos = new Vector3[objects.Length];
for (int i = 0; i < objects.Length; i++)
{
originalpos[i] = objects[i].transform.position;
}
}
private void Update()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.transform.tag == "Test")
{
// if (transform.position.z != originalpos.z - 3)
// StartCoroutine(moveToX(transform, new Vector3(transform.position.x, transform.position.y, transform.position.z - 3), 0.1f));
}
else
{
// StartCoroutine(moveToX(transform, originalpos, 0.1f));
}
}
else
{
// reset
// StartCoroutine(moveToX(transform, originalpos, 0.1f));
}
}
bool isMoving = false;
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
isMoving = false;
}
}
The script was working fine when objects and originalpos were singles.
But now I made them arrays since I have more then one gameobject.
I have 3 gameobjects tagged : "Test" , "Test1" , "Test2"
I want to perform them same action but for each object hit.
If hitting "Test" move only "Test" on z - 3 and then back to it's original pos.
If hitting "Test1" move only "Test1" on z - 3 and then back to it's original pos. And same for "Test2".
Make the same action but only for the hitting object.
You can use Physics.RaycastAll, it returns RaycastHit[] which you can loop through.
Like so:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, 100f);
// For each object that the raycast hits.
foreach (RaycastHit hit in hits) {
if (hit.collider.CompareTag("Test")) {
// Do something.
} else if (hit.collider.CompareTag("Test1")) {
// Do something.
} else if (hit.collider.CompareTag("Test2")) {
// Do something.
}
}
As you can see, I started with an object in a scene and attached a script to it along with other components. https://imgur.com/z9Tooh9
It looks barren due to there not actually being a model in there. However, when the game is started, it is created by another script in an object in the hierarchy. https://imgur.com/guQQlJO
You can also see that the script works as expected and detects all the skinned mesh renderers and allows you to adjust the colors accordingly.
However, this is where the problem comes in.
When I duplicate this object the first object does what is expected of it but, the second one doesn't.
As you can see, there are no skinned mesh renderers in the list for the second object. https://imgur.com/zTRHL9F
Naturally, I put debug logs at the point where it detects the skinned mesh renderers to see what the issue is:
void OnMouseEnter()
{
Debug.Log("Mouse Entered");
foreach (SkinnedMeshRenderer element in skinnedMeshRenderersScan) //For
every object it finds
{
Debug.Log("Detected: " + element);
Debug.Log("Detected Color: " + selectedColor);
element.material.color = selectedColor;
}
}
void OnMouseExit()
{
Debug.Log("Mouse Left");
foreach (SkinnedMeshRenderer element in skinnedMeshRenderersScan) //For
every object it finds
{
//Debug.Log("Detected: " + element);
//Debug.Log("Detected Color: " + deselectedColor);
element.material.color = deselectedColor;
}
}
The first object notes that they're detected. https://imgur.com/fBhXjKj
The second one simply pretends as if the debug log is not even there. https://imgur.com/alE76aY
I'm more than happy to elaborate in the event that you don't quite understand what it is I'm asking.
Many thanks in advance and sorry if my formatting is terrible I'm not the best at it.
I've tried searching for answers online but, I could not locate a solution to my rather unique problem.
The whole script is as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TankController : MonoBehaviour
{
Vector3 targetPosition;
Vector3 lookAtTarget;
public Vector4 selectedColor;
public Vector4 deselectedColor;
Quaternion playerRot;
float rotSpeed = 2;
float speed = 3;
bool moving = false;
public bool Building = false;
public bool selected = false;
public bool CoolingDown = false;
public double CoolDown = .2;
public double original = .2;
MeshRenderer RenderMesh;
MeshRenderer RenderMeshParent;
SkinnedMeshRenderer[] skinnedMeshRenderersScan;
public List<SkinnedMeshRenderer> skinnedMeshRenderersList = new
List<SkinnedMeshRenderer>();
// Use this for initialization
void Start()
{
RenderMesh = GetComponentInChildren<MeshRenderer>();
RenderMeshParent = GetComponentInParent<MeshRenderer>();
skinnedMeshRenderersScan = GetComponentsInChildren<SkinnedMeshRenderer> ();
foreach (SkinnedMeshRenderer element in skinnedMeshRenderersScan) //For every object it finds
{
if (!skinnedMeshRenderersList.Contains(element)) //If it isn't already in this list
{
skinnedMeshRenderersList.Add(element); //Add to the list
}
}
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
if (CoolingDown == false) //If not cooling down
{
SetTargetPosition();
CoolingDown = true; //Set cooling down to true
}
}
if (CoolingDown == true)
{
CoolDown -= Time.deltaTime; //Takes some time away
if (CoolDown <= 0) //Checks if the cooldown is done yet
{
CoolingDown = false; //Sets cooling down to false
CoolDown = original; //Cooldown timer is reset by equalling its original value
}
}
if (moving)
Move();
}
void SetTargetPosition()
{
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, Mathf.Infinity))
{
if (hit.collider.CompareTag("Hittable") == true && selected == true)
{
targetPosition = hit.point;
lookAtTarget = new Vector3(targetPosition.x - transform.position.x,
transform.position.y,
targetPosition.z - transform.position.z);
playerRot = Quaternion.LookRotation(lookAtTarget);
moving = true;
}
if (hit.collider.CompareTag("Unit") == true)
{
Fighting self = GetComponentInChildren<Fighting>();
Fighting other = hit.collider.gameObject.GetComponentInChildren<Fighting>();
PlayerMaster playcheck = GetComponent<PlayerMaster>();
if (CoolingDown == false) //If not cooling down
{
if (gameObject.name == hit.collider.name)
{
if (selected == false)
{
selected = true;
RenderMesh.enabled = !RenderMesh.enabled;
}
else if (selected == true)
{
selected = false;
RenderMesh.enabled = !RenderMesh.enabled;
}
}
CoolingDown = true; //Set cooling down to true
}
}
}
}
}
void Move()
{
if (Building == false)
{
transform.rotation = Quaternion.Slerp(transform.rotation,
playerRot,
rotSpeed * Time.deltaTime);
transform.position = Vector3.MoveTowards(transform.position,
targetPosition,
speed * Time.deltaTime);
if (transform.position == targetPosition)
moving = false;
}
}
void OnMouseEnter()
{
Debug.Log("Mouse Entered");
foreach (SkinnedMeshRenderer element in skinnedMeshRenderersScan) //For every object it finds
{
Debug.Log("Detected: " + element);
Debug.Log("Detected Color: " + selectedColor);
element.material.color = selectedColor;
}
}
void OnMouseExit()
{
Debug.Log("Mouse Left");
foreach (SkinnedMeshRenderer element in skinnedMeshRenderersScan) //For every object it finds
{
//Debug.Log("Detected: " + element);
//Debug.Log("Detected Color: " + deselectedColor);
element.material.color = deselectedColor;
}
}
}
To recap, the expected result is that the second object (Or more) would work the same way the first object does.
In practice, it does not.
I was able to fix this issue by putting the scans in the awake() method.
I'm using my android device to detect the touched position and move my object, but my object change the position way to far it disapears from my camera view, do I need to use ScreenToWorldPoint? It so, how do I use it?
Here is my code:
void Update() {
for (var i = 0; i < Input.touchCount; i++) {
if (Input.GetTouch(i).phase == TouchPhase.Began) {
transform.position = new Vector3 (Input.GetTouch(i).position.x, Input.GetTouch(i).position.y, transform.position.z);
}
}
}
You can try like this:
private void Update()
{
for (var i = 0; i < Input.touchCount; i++)
{
if (Input.GetTouch(i).phase == TouchPhase.Began)
{
var worldPosition = Camera.main.ScreenToWorldPoint(Input.GetTouch(i).position);
transform.position = worldPosition;
}
}
}
You can try this,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SelectObject : MonoBehaviour {
// Use this for initialization
GameObject hitObj;
RaycastHit hit;
private float speed = 1;
void Start () {
}
// Update is called once per frame
void Update () {
foreach (Touch touch in Input.touches) {
switch (touch.phase) {
case TouchPhase.Began:
Ray ray = Camera.main.ScreenPointToRay (touch.position);
if (Physics.Raycast (ray, out hit, 10)) {
hitObj = hit.collider.gameObject;
}
break;
case TouchPhase.Moved:
// If the finger is on the screen, move the object smoothly to the touch position
float step = speed * Time.deltaTime; // calculate distance to move
if(hitObj != null)
hitObj.transform.position = Camera.main.ScreenToWorldPoint(new Vector3 (touch.position.x, touch.position.y, hitObj.transform.position.z));
break;
}
}
}
}
This is a follow on from my last question now I'm looking to extend it's functionality but I'm hitting some major road blocks.
My goal is as follows:
I have a camera that is projecting a ray to hit objects with a specific tag. When the ray hits a tag for a set period of time I'm looking for something to happen, just now all I'm looking for is a debug message or something simple. When the ray doesn't hit any object with a tag I'm wanting my timer to not update. If the user looks away from the object and comes back, I'm wanting the timer to start again from 0 and I want similar functionality should the user look another one of the tags i.e the timer starts again.
How ever, my timer isn't working as intended and it just keeps counting no matter what I'm looking. I've spent 3 hours on this and I've gotten tunnel vision where I keep attempting the same lines of code over and over.
Could someone with a fresh pair of eyes take a look at what I've done so far and point out to me what it is I'm missing / doing wrong?
public float end_time;
public float start_time;
private float running_time;
public Texture2D progress_bar_empty;
public Texture2D progress_bar_full;
public Material highlight_material;
public float progress = 0;
public bool hited = false;
public List<string> _tags = new List<string>();
private GameObject hit_object;
private Material oldMat;
RaycastHit hit;
// Use this for initialization
void Start ()
{
start_time = Time.deltaTime;
running_time = 0;
}
// Update is called once per frame
void Update ()
{
EyeTarget();
Check(hited);
}
void EyeTarget()
{
try
{
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, fwd, out hit))
{
foreach(string t in _tags)
{
if(hit.collider.gameObject.tag == t)
{
HighLight();
}
}
}
Debug.DrawRay(transform.position, fwd, Color.red);
}
catch(Exception e)
{
Debug.Log(e.Message);
}
}
void ResetTimer()
{
start_time = Time.time;
running_time = 0f;
//Debug.Log("resetting timer");
}
void HighLight()
{
if(hit_object == null)
{
ResetTime();
oldMat = hit.transform.renderer.material;
hit.collider.gameObject.renderer.material = highlight_material;
hit_object = hit.collider.gameObject;
hited = true;
}
else if( hit.transform.tag != hit_object.tag)
{
//hit.collider.gameObject.renderer.material = oldMat;
hit_object = null;
hit_object.renderer.material = oldMat;
progress = 0;
Debug.Log("hi");
hited = false;
}
}
// see if ray has hit object
void Check(bool hit)
{
if(hit)
{
start_time = Time.time - end_time;
running_time += Time.deltaTime;
if ( running_time >= end_time )
{
hited = false;
}
}
else if( hited == false)
ResetTime();
}
void ResetTime()
{
start_time = Time.time;
running_time = 0f;
Debug.Log("restting timer");
}
I tried to leave your version alone as much as possible. This version will stop the timer when the cursor moves off a tagged object and will start again, from 0, when another tagged object is under the raycast.
My code is a little verbose but it makes it easier to see what's going on.
public float end_time;
public float start_time;
public float running_time;
public Texture2D progress_bar_empty;
public Texture2D progress_bar_full;
public Material highlight_material;
public float progress = 0;
public bool trackTimer = false;
public List<string> _tags = new List<string>();
public GameObject lastHitObject = null;
private Material oldMat;
// Use this for initialization
void Start ()
{
ResetTimer();
}
// Update is called once per frame
void Update ()
{
EyeTarget();
// Update the timer if and only if we are tracking time AND
// the last ray cast hit something.
bool updateTimer = (trackTimer && lastHitObject != null);
Check(updateTimer);
}
void EyeTarget()
{
RaycastHit hit;
bool hitTaggedObject = false;
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, fwd, out hit))
{
foreach(string t in _tags)
{
if(hit.collider.gameObject.tag == t)
{
HighLight(hit.collider.gameObject);
hitTaggedObject = true;
}
}
}
// ** Make sure to clean up highlighting if nothing new was hit
if (!hitTaggedObject){
HighLight(null);
}
}
void ResetTimer()
{
start_time = Time.time;
running_time = 0f;
}
void HighLight(GameObject nextHitObject)
{
// Case1: Last ray and new ray both hit objects
if(lastHitObject != null && nextHitObject != null){
//1a: same objects, do nothing
if(lastHitObject.tag == nextHitObject.tag)return;
{ //1b: different objects, swap highlight texture
lastHitObject.renderer.material = oldMat;
lastHitObject = nextHitObject;
oldMat = lastHitObject.renderer.material;
lastHitObject.renderer.material = highlight_material;
trackTimer = true;
return;
}
}
// Case2: Last ray hit nothing, new ray hit object.
if(lastHitObject == null && nextHitObject != null){
ResetTimer();
lastHitObject = nextHitObject;
oldMat = lastHitObject.renderer.material;
lastHitObject.renderer.material = highlight_material;
trackTimer = true;
return;
}
// Case3: Last ray hit something, new ray hit nothing
if(lastHitObject != null && nextHitObject == null){
lastHitObject.renderer.material = oldMat;
lastHitObject = null;
trackTimer = false;
return;
}
}
// see if ray has hit object
void Check(bool updateTimer)
{
if(updateTimer)
{
start_time = Time.time - end_time;
running_time += Time.deltaTime;
if ( running_time >= end_time )
{
trackTimer = false;
}
}
}
trackTimer is a state flag, separate from the state of your selected object, that tracks when running_time reached end_time. Once those 2 are equal, trackTimer flips to false and you need to highlight a new object or rehighlight the current object before the timer will start again.