Dynamically create a plane between 4 known points in Unity ARCore for Android - c#

I am trying to dynamically create a plane between 4 known points. The 4 points are created by the user touching the phone screen and a cylinder is placed where they touched, very similar to HelloAR tutorial except with cylinders and not Andys. This is working, the 4 cylinders get created. In a hypothetical situation the user is going to create a square with the 4 points, let's say 2 meters by 2 meters. I then want to dynamically place a plane that fits into the 4 points (it doesn't have to fit exactly, just roughly). Using adb I have validated that the CreateBricks method is called and the plane is instantiated with the correct transform. I just don't see the plane get created.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using GoogleARCore;
public class SceneController : MonoBehaviour {
public Camera firstPersonCamera;
public Text anchorcount;
public GameObject pointPrefab; //The cylinder prefab
public GameObject patioPrefab; //The plane prefab
public ArrayList pointArray;
private int anchorCount;
private bool m_IsQuitting = false;
private bool brickCreated = false;
// Use this for initialization
void Start () {
QuitOnConnectionErrors();
anchorCount = 0;
pointArray = new ArrayList();
brickCreated = false;
}
// Update is called once per frame
void Update () {
_UpdateApplicationLifecycle();
if (anchorCount < 4)
ProcessTouches();
else if(!brickCreated)
CreateBricks();
}
private void CreateBricks()
{
try
{
brickCreated = true;
Debug.Log("CreateBricks");
float dist1 = Vector3.Distance(((GameObject)pointArray[0]).transform.position, ((GameObject)pointArray[1]).transform.position);
float dist2 = Vector3.Distance(((GameObject)pointArray[0]).transform.position, ((GameObject)pointArray[3]).transform.position);
Debug.Log("dist1:" + dist1.ToString());
Debug.Log("dist2:" + dist2.ToString());
GameObject prefab;
prefab = patioPrefab;
// Instantiate Andy model at the hit pose.
var pointObject = Instantiate(prefab);
prefab.transform.position = ((GameObject)pointArray[0]).transform.position;
prefab.transform.position.Scale(new Vector3(dist1, 0.5f, dist2));
Debug.Log(prefab.transform.position.x.ToString() + " " + prefab.transform.position.y.ToString() + " " + prefab.transform.position.z.ToString());
}
catch (System.Exception ex)
{
Debug.Log(ex.Message);
}
}
void ProcessTouches()
{
Touch touch;
if (Input.touchCount != 1 ||
(touch = Input.GetTouch(0)).phase != TouchPhase.Began)
{
return;
}
// Raycast against the location the player touched to search for planes.
TrackableHit hit;
TrackableHitFlags raycastFilter = TrackableHitFlags.PlaneWithinPolygon |
TrackableHitFlags.FeaturePointWithSurfaceNormal;
if (Frame.Raycast(touch.position.x, touch.position.y, raycastFilter, out hit))
{
// Use hit pose and camera pose to check if hittest is from the
// back of the plane, if it is, no need to create the anchor.
if ((hit.Trackable is DetectedPlane) &&
Vector3.Dot(firstPersonCamera.transform.position - hit.Pose.position,
hit.Pose.rotation * Vector3.up) < 0)
{
Debug.Log("Hit at back of the current DetectedPlane");
}
else
{
// Choose the Andy model for the Trackable that got hit.
GameObject prefab;
prefab = pointPrefab;
// Instantiate Andy model at the hit pose.
var pointObject = Instantiate(prefab, hit.Pose.position, hit.Pose.rotation);
// Compensate for the hitPose rotation facing away from the raycast (i.e. camera).
//andyObject.transform.Rotate(0, k_ModelRotation, 0, Space.Self);
// Create an anchor to allow ARCore to track the hitpoint as understanding of the physical
// world evolves.
var anchor = hit.Trackable.CreateAnchor(hit.Pose);
// Make Andy model a child of the anchor.
pointObject.transform.parent = anchor.transform;
pointArray.Add(pointObject);
anchorCount++;
anchorcount.text = "Points: " + anchorCount;
}
}
}
private void _UpdateApplicationLifecycle()
{
// Exit the app when the 'back' button is pressed.
if (Input.GetKey(KeyCode.Escape))
{
Application.Quit();
}
// Only allow the screen to sleep when not tracking.
if (Session.Status != SessionStatus.Tracking)
{
const int lostTrackingSleepTimeout = 15;
Screen.sleepTimeout = lostTrackingSleepTimeout;
}
else
{
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
if (m_IsQuitting)
{
return;
}
QuitOnConnectionErrors();
}
private void _ShowAndroidToastMessage(string message)
{
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
if (unityActivity != null)
{
AndroidJavaClass toastClass = new AndroidJavaClass("android.widget.Toast");
unityActivity.Call("runOnUiThread", new AndroidJavaRunnable(() =>
{
AndroidJavaObject toastObject = toastClass.CallStatic<AndroidJavaObject>("makeText", unityActivity,
message, 0);
toastObject.Call("show");
}));
}
}
void QuitOnConnectionErrors()
{
// Quit if ARCore was unable to connect and give Unity some time for the toast to appear.
if (Session.Status == SessionStatus.ErrorPermissionNotGranted)
{
_ShowAndroidToastMessage("Camera permission is needed to run this application.");
m_IsQuitting = true;
Invoke("_DoQuit", 0.5f);
}
else if (Session.Status.IsError())
{
_ShowAndroidToastMessage("ARCore encountered a problem connecting. Please start the app again.");
m_IsQuitting = true;
Invoke("_DoQuit", 0.5f);
}
}
}

The issue with the above code was resolved by setting the parent of the pointObject variable in the Instantiate object. The plane then showed up relative to the parent object.

Related

How to accurately get the objects that the camera is directly pointed to?

I'm trying to pick up objects on my game. so the object that the user is directly looking at can be picked up on a button click. I have LookObject which is a gameObejct that stores the objects that I'm currently looking at. The issue I'm facing here is that the LookObject is NOT accurately showing the objects that I'm looking at.
[Header("InteractableInfo")]
public float sphereCastRadius = 0.5f;
public int interactableLayerIndex;
private Vector3 raycastPos;
public GameObject lookObject;
private PhysicsObjects physicsObject;
private Camera mainCamera;
public GameObject winUI;
private InteractiveObjects interactiveObjects;
void Update()
{
//Here we check if we're currently looking at an interactable object
raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
if (Physics.SphereCast(raycastPos, sphereCastRadius, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
lookObject = hit.collider.transform.gameObject;
}
//if we press the button of choice
if (Input.GetKeyDown(KeyCode.Space))
{
//and we're not holding anything
if (currentlyPickedUpObject == null)
{
//and we are looking an interactable object
if (lookObject != null )
{
PickUpObject();
}
}
}
//if we press the pickup button and have something, we drop it
else
{
BreakConnection();
}
}
public void BreakConnection()
{
pickupRB.constraints = RigidbodyConstraints.None;
currentlyPickedUpObject = null;
physicsObject.pickedUp = false;
currentDist = 0;
}
public void PickUpObject()
{
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
}
[Here is the cube inspector][2]
The cube and all other interactable objects have this script:
public class PhysicsObjects : MonoBehaviour
{
public float waitOnPickup = 0.2f;
public float breakForce = 35f;
[HideInInspector] public bool pickedUp = false;
[HideInInspector] public PlayerInteractions playerInteractions;
private void OnCollisionEnter(Collision collision)
{
if (pickedUp)
{
if (collision.relativeVelocity.magnitude > breakForce)
{
playerInteractions.BreakConnection();
}
}
//this is used to prevent the connection from breaking when you just picked up the object as it sometimes fires a collision with the ground or whatever it is touching
public IEnumerator PickUp()
{
yield return new WaitForSecondsRealtime(waitOnPickup);
pickedUp = true;
}
}
You are doing
if (Physics.SphereCast(raycastPos, sphereCastRadius, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
lookObject = hit.collider.transform.gameObject;
}
with a radius of 0.5 meters!
Then next you do
Physics.Raycast(mainCamera.transform.position, mainCamera.transform.forward, out hit, maxDistance);
without any layer filter so you hit e.g. the floor.
So the check
if (hit.transform)
{
interactiveObjects = hit.transform.GetComponent<InteractiveObjects>();
}
else
{
lookObject = null;
interactiveObjects = null;
}
enters the if case, even then when the hit.transform of the second cast is not he same as the first one.
I don't really understand why you do two casts in the first place. You probably rather want to simply stick with
if (Physics.SphereCast(mainCamera.ViewportPointToRay(Vector2.one * 0.5f), sphereCastRadius, out var hit, maxDistance, 1 << interactableLayerIndex))
// or if you don't want to use a sphere cast anyway
//if(Physics.Raycast(mainCamera.ViewportPointToRay(Vector2.one * 0.5f), maxDistance, 1 << interactableLayerIndex))
{
var lookObject = hit.collider.gameObject;
if(lookObject.TryGetComponent<InteractiveObjects>(out var interactiveObject))
{
if (Input.GetKeyDown(KeyCode.Space))
{
//and we're not holding anything
if (!currentlyPickedUpObject)
{
// whatever happens in here
PickUpObject(interactiveObject);
}
}
}
}
and simply use a smaller radius for the sphere cast or as commented use the simple RayCast directly if you don't want to use a radius anyway.
in general I would also recommend to rather use a
public LayerMask interactableLayers;
and then directly use interactableLayers instead of 1 << interactableLayerIndex.

Clamp action distance in isometric grid

I am currently working on a 2D isometric game where the player will be able to control different units, and interact through them.
I created a Scriptable Object called UnitType. Thanks to this system I can define an action range and a moving range, that is the maximum distance in cells that the player can move to or interact with.
The problem is I don't know how to implement this through code. This is what I want to achieve, using an action range of 2 cells for the demonstration.
This is the goal
With a friend of mine, we thought about calculating the linear equation of those 4 lines to check if the raycast hit was within them, but it's not working right with negative x.
This is the current system
What would be the best practice ?
Thank you very much for your time and attention,
Mousehit is a Unity GameObject that is the child of the character. Its positions is bound to the center of the tilemap cells thanks to mousePosition that is a script attached to the GameManager.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class Interact : MonoBehaviour
{
[SerializeField] private Sprite upperLeft, upperRight, midLeft, midRight, bottomLeft, bottomMid, bottomRight;
[SerializeField] private GameObject mouseHit;
public UnitType unitType;
private GameObject gameManager;
private CheckMousePosition mousePosition;
private bool isInteracting, isAtGoodDistance;
private SpriteRenderer spriteRenderer;
private Tilemap tilemap;
private RaycastHit2D hit;
//private Transform mouseHit;
void Start()
{
tilemap = GameObject.Find("Tilemap").GetComponent<Tilemap>();
gameManager = GameObject.FindGameObjectWithTag("GameManager");
mousePosition = gameManager.GetComponent<CheckMousePosition>();
spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
isInteracting = false;
mouseHit.SetActive(false);
}
// Update is called once per frame
void Update()
{
if (isInteracting)
{
mouseHit.SetActive(true);
mouseHit.transform.position = new Vector3(mousePosition.tilemap.GetCellCenterLocal(mousePosition.cellPosition).x, mousePosition.tilemap.GetCellCenterLocal(mousePosition.cellPosition).y, 1);
if (Input.GetMouseButtonDown(0))
{
Interaction();
}
}
if (isInteracting == false)
{
spriteRenderer.sprite = null;
mouseHit.SetActive(false);
}
}
public void InitInteraction()
{
isInteracting = true;
transform.root.GetComponent<ContextualMenu>().CloseContextualMenu();
}
private void Interaction()
{
//When the player interacts, we cast a ray that will determine what he is interacting with.
hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (hit.collider != null)
{
if (isInteracting)
{
isInteracting = false; //the player is no longer interacting.
//If the player is interacting with a farmable cell using the worker.
if (transform.root.name == "Worker"
&& hit.collider.GetComponent<FarmableCell>() != null)
{
CheckInteractionDistance();
//hit.collider.GetComponent<FarmableCell>().CheckIfFarmed();
}
}
}
}
private void CheckInteractionDistance()
{
if (mouseHit.transform.localPosition.y <= (-0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x >= (-0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x >= (0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x <= (0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2))
{
Debug.Log("ok");
}
else
{
Debug.Log("not ok");
}
}
}

Why is my line renderer not showing up in Unity 2D

I am trying to create a Fruit Ninja style game on Unity 2D and I want to create a trail that follows where the player has "cut". I've tried to instantiate a "cut" object that contains the line renderer every time a user drags. However, the line renderer is not showing up. Can anyone correct any errors or suggest a new method?
public class CreateCuts : MonoBehaviour
{
public GameObject cut;
public float cutDestroyTime;
private bool dragging = false;
private Vector2 swipeStart;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
dragging = true;
swipeStart = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
else if (Input.GetMouseButtonUp(0) && dragging)
{
createCut();
}
}
private void createCut()
{
this.dragging = false;
Vector2 swipeEnd = Camera.main.ScreenToWorldPoint(Input.mousePosition);
GameObject cut = Instantiate(this.cut, this.swipeStart, Quaternion.identity) as GameObject;
cut.GetComponent<LineRenderer>().positionCount = 1 ;
cut.GetComponent<LineRenderer>().enabled = true;
cut.GetComponent<LineRenderer>().SetPosition(0, this.swipeStart);
cut.GetComponent<LineRenderer>().SetPosition(1, swipeEnd);
Vector2[] colliderPoints = new Vector2[2];
colliderPoints[0] = new Vector2(0.0f, 0.0f);
colliderPoints[1] = swipeEnd - swipeStart;
cut.GetComponent<EdgeCollider2D>().points = colliderPoints;
Destroy(cut.gameObject, this.cutDestroyTime);
}
}
I expect there to be a line, but nothing shows up. There is also a warning stating that the SetPosition(1, swipeEnd) is out of bounds.
EDIT: Here are the settings of my cut object
1st part of cut object settings
2nd part of cut object settings
Positions tab of line renderer
I want to create a trail that follows where the player has "cut".
The word "trail" indicates that you should rather use a trail renderer!
Manual: https://docs.unity3d.com/Manual/class-TrailRenderer.html
API reference: https://docs.unity3d.com/ScriptReference/TrailRenderer.html
Back to your original question:
Your linerenderer probably is rendered but at a random position, because of Vector2 to Vector3 conversion, i dunno your project structure but this can be the case.
Please post a picture with one of your cut gameobject, that holds your linerenderer, and also extend the positions tab on the linerenderer so we can see your points xyz coordinates
Also apply the changes mentioned by commenters, because you really need 2 verticies for a line :P
Update:
public class CreateCuts : MonoBehaviour
{
public GameObject cut;
public float cutDestroyTime;
private bool dragging = false;
private Vector3 swipeStart;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
dragging = true;
swipeStart = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Debug.Log("Swipe Start: " + swipeStart);
}
else if (Input.GetMouseButtonUp(0) && dragging)
{
createCut();
}
}
private void createCut()
{
this.dragging = false;
Vector3 swipeEnd = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Debug.Log("SwipeEnd: " + swipeEnd);
GameObject cut = Instantiate(this.cut, swipeStart, Quaternion.identity);
cut.GetComponent<LineRenderer>().positionCount = 2;
// why is it not enabled by default if you just instantiate the gameobject O.o?
cut.GetComponent<LineRenderer>().enabled = true;
cut.GetComponent<LineRenderer>().SetPositions(new Vector3[]{
new Vector3(swipeStart.x, swipeStart.y, 10),
new Vector3(swipeEnd.x, swipeEnd.y, 10)
// z is zero cos we are in 2d in unity up axis is Y we set it to 10 for visibility reasons tho}
});
// Commented out cos atm we are "debugging" your linerenderer
// Vector2[] colliderPoints = new Vector2[2];
// colliderPoints[0] = new Vector2(0.0f, 0.0f);
// colliderPoints[1] = swipeEnd - swipeStart;
// cut.GetComponent<EdgeCollider2D>().points = colliderPoints;
//Destroy(cut.gameObject, this.cutDestroyTime);
}
}

Script only working on one object in scene

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.

ARKit - place object without touching the screen

Using ARKit I can tap on the surface to place 3D object here. I also can move my finger and thus move the object along the surface.
How can I make an object to automatically appear and stick to the surface in front of the camera, with no need in touching the screen?
Here is the example script for placing 3D objects by finger tap:
using System;
using System.Collections.Generic;
namespace UnityEngine.XR.iOS
{
public class UnityARHitTestExample : MonoBehaviour
{
public Transform m_HitTransform;
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 true;
}
}
return false;
}
// Update is called once per frame
void Update () {
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 in resultTypes)
{
if (HitTestWithResultType (point, resultType))
{
return;
}
}
}
}
}
}
}
I've done something similar in Objective-C, so hopefully I can help with your unity example.
My logic was basically that placing the objects using a tap is based on the hitTest function with a point provided to it retrieved from the touch location. So I just created a CGPoint programmatically in the center of the screen and ran the hitTest function with this point. Had it hooked to a timer, and when a hit returned objects were added there.
CGPoint point = CGPointMake(self.sceneView.frame.size.width / 2, self.sceneView.frame.size.height / 2); //Get a point at the middle of the screen
NSArray <ARHitTestResult *> *hitResults = [_sceneView hitTest:point types:ARHitTestResultTypeFeaturePoint]; //Try to do a AR Hit Test on this point
if ([hitResults count] != 0) { //If have any results
//Perform desired operation
}

Categories

Resources