Create objects with ground plane detection only once with Vuforia & Unity - c#

I am trying to create an AR app using Unity & Vuforia. I have a 3D model that needs to be spawned when ground plane is detected.But this needs to happen only once.
The way Vuforia work is, it keeps on spawning objects when new plane is detected. So what i need to do is either detect plane only once or spawn the object only once. As i am new to Unity, i need help doing this. Great if someone could tell me what i need to do to achieve this.

Vuforia has updated.Now There is no DeploymentStageOnce script.Inorder to stop duplicating while we touch, we have to turn off Duplicate Stage in Content Positioning Behaviour (Script)Check the Inspector when we click Plane Finder.

In your app you should have a Plane Finder object somewhere with the following properties set by default
The Plane Finder object has a Behaviour component attached that calls a Position Content method if a plane was found. That method belongs to the Content Positioning Behaviour and it makes an instance (Clone) of your Ground Plane Stage. In order to avoid more than one instance you should import the vuforia Deploy Stage Once script located here: https://library.vuforia.com/articles/Solution/ground-plane-guide.html and you should change the Plane Finder Behaviour as the following:

I struggled a long with it, in short we must disable AnchorInputListenerBehaviour after hit.
I attached a new script on PlaneFinder with this code below:
<!-- language-all: c# -->
public void OnInteractiveHitTest(HitTestResult result)
{
var listenerBehaviour = GetComponent<AnchorInputListenerBehaviour>();
if (listenerBehaviour != null)
{
listenerBehaviour.enabled = false;
}
}
I added event on Plane Finder Behavior
That's all, I hope it will be useful.

For Updated Versions:
go to "Advanced" setting and "On Interactive Hit Test" script -> Select "Off" option for the script.

Most of the answers are correct but kind of obsolete, the correct way to do that is by code.
Create for example a gameObject called GameManager and pass the GroundPlaneStage and a prefab of the object you want to spawn to a script attached to that GameManager for example call it GameManagerScript.cs, and create a small function called spawnObjects which does the following:
public class SceneManagerScript : MonoBehaviour {
public GameObject objPrefab;
public GameObject ground;
private int count = 0;
public void spawnObject() {
Instantiate(objPrefab, new Vector3(count, 0, 0), Quaternion.identity, ground.transform);
count += 2;
}
}
then after that go to the PlaneFinder specifically to the PlaneFinderBehaviour.cs component you will have callbacks for OnInteractiveHitTest and OnAutomaticHitTest, in your case you need the OnAutomativeHitTest, click + and add a new callback (the function spawnObject in code above like in the image below)
also when you instantiate the object of your preference via the prefab don't forget to write the proper position updates to prevent the objects from getting added in the same position
also don't forget to make the GroundPlaneStage the parent of the object and realize that the position you are adding in the Instantiate() function is relative to that parent (GroundPlaneStage which is represented in the code above with the variable ground)
Finally don't forget to uncheck Duplicate Stage from the "Content Positioning Behaviour" component in the Plane Finder as shown in the picture below:
I hope that helps

please try the vuforia website for this problem
Introduction to Ground Plane in Unity

Related

How to turn on a prefab with code in C# and unity

I am new to C# and I am working on an AR project where I am showing directions to a section of books in a library. I have a basic scene set up where the user clicks on a button and something happens. I am using the Mixed Reality Tool Kit's directional indicator. I created 4 game objects that represent different subjects and are placed at different points in space in the scene. I have a directional indicator for each of those game objects and those directional indicators are prefabs. They are an arrow that points to that game object, so what I am trying to do is have them turned off and when the user clicks the button, it turns them on.
Prefabs are just files that contain GameObject, they aren't directly connected to the scene. If you want to create new GameObject from Prefab in runtime you can Instantiate it, and then keep reference to this new GameObject.
You also can check similar question, maybe it will help.
Code example
class SomeClass: MonoBehaviour
{
private GameObject newGameObject;
...
public void InstantiatePrefab(GameObject prefab)
{
newGameObject = Instantiate(prefab, ...);
}
public void SetGameObjectState(bool state)
{
newGameObject.SetActive(state);
}
}

Instantiating a U.I object at the position of another object while keeping the Canvas as the parent

Ran into a U.I problem that I am struggling to solve (UI is always an issue for me). Gist of the project is that I have an array of spawn points (Positions[i]), an array composed of enemy prefabs(Enemies[j]) and just a UI panel prefab with a text component (EnemyHUD). When I pass string names through a certain function, as long as there is a prefab with the same name and position available to it, it will load in the enemy. Now, for every enemy prefab loaded in, I would like a "EnemyHUD" prefab to instantiate at at the enemy positions and with text displaying the name of the enemy.
GameObject HUD = Instantiate(EnemyHUB, Positions[i].position, Positions[i].rotation);
This line spawns the EnemyHUD prefab at the right location but its Instantiates them outside of the canvas so they show up as red x's. So I added this:
HUD.transform.SetParent(GameObject.Find("Canvas").transform, false);
This fixes the issue of Instantiating outside of the canvas but also (irritatingly)resets the Instantiate position and embarrassingly, I'm not sure how to set it back while making sure it stays a child of the Canvas. Have not event touched the name change part yet.
I have been working on this this since last night and while I got a lot of different results experimenting, none have been the one that I want. Very much still a novice, so I am sure it is looking me in the face so please help me find it.
First of all I would strongly recommend to avoid Find whenever possible! Rather already reference the according Canvas and store its reference. Especially the i lets assume you are doing this in a loop so using Find repeatedly is extremely inefficient!
// Drag you Canvas object into this field via the Inspector in Unity
[SerializeField] private Canvas _parentCanvas;
private void Awake()
{
// use this only as fallback an only ONCE
if(!_parentCanvas) _parentCanvas = GameObject.Find("Canvas");
if(!_parentCanvas) Debug.LogError("Could not get Canvas!", this);
}
Well and then the second parameter of Transform.SetParent is called
worldPositionStaysIf true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.
And
The default value of worldPositionStays argument is true.
Simply either leave it out like
HUD.transform.SetParent(_parentCanvas);
// this equals
//HUD.transform.SetParent(_parentCanvas, true);
or you could even do it in one single go already in Object.Instantiate which takes an additional parameter
parent Parent that will be assigned to the new object.
// This directly instantiates the new hud as child of _parentCanvas
// while still taking the position and rotation in absolute worldspace
GameObject HUD = Instantiate(EnemyHUB, Positions[i].position, Positions[i].rotation, _parentCanvas);

Vuforia + Unity : Instantiate model in AR when UI Button pressed

I know this is a really simple question but I can't figure out how to archive this:
I have a UI button in my scene and I want Vuforia to instantiate one AR Model ONLY when I press a button.
Following the tutorial on the net I was able to instantiate a model on the screen when I touch it but I need to know how to set up Vuforia for archive the same result only when I press a button.
I have to disable the "Anchor Input Listener Behaviour"?
And then?
I want to call for the PositionContentAtPlaneAnchor but I can't figure out how to call it in the right way in the OnClick field of the button. I need to make a custom script for this?
Thanks for any answer.
Ok, sorry for the delay.
I deduce that you are working with the ground plane, if you have the Ground Plane Stage and the Plane Finder in the scene and works, we're at a good point.
Now, you must only add a button to the scene and into the script add something like this:
public PlaneFinderBehaviour plane;
void Start()
{
...
buttonOnTheScene.onClick.AddListener(TaskOnClick);
...
}
void TaskOnClick()
{
Vector2 aPosition = new Vector2(0,0);
...
plane.PerformHitTest(aPosition);
}
What does it mean?
First of all, you must move the Plane Finder from Hierarchy to the script variable, so we have a reference to the plane into the script.
Then when you click (or tap) on the button you simulate the click (or tap) on the display with the PerformHitTest.
If you're wondering why my question in the comment, it's because the Plane Finder Behaviour Script has two modes type: Interactive and Automatic. The Interactive intercept the tap on the display and shows the object (on the ground plane) in the exact position of the tap, the automatic shows the object in the center of the plane.
So if you want the object in an exact position you can pass a Vector2 position in the PerformHitTest and if you want to show an object programmatically or do something when it shows the object you can call a custom method OnInteractiveHitTest.
That's all.

Unity instantiates multiple objects instead of one - on click

Here's the code I'm having problems with:
public class PlaneBehaviour : MonoBehaviour {
public GameObject ClickSymbol;
public void Update() {
if (Input.GetMouseButtonDown(0)) {
Instantiate(ClickSymbol, Input.mousePosition, Quaternion.identity);
}
}
}
When user clicks the left mouse button, then multiple ClickSymbol's will be instantiated. How to prevent that behavior (create the object only once) ?
As for the PlaneBehaviour script, it's attached to multiple (not
intersecting, not overlapping) objects on a plane
That's what I thought you are doing and wanted to verify this before adding an answer. Joe's answer should work but this is a bad design.
You get the multiple Objects instead of one because the script is attached to multiple Objects.
The proper solution here is to have one script that reads from the Input then call a function or trigger an event in another script if required. Create a new script called InputReader or with similar name then move your if (Input.GetMouseButtonDown(0))... Make that that this new script is only attached to one GameObject. Just attach it to an empty GameObject. That's it.
It will prevent problems such as executing code twice when there is an input event.
Now, let;s say that you want to notify another script attached to another GameObject when key is pressed, you can find that GameObject, get that script that is attached to it then call its function.
GameObject obj = GameObject.Find("YourOtherObjectName");
Script script = obj.GetComponent<Script >();
script.notifyKeyPress();
I attached it to multiple game objects to be able to detect which game
object was clicked
That's not how to detect which GameObject is clicked. Since this is a plane, I assume it is a 3D Object with a 3D collider. See 6.For 3D Object (Mesh Renderer/any 3D Collider) from my other post on how to do this.

Instantiate gameobject from a script that is on that gameobject

I have a script called DialogueController, which is attached to a canvas. I would like to instantiate that canvas from the DialogueController script. Whenever I attempt to do this, all of the public objects that were assigned to the canvas through the Unity editor are set to null (including the canvas itself, which is what I am trying to instantiate).
Is there a simple way to do this? I have an alternative solution, but being able to do this would keep my code slightly more compartmentalized.
You could initialize these variables from the script itself, using the resources folders (and the Resources.Load() method). Something like that:
//The canvas is at the path "Assets/Resources/canvas".
function Start () {
your_canvas = Instantiate(Resources.Load("canvas"));
}
From the comments, it seems like you need an initial "seed" instance of that Canvas which contains your script to instantiate more copies if you need. Part 2 is most common for your needs
The easiest way is to have one instance of said canvas already in the scene. You could have the drawable/visible canvas part "disabled" by unticking the little checkbox in the inspector, and then have it enable itself in a function of a script on the GameObject...
void Start ()
{
thisCanvas = GetComponent<Canvas>();
thisCanvas.enabled = true;
}
of course, another way would just be to instantiate one copy from another script which you already have in the scene - classic case:
Create a BLANK GameObject in your scene (CTRL+Shift+N), [F2] rename it "GameManager" (SOME unity functions still bug out when there are spaces in GameObject names BTW) and then attach a new script called GameManager.
This script is basically just here to make sure the right scenes load, and instantiate certain prefabs, make network connections, set player variables that you haven't done from the editor etc etc
so:
using UnityEngine;
using System.Collections;
public class GameManager : MonoBehaviour
{
public GameObject myObject;
void Start ()
{
Instantiate(myObject, transform.position, transform.rotation);
}
}
now drag+drop your "prefab" which you want to instantiate into the slot on your "GameManager" in the inspector.
Note that it doesn't have to be when the game starts, another example is the GameManager script having a function listening for whenever a login is needed, and at that time - it instantiates your login dialog.

Categories

Resources