I'm new to C#, HoloLens, and Unity. I want to add the voice command functionality from Holograms 101 to Holograms 230.
Having completed Holograms 230, I've taken the SpeechManager script from Holograms 101, added some Debug.Log commands, and dragged it onto an object in Unity's "Hierarchy" panel. When I click the "Play" button in Unity, my script runs (as verified by the fact that my Debug.Log appears in the status bar). But when I build and deploy it to the HoloLens (via "Debug -> Start Debugging" in Visual Studio), the Debug.Logs from my new script don't show up in Visual Studio's "Output" panel, even through Debug.Logs from other scripts do.
Here's my SpeechManager.cs:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Windows.Speech;
public class SpeechManager : MonoBehaviour {
KeywordRecognizer keywordRecognizer = null;
Dictionary<string, System.Action> keywords = new Dictionary<string, System.Action>();
// Use this for initialization
void Start () {
Debug.Log("!!Starting speech manager");
keywords.Add("Place item", () =>
{
Debug.Log("!!Place item");
});
// Tell the KeywordRecognizer about our keywords.
keywordRecognizer = new KeywordRecognizer(keywords.Keys.ToArray());
// Register a callback for the KeywordRecognizer and start recognizing!
keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
keywordRecognizer.Start();
}
private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
{
System.Action keywordAction;
if (keywords.TryGetValue(args.text, out keywordAction))
{
keywordAction.Invoke();
}
}
}
Here's another script, attached to the same object in Unity, that functions correctly and triggers Debug.Logs:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Windows.Speech;
using Academy.HoloToolkit.Unity;
/// <summary>
/// The SurfaceManager class allows applications to scan the environment for a specified amount of time
/// and then process the Spatial Mapping Mesh (find planes, remove vertices) after that time has expired.
/// </summary>
public class PlaySpaceManager : Singleton<PlaySpaceManager>
{
[Tooltip("When checked, the SurfaceObserver will stop running after a specified amount of time.")]
public bool limitScanningByTime = true;
[Tooltip("How much time (in seconds) that the SurfaceObserver will run after being started; used when 'Limit Scanning By Time' is checked.")]
public float scanTime = 30.0f;
[Tooltip("Material to use when rendering Spatial Mapping meshes while the observer is running.")]
public Material defaultMaterial;
[Tooltip("Optional Material to use when rendering Spatial Mapping meshes after the observer has been stopped.")]
public Material secondaryMaterial;
[Tooltip("Minimum number of floor planes required in order to exit scanning/processing mode.")]
public uint minimumFloors = 1;
[Tooltip("Minimum number of wall planes required in order to exit scanning/processing mode.")]
public uint minimumWalls = 1;
/// <summary>
/// Indicates if processing of the surface meshes is complete.
/// </summary>
private bool meshesProcessed = false;
/// <summary>
/// GameObject initialization.
/// </summary>
private void Start()
{
// Update surfaceObserver and storedMeshes to use the same material during scanning.
SpatialMappingManager.Instance.SetSurfaceMaterial(defaultMaterial);
// Register for the MakePlanesComplete event.
SurfaceMeshesToPlanes.Instance.MakePlanesComplete += SurfaceMeshesToPlanes_MakePlanesComplete;
}
/// <summary>
/// Called once per frame.
/// </summary>
private void Update()
{
// Check to see if the spatial mapping data has been processed
// and if we are limiting how much time the user can spend scanning.
if (!meshesProcessed && limitScanningByTime)
{
// If we have not processed the spatial mapping data
// and scanning time is limited...
// Check to see if enough scanning time has passed
// since starting the observer.
if (limitScanningByTime && ((Time.time - SpatialMappingManager.Instance.StartTime) < scanTime))
{
// If we have a limited scanning time, then we should wait until
// enough time has passed before processing the mesh.
}
else
{
// The user should be done scanning their environment,
// so start processing the spatial mapping data...
/* TODO: 3.a DEVELOPER CODING EXERCISE 3.a */
// 3.a: Check if IsObserverRunning() is true on the
// SpatialMappingManager.Instance.
if (SpatialMappingManager.Instance.IsObserverRunning())
{
// 3.a: If running, Stop the observer by calling
// StopObserver() on the SpatialMappingManager.Instance.
SpatialMappingManager.Instance.StopObserver();
Debug.Log("!!Stop Observer!!");
}
// 3.a: Call CreatePlanes() to generate planes.
CreatePlanes();
// 3.a: Set meshesProcessed to true.
meshesProcessed = true;
}
}
}
/// <summary>
/// Handler for the SurfaceMeshesToPlanes MakePlanesComplete event.
/// </summary>
/// <param name="source">Source of the event.</param>
/// <param name="args">Args for the event.</param>
private void SurfaceMeshesToPlanes_MakePlanesComplete(object source, System.EventArgs args)
{
/* TODO: 3.a DEVELOPER CODING EXERCISE 3.a */
// Collection of floor and table planes that we can use to set horizontal items on.
List<GameObject> horizontal = new List<GameObject>();
// Collection of wall planes that we can use to set vertical items on.
List<GameObject> vertical = new List<GameObject>();
// 3.a: Get all floor and table planes by calling
// SurfaceMeshesToPlanes.Instance.GetActivePlanes().
// Assign the result to the 'horizontal' list.
horizontal = SurfaceMeshesToPlanes.Instance.GetActivePlanes(PlaneTypes.Table | PlaneTypes.Floor);
// 3.a: Get all wall planes by calling
// SurfaceMeshesToPlanes.Instance.GetActivePlanes().
// Assign the result to the 'vertical' list.
vertical = SurfaceMeshesToPlanes.Instance.GetActivePlanes(PlaneTypes.Wall);
// Check to see if we have enough horizontal planes (minimumFloors)
// and vertical planes (minimumWalls), to set holograms on in the world.
if (horizontal.Count >= minimumFloors && vertical.Count >= minimumWalls)
{
// We have enough floors and walls to place our holograms on...
// 3.a: Let's reduce our triangle count by removing triangles
// from SpatialMapping meshes that intersect with our active planes.
// Call RemoveVertices().
// Pass in all activePlanes found by SurfaceMeshesToPlanes.Instance.
RemoveVertices(SurfaceMeshesToPlanes.Instance.ActivePlanes);
// 3.a: We can indicate to the user that scanning is over by
// changing the material applied to the Spatial Mapping meshes.
// Call SpatialMappingManager.Instance.SetSurfaceMaterial().
// Pass in the secondaryMaterial.
SpatialMappingManager.Instance.SetSurfaceMaterial(secondaryMaterial);
// 3.a: We are all done processing the mesh, so we can now
// initialize a collection of Placeable holograms in the world
// and use horizontal/vertical planes to set their starting positions.
// Call SpaceCollectionManager.Instance.GenerateItemsInWorld().
// Pass in the lists of horizontal and vertical planes that we found earlier.
SpaceCollectionManager.Instance.GenerateItemsInWorld(horizontal, vertical);
}
else
{
// We do not have enough floors/walls to place our holograms on...
// 3.a: Re-enter scanning mode so the user can find more surfaces by
// calling StartObserver() on the SpatialMappingManager.Instance.
SpatialMappingManager.Instance.StartObserver();
// 3.a: Re-process spatial data after scanning completes by
// re-setting meshesProcessed to false.
meshesProcessed = false;
}
}
/// <summary>
/// Creates planes from the spatial mapping surfaces.
/// </summary>
private void CreatePlanes()
{
// Generate planes based on the spatial map.
SurfaceMeshesToPlanes surfaceToPlanes = SurfaceMeshesToPlanes.Instance;
if (surfaceToPlanes != null && surfaceToPlanes.enabled)
{
surfaceToPlanes.MakePlanes();
}
}
/// <summary>
/// Removes triangles from the spatial mapping surfaces.
/// </summary>
/// <param name="boundingObjects"></param>
private void RemoveVertices(IEnumerable<GameObject> boundingObjects)
{
RemoveSurfaceVertices removeVerts = RemoveSurfaceVertices.Instance;
if (removeVerts != null && removeVerts.enabled)
{
removeVerts.RemoveSurfaceVerticesWithinBounds(boundingObjects);
}
}
/// <summary>
/// Called when the GameObject is unloaded.
/// </summary>
private void OnDestroy()
{
if (SurfaceMeshesToPlanes.Instance != null)
{
SurfaceMeshesToPlanes.Instance.MakePlanesComplete -= SurfaceMeshesToPlanes_MakePlanesComplete;
}
}
}
Related
I have 10 meshes that are each basically freeze-"frames" of an animation of a whale swimming.
If I were to loop through displaying these meshes then it would create a pseudo-animation, which is what I want.
How can this be achieved? Currently I have GameObject AnimTest, and I've placed the 10 mesh .obj files as children to it. I've started with this code on a script changeMeshes for the GameObject:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class changeMeshes : MonoBehaviour
{
//Create array of the meshes
public float[] currentMesh;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < 10; i++)
{
currentMesh[i].gameObject.GetComponent<MeshRenderer>().enabled = true;
}
}
}
This is obviously very wrong since I'm new and confused, so I'm getting errors with script like float does not contain a definition for gameObject. Can anyone help lead me down the correct path?
Edit: Here's my better try at it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class changeMeshes : MonoBehaviour
{
//Create array of the meshes
public GameObject[] whaleMeshes;
void FindWhales()
{
whaleMeshes = GameObject.FindGameObjectsWithTag("WhaleMesh");
print(whaleMeshes.Length);
}
void CycleWhales()
{
for (int i = 0; i < whaleMeshes.Length; i++)
{
if (i > 0)
{
whaleMeshes[i - 1].gameObject.GetComponentInChildren<MeshRenderer>().enabled = false;
}
whaleMeshes[i].gameObject.GetComponentInChildren<MeshRenderer>().enabled = true;
}
}
// Start is called before the first frame update
void Start()
{
FindWhales();
}
// Update is called once per frame
void Update()
{
CycleWhales();
}
}
The result now is that only the last Mesh is permanently rendered, rather than the script cycling through the meshes on a loop. How do I get the meshes to loop?
Well .. a float has no property .gameObject .. not even Mesh would
Since you say the objects are children of this component you can simply iterate through them.
And then I would simply activate and deactivate the entire GameObject.
Sounds like what you would want to do is something like
public class changeMeshes : MonoBehaviour
{
// How long should one mesh be visible before switching to the next one
[SerializeField] private float timePerMesh = 0.2f;
public UnityEvent whenDone;
private IEnumerator Start()
{
// Get amount of direct children of this object
var childCount = transform.childCount;
// Get the first child
var currentChild = transform.GetChild(0);
// Iterate through all direct children
foreach(Transform child in transform)
{
// Set all objects except the first child to inactive
child.gameObject.SetActive(child == currentChild);
}
// Iterate though the rest of direct children
for(var i = 1; i < childCount; i++)
{
// Wait for timePerMesh seconds
yield return new WaitForSeconds(timePerMesh);
// Set the current child to inactive
currentChild.gameObject.SetActive(false);
// Get the next child
currentChild = transform.GetChild(i);
// Set this one to active
currentChild.gameObject.SetActive(true);
}
// Optionally do something when done like e.g. destroy this GameObject etc
yield return new WaitForSeconds(timePerMesh);
whenDone.Invoke();
}
}
I make game with isometric view. When player comes into the house the roof of it hides and player can interact with NPCs, items, etc. But now it can interact with it even when roof is visible. How to detect that item is hidden by house roof or wall or another object?
void Update() {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity);
if (Input.GetMouseButtonDown(0)) {
foreach (RaycastHit hit in hits) {
if (hit.collider.tag != "NPC") {
continue;
}
//Interact ...
}
}
}
You can simply check the distance between the hit wall/roof and NPC, from the ray-cast origin (camera). Like so:
private Camera cameraRef;
private void Awake() {
// P.S: Cache the 'Camera.main', calls to it can be expensive.
cameraRef = Camera.main;
}
void Update() {
if (Input.GetMouseButtonDown(0)) {
Ray ray = cameraRef.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity);
foreach (RaycastHit hit in hits) {
if (hit.collider.tag != "NPC") {
continue;
} else if (RaycastHitRoofOrWallFirst(hits, hit.collider.gameObject)) {
// This NPC is hidden behind a roof/wall.
continue;
}
// Interaction...
}
}
}
/// <summary>
/// Check if a target object is being hidden behind a roof/wall.
/// </summary>
/// <param name="hits">The hits that the raycast gotten.</param>
/// <param name="targetObject">The gameobject to check against.</param>
/// <returns>Return true if the target object is hidden, false if not.</returns>
private bool RaycastHitRoofOrWallFirst(RaycastHit[] hits, GameObject targetObject) {
foreach (RaycastHit hit in hits) {
if (hit.collider.CompareTag("roof") || hit.collider.CompareTag("wall")) {
float distanceFromCameraToObstacle = Vector3.Distance(cameraRef.transform.position, hit.collider.transform.position);
float distanceFromCameraToNPC = Vector3.Distance(cameraRef.transform.position, targetObject.transform.position);
// Check if the NPC is closer to the camera (raycast origin)
// compared to the roof or wall.
if (distanceFromCameraToObstacle < distanceFromCameraToNPC) {
// The roof/wall is closer to the camera (raycast origin)
// compared to the NPC, hence the NPC is blocked by the roof/wall
return true;
}
}
}
return false;
}
Here is a small visual diagram of what it should check for:
Or just use simple raycast...
If possible depending on the context, instead of using Physics.RaycastAll, you can use Physics.Raycast.
It returns the first object that the ray-cast hits.
Adding to this answer an alternative could maybe also be using OnBecameVisible
OnBecameVisible is called when the object became visible by any Camera.
This message is sent to all scripts attached to the Renderer.
and OnBecameInvisible
OnBecameInvisible is called when the Renderer is no longer visible by any Camera.
This message is sent to all scripts attached to the Renderer.
OnBecameVisible and OnBecameInvisible are useful to avoid computations that are only necessary when the object is visible.
For activating and deactivating the according NPC's colliders so the Raycast anyway will only work on visible objects in the first place.
Like on the NPCs have a script
public class InteractableController : MonoBehaviour
{
// you can also reference them via the Inspector
public Collider[] colliders;
private void Awake()
{
// pass in true to also get inactive components
if(colliders.Length = 0) colliders = GetComponentsInChildren<Collider>(true);
}
private void OnBecameInvisible()
{
foreach(var collider in colliders)
{
collider.enabled = false;
}
}
private void OnBecameVisible()
{
foreach(var collider in colliders)
{
collider.enabled = true;
}
}
}
However
Note that object is considered visible when it needs to be rendered in the Scene. It might not be actually visible by any camera, but still need to be rendered for shadows for example. Also, when running in the editor, the Scene view cameras will also cause this function to be called.
I have recently started dabbling in Unity with ARcore, I am using the HelloARController.cs that comes with ARcore 1.5 but I want to load a bundle from my server, I have two scripts
one called LoadAsset.cs which loads a bundle from a webserver and I have another called HelloARcontroller.cs which is basically the main part of the app, this allows me to preset prefabs that will be loaded when the screen is clicked
What I would like to do instead of loading one of the "Andy" objects is for it to pull a prefab from the webserver and use that instead
I have managed so far to load a prefab from my server at runtime but placing it is another matter
namespace GoogleARCore.Examples.HelloAR
{
using System.Collections.Generic;
using GoogleARCore;
using GoogleARCore.Examples.Common;
using UnityEngine;
using System;
using System.Collections;
#if UNITY_EDITOR
// Set up touch input propagation while using Instant Preview in the editor.
using Input = InstantPreviewInput;
#endif
/// <summary>
/// Controls the HelloAR example.
/// </summary>
public class HelloARController : MonoBehaviour
{
public string BundleURL; // -->
http://Myserver/public_http/Assets/AssetBundles/cube_prefab (path to the AssetBundle)
public string AssetName; // --> Cube_pref (name of the Asset prefab)
public int version;
/// <summary>
/// The first-person camera being used to render the passthrough camera image (i.e. AR background).
/// </summary>
public Camera FirstPersonCamera;
/// <summary>
/// A prefab for tracking and visualizing detected planes.
/// </summary>
public GameObject DetectedPlanePrefab;
/// <summary>
/// A model to place when a raycast from a user touch hits a plane.
/// </summary>
public GameObject AndyPlanePrefab;
/// <summary>
/// A model to place when a raycast from a user touch hits a feature point.
/// </summary>
public GameObject AndyPointPrefab;
/// <summary>
/// A gameobject parenting UI for displaying the "searching for planes" snackbar.
/// </summary>
public GameObject SearchingForPlaneUI;
/// <summary>
/// The rotation in degrees need to apply to model when the Andy model is placed.
/// </summary>
private const float k_ModelRotation = 180.0f;
/// <summary>
/// A list to hold all planes ARCore is tracking in the current frame. This object is used across
/// the application to avoid per-frame allocations.
/// </summary>
private List<DetectedPlane> m_AllPlanes = new List<DetectedPlane>();
/// <summary>
/// True if the app is in the process of quitting due to an ARCore connection error, otherwise false.
/// </summary>
private bool m_IsQuitting = false;
/// <summary>
/// The Unity Update() method.
/// </summary>
void Start()
{
StartCoroutine(DownloadAndCache());
}
IEnumerator DownloadAndCache()
{
// Load the AssetBundle file from Cache if it exists with the same version or download and store it in the cache
using (WWW www = WWW.LoadFromCacheOrDownload(BundleURL, version))
{
yield return www;
if (www.error != null)
throw new Exception("WWW download had an error:" + www.error);
AssetBundle bundle = www.assetBundle;
GameObject testing = bundle.LoadAsset(AssetName) as GameObject;
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
} // memory is freed from the web stream (www.Dispose() gets called implicitly)
}
I've declared all my variables and managed to get the Prefab to load up to this point ^^^^^
public void Update()
{
_UpdateApplicationLifecycle();
// Hide snackbar when currently tracking at least one plane.
Session.GetTrackables<DetectedPlane>(m_AllPlanes);
bool showSearchingUI = true;
for (int i = 0; i < m_AllPlanes.Count; i++)
{
if (m_AllPlanes[i].TrackingState == TrackingState.Tracking)
{
showSearchingUI = false;
break;
}
}
SearchingForPlaneUI.SetActive(showSearchingUI);
// If the player has not touched the screen, we are done with this update.
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
{
This is where it loads the Andy prefabs, what I would like to do is swap the andy prefab out for the asset bundle
// Choose the Andy model for the Trackable that got hit.
GameObject prefab;
if (hit.Trackable is FeaturePoint)
{
prefab = AndyPlanePrefab;
}
else
{
prefab = AndyPointPrefab;
}
// Instantiate Andy model at the hit pose.
var andyObject = 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.
andyObject.transform.parent = anchor.transform;
}
}
}
/// <summary>
/// Check and update the application lifecycle.
/// </summary>
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;
}
// 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);
}
}
/// <summary>
/// Actually quit the application.
/// </summary>
private void _DoQuit()
{
Application.Quit();
}
/// <summary>
/// Show an Android toast message.
/// </summary>
/// <param name="message">Message string to show in the toast.</param>
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");
}));
}
}
}
}
My plan is to change the URL dynamically as thats just a public variable so that I can load a different prefab once the app is running, then change the url and load a different prefab.
Any help, advice greatly appreciated.
Hi I am trying to augment different prefabs for different images say around 20 models.Currently testing with 2 models for 2 images in AugmentedImage sample scene.I have added the script AugmentedImageVisualizer.cs to each prefab.I drag and dropped the two models to the script.In the AugmenetedImageExampleController.cs I have made the following changes.
namespace GoogleARCore.Examples.AugmentedImage
{
using System.Collections.Generic;
using System.Runtime.InteropServices;
using GoogleARCore;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Controller for AugmentedImage example.
/// </summary>
public class AugmentedImageExampleController : MonoBehaviour
{
/// <summary>
/// A prefab for visualizing an AugmentedImage.
/// </summary>
// public AugmentedImageVisualizer AugmentedImageVisualizerPrefab;
public List<AugmentedImageVisualizer> AugmentedImageVisualizerPrefab = new List<AugmentedImageVisualizer>();
/// <summary>
/// The overlay containing the fit to scan user guide.
/// </summary>
public GameObject FitToScanOverlay;
private Dictionary<int, AugmentedImageVisualizer> m_Visualizers
= new Dictionary<int, AugmentedImageVisualizer>();
private List<AugmentedImage> m_TempAugmentedImages = new List<AugmentedImage>();
/// <summary>
/// The Unity Update method.
/// </summary>
public void Update()
{
// Exit the app when the 'back' button is pressed.
if (Input.GetKey(KeyCode.Escape))
{
Application.Quit();
}
// Check that motion tracking is tracking.
if (Session.Status != SessionStatus.Tracking)
{
return;
}
// Get updated augmented images for this frame.
Session.GetTrackables<AugmentedImage>(m_TempAugmentedImages, TrackableQueryFilter.Updated);
// Create visualizers and anchors for updated augmented images that are tracking and do not previously
// have a visualizer. Remove visualizers for stopped images.
foreach (var image in m_TempAugmentedImages)
{
AugmentedImageVisualizer visualizer = null;
m_Visualizers.TryGetValue(image.DatabaseIndex, out visualizer);
if (image.TrackingState == TrackingState.Tracking && visualizer == null)
{
// Create an anchor to ensure that ARCore keeps tracking this augmented image.
Anchor anchor = image.CreateAnchor(image.CenterPose);
visualizer = (AugmentedImageVisualizer)Instantiate(AugmentedImageVisualizerPrefab[image.DatabaseIndex], anchor.transform);
visualizer.Image = image;
m_Visualizers.Add(image.DatabaseIndex, visualizer);
}
else if (image.TrackingState == TrackingState.Stopped && visualizer != null)
{
m_Visualizers.Remove(image.DatabaseIndex);
GameObject.Destroy(visualizer.gameObject);
}
}
// Show the fit-to-scan overlay if there are no images that are Tracking.
foreach (var visualizer in m_Visualizers.Values)
{
if (visualizer.Image.TrackingState == TrackingState.Tracking)
{
FitToScanOverlay.SetActive(false);
return;
}
}
FitToScanOverlay.SetActive(true);
}
}
}
My unity screen looks like below
Added Augmented Image Visualizer script to the prefabs to Rabbit prefab and Monkey prefab.Image given below
This is how it should be done?The problem once the model appears it will not disappear.So when I show the next image anther model comes on top of it.How to hide the model when the image is not tracked?
In the AugmentedImageControllerExample.cs we are using the below code.Still I dont understand why the models are not disappearing after they lost tracking of the image.
else if (image.TrackingState == TrackingState.Stopped && visualizer != null)
{
m_Visualizers.Remove(image.DatabaseIndex);
GameObject.Destroy(visualizer.gameObject);
}
AugmentedImageVisualizer.cs code given below?I have referred this Link.
namespace GoogleARCore.Examples.AugmentedImage
{
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using GoogleARCore;
using GoogleARCoreInternal;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Uses 4 frame corner objects to visualize an AugmentedImage.
/// </summary>
public class AugmentedImageVisualizer : MonoBehaviour
{
/// <summary>
/// The AugmentedImage to visualize.
/// </summary>
public AugmentedImage Image;
public GameObject[] Models;
private void Start()
{
}
/// <summary>
/// A model for the lower left corner of the frame to place when an image is detected.
/// </summary>
// public GameObject FrameLowerLeft;
/// <summary>
/// A model for the lower right corner of the frame to place when an image is detected.
/// </summary>
// public GameObject FrameLowerRight;
/// <summary>
/// A model for the upper left corner of the frame to place when an image is detected.
/// </summary>
// public GameObject FrameUpperLeft;
/// <summary>
/// A model for the upper right corner of the frame to place when an image is detected.
/// </summary>
// public GameObject FrameUpperRight;
/// <summary>
/// The Unity Update method.
/// </summary>
public void Update()
{
if (Image == null || Image.TrackingState != TrackingState.Tracking)
{
Models[Image.DatabaseIndex].SetActive(false);
//Models[0].SetActive(false);
//Models[1].SetActive(false);
return;
}
if (Image == null || Image.TrackingState == TrackingState.Stopped)
{
Models[Image.DatabaseIndex].SetActive(false);
//Models[0].SetActive(false);
//Models[1].SetActive(false);
return;
}
if (Image == null || Image.TrackingState == TrackingState.Paused)
{
Models[Image.DatabaseIndex].SetActive(false);
//Models[0].SetActive(false);
//Models[1].SetActive(false);
return;
}
Models[Image.DatabaseIndex].SetActive(true);
}
}
}
The problem is, that in your update function you set always both models active true. But you should only set the model active you are tracking! So like said in the comment you should use the AugmentedImage DatabseIndex.
For example your Models[0] is the model coresponding to the first Image in the Database and the Models[1] is coresponding to the second Image.
So instead of:
// Wrong code, because you're always setting both models active
Models[0].SetActive(true);
Models[1].SetActive(true);
you can write:
// Only set the tracking Image active
Models[Image.DatabaseIndex].SetActive(true);
Another thing is in your if (Image != null && Image.TrackingState == TrackingState.Paused) and if (Image != null && Image.TrackingState == TrackingState.Stopped) you could write a return; after deactivating your model, so that you quit your Update function and don't set the model active again.
TrackingState is not working as intended.
Try using TrackingMethod where ever you are using TrackingState . Both in AugmentedImageExampleController and AugmentedImageVisualizer scripts
TrackingMethod has 3 states FullTracking,NotTracking, LastKnowPose.
for eg: the below if statement will be
if (image.TrackingState == TrackingState.Tracking && visualizer == null){}
change to
if (image.TrackingMethod == AugmentedImageTrackingMethod.FullTracking && visualizer == null){}
Here is complete working example in case some one is looking for this solution. https://github.com/darshanpv/DigiCard
So I'm trying to create an interactive environment using a 3D model. I have the model and camera moving on a fixed Z-axis increment but after 3 or so seconds the model just disappears. Not sure what's happening, help is very appreciated.
My Game code is posted below.
namespace model_viewer
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Loads the ship
Model Ship;
//Moves the ship and the camera together
float moveCamera;
float moveShip;
//moves the ship in the user's direction
float keyMoveX;
float keyMoveY;
public Game1()
: base()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
moveCamera = -3;
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
Ship = Content.Load<Model>("Graphics/Ship");
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
moveCamera += 0.005f;
moveShip += 0.005f;
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
Matrix proj = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver2, 1, 0.1f, 10000.0f);
Matrix view = Matrix.CreateLookAt(
new Vector3(0, 0, moveCamera),
Vector3.Zero,
Vector3.Up);
float scale = 1.0f / Ship.Meshes[0].BoundingSphere.Radius;
Matrix world = Matrix.CreateScale(scale) * Matrix.CreateRotationY(MathHelper.ToRadians(180)) * Matrix.CreateTranslation(new Vector3(0, 0, moveShip));
Ship.Draw(world, view, proj);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
I found the answer, my 'view' matrix was targeting a point that wasn't moving with my model and was rotating to view the point.