I am rather new (very new, in-fact less than 8 hrs new) to Unity 3D.
As it turns out, my newness to Unity has posed me with a rather weird problem. Consider two Behaviors below:
Behavior CamCaptureDialogBehavior:
using UnityEngine;
using System.Collections;
public class CamCaptureDialogBehavior : MonoBehaviour
{
// 200x300 px window will apear in the center of the screen.
private Rect windowRect = new Rect ((Screen.width - 200) / 2, (Screen.height - 300) / 2, 200, 300);
// Only show it if needed.
private bool show = false;
public CamCaptureDialogBehavior ()
{
}
// Use this for initialization
void Start ()
{
}
void OnGUI ()
{
if (show)
windowRect = GUI.Window (0, windowRect, DialogWindow, "Game Over");
}
void DialogWindow (int windowID)
{
float y = 20;
GUI.Label (new Rect (5, y, windowRect.width, 20), "Title goes here");
if (GUI.Button (new Rect (5, y, windowRect.width - 10, 20), "Ok")) {
Application.LoadLevel (0);
show = false;
}
}
// To open the dialogue from outside of the script.
public void Open ()
{
show = true;
}
// Update is called once per frame
void Update ()
{
}
}
Behavior: PictureButtonBehavior:
using UnityEngine;
using UnityEditor;
using System.Collections;
public class PictureButtonBehavior : MonoBehaviour
{
private bool displayedGUI = false;
private bool ShowThisGUI = false;
void Start ()
{
}
void Update ()
{
if (displayedGUI == true) {
Debug.Log (string.Format ("displayedGUI = {0}\r\n", displayedGUI));
displayedGUI = false;
ShowThisGUI = false;
}
}
void OnGUI ()
{
if (ShowThisGUI) {
Debug.Log (string.Format ("ShowThisGUI = {0}\r\n", ShowThisGUI));
displayedGUI = true;
ShowThisGUI = false;
CamCaptureDialogBehavior ccdb = new CamCaptureDialogBehavior ();
if (ccdb != null) {
ccdb.enabled = true;
ccdb.Open ();
}
}
}
public void OnClick ()
{
ShowThisGUI = true;
}
}
At CamCaptureDialogBehavior ccdb = new CamCaptureDialogBehavior ();, ccdb is always null.
Is there a unqiue way to instantiate classes in Unity/Mono?
or, How can I instantiate CamCaptureDialogBehavior in PictureButtonBehavior and be able to display the dialog represented by CamCaptureDialogBehavior.
You can't call new on MonoBehaviours.
You can instantiate prefabs that have the script attached to them.
GameObject g = Instantiate(prefab) as GameObject;
Or you can add them to an already existing GameObject.
gameObject.AddComponent<ScriptName>();
Next you will ask what is a prefab. It is something very simple, yet very powerful in Unity. Short tutorial on how to make one.
You can create a prefab by selecting Asset > Create Prefab and then
dragging an object from the scene onto the “empty” prefab asset that
appears. Simply dragging the prefab asset from the project view to the
scene view will then create instances of the prefab.
Related
I am trying to create a unity script that detects when the player has left a certain area using C# code only (no boundary boxes). My code should play a sound once the player leaves but the code is not working as expected and I cannot see a fault in the logic.
Expected behaviour
When the player steps outside the boundary, a sound will play once.
Actual Behaviour
The sound plays all the time no matter where the player is.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EX2BoundaryDetect : MonoBehaviour {
// create a GameObject to represent the player
public GameObject playerObject;
public bool leaveArea = false;
// create an Audio-clip object. This assumes we drag a sound file onto the myclip box in the Inspector.
public AudioClip myclip;
// Use this for initialization
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
// get the actual Sound file from the Inspector
GetComponent<AudioSource>().clip = myclip;
}
// Update is called once per frame
void Update () {
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
}
public void PlayAudio1 () {
// play the sound if the player is outside some boundaries
if (transform.position.x > 10) {
leaveArea = true;
}
if (transform.position.x < -29) {
leaveArea = true;
}
if (transform.position.z > 10) {
leaveArea = true;
}
if (transform.position.z < -29) {
leaveArea = true;
}
}
}
leaveArea is only set to false in the class definition. Once set to true, it may never be set to false again, and it may be inadvertently overwritten in the scene definition in the first place.
To fix this issue, set it to false at the beginning of Update:
void Update () {
leaveArea = false;
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
}
Also, GetComponent is an expensive action and it is good to avoid calling it in Update wherever possible. Therefore, you may want to move it into a class property and set it once in Start:
private AudioSource audioSource;
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
audioSource = GetComponent<AudioSource>();
// get the actual Sound file from the Inspector
audioSource.clip = myclip;
}
// Update is called once per frame
void Update () {
leaveArea = false;
PlayAudio1();
if (leaveArea) {
audioSource.Play ();
}
}
Got it working now, by adding
leaveArea = false;
to the Update method.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EX2BoundaryDetect : MonoBehaviour {
// create a GameObject to represent the player
public GameObject playerObject;
public bool leaveArea = false;
// create an Audio-clip object. This assumes we drag a sound file onto the myclip box in the Inspector.
public AudioClip myclip;
// Use this for initialization
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
// get the actual Sound file from the Inspector
GetComponent<AudioSource>().clip = myclip;
}
// Update is called once per frame
void Update () {
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
leaveArea = false;
}
public void PlayAudio1 () {
// play the sound if the player is outside some boundaries
if (transform.position.x > 10) {
leaveArea = true;
}
if (transform.position.x < -29) {
leaveArea = true;
}
if (transform.position.z > 10) {
leaveArea = true;
}
if (transform.position.z < -29) {
leaveArea = true;
}
}
}
I think the above answers still do not solve your problem of "When the player steps outside the boundary, a sound will play once."
I will share a cleaner way of doing it (possibly optimized)
Using Bounds
Create our own bounds by using Bounds https://docs.unity3d.com/ScriptReference/Bounds.html
The constructor of Bounds is Bounds(Vector3 center,vector3 size)
So in your case center will be center = (-9.5f, 0, -9.5f) (midpoint formula) and finally the size of the bounding box will be size = (39.0f, 0f, 39.0f)
Let's move to the Code Part
public class BoundsCheck: MonoBehaviour
{
public bool isInside = false;
public bool isPlayedOnce = false;
private Bounds bounds;
void Start()
{
bounds = new Bounds(new Vector3(-9.5f, 0, -9.5f), new Vector3(39.0f, 0f, 39.0f));
}
void Update()
{
CheckBounds();// play the sound if the player is outside some boundaries
if (!isInside &&!isPlayedOnce)
{
Debug.Log("PLAY");
isPlayedOnce = true;
}
}
private void CheckBounds()
{
bool isInsideBound = bounds.Contains(transform.position);
if (isInsideBound)
{
isInside = true;
if (isPlayedOnce)
{
isPlayedOnce = false;
}
}
else
{
isInside = false;
}
}
}
So instead of multiple ifs, we can just check if the position of the transform is inside the bound by using bool isInsideBound = bounds.Contains(transform.position);
Note:- To play sound only once, I have used one more boolean isPlayedOnce
I am not sure if this the most optimized way of doing it. But surely a cleaner way(via code).
I know this has been asked previously here, but it doesn't work. It moves the GameObject to another scene successfully but It does not close the previous scene. Here is a screenshot.
Here is the Expanded Screen shot of the project Expanded Screen Shot
And here is the script but in the script you'll also find a code of a Slide Show menu which will display my models as menu
In function enableScene() On line 1 I have tried to close the previous scene but it doesn't work
public class PizzaScript : MonoBehaviour {
public Text header;
public List<GameObject> createObjects = new List<GameObject>();
private static int slideIndex = 1;
private GameObject instance;
private Vector3 temp = new Vector3(0.34f, 0.074f, 0);
private AsyncOperation sceneAsync;
public GameObject Pizza;
public GameObject Cube;
public GameObject Sphere;
public GameObject CocaCola;
// Use this for initialization
void Start()
{
object[] subListObjects = { Pizza, Cube, Sphere, CocaCola };
foreach(GameObject list in subListObjects )
{
GameObject lo = (GameObject)list;
createObjects.Add(lo);
}
showPrefabs(slideIndex);
StartCoroutine(loadScene(2));
}
IEnumerator loadScene(int index)
{
AsyncOperation scene = SceneManager.LoadSceneAsync(index, LoadSceneMode.Additive);
scene.allowSceneActivation = false;
sceneAsync = scene;
//Wait until we are done loading the scene
while (scene.progress < 0.9f)
{
Debug.Log("Loading scene " + " [][] Progress: " + scene.progress);
yield return null;
}
Debug.Log("Progress Completed.............................");
}
public void OnFinishedLoadingAllScene()
{
Debug.Log("Done Loading Scene");
enableScene(2);
Debug.Log("Scene Activated!");
}
private void enableScene(int index)
{
SceneManager.UnloadSceneAsync(1);
//Activate the Scene
sceneAsync.allowSceneActivation = true;
Scene sceneToLoad = SceneManager.GetSceneByBuildIndex(index);
if (sceneToLoad.IsValid())
{
Debug.Log("Scene is Valid");
SceneManager.MoveGameObjectToScene(instance, sceneToLoad);
SceneManager.SetActiveScene(sceneToLoad);
}
}
// Update is called once per fram
public void OnClickRightArrow()
{
plusPrefabs(1);
}
public void OnClickLeftArrow()
{
plusPrefabs(-1);
}
public void plusPrefabs(int n)
{
if(n == 1 || n == -1)
{
Destroy(instance,0.01f);
}
showPrefabs(slideIndex += n);
}
public void showPrefabs(int n)
{
var x = createObjects.Count;
if (n > createObjects.Count)
{
slideIndex = 1;
}
if (n < 1)
{
slideIndex = createObjects.Count;
}
if (slideIndex == 1)
{
header.text = slideIndex.ToString();
instance = Instantiate(createObjects[0], temp, Quaternion.Euler(-90,0,0));
instance.transform.localScale = new Vector3(20, 20, 20);
}
else if(slideIndex == 2)
{
header.text = slideIndex.ToString();
instance = Instantiate(createObjects[1], temp, transform.rotation);
instance.transform.localScale = new Vector3(10, 10, 10);
}
else if (slideIndex == 3)
{
header.text = slideIndex.ToString();
instance = Instantiate(createObjects[2], temp, transform.rotation);
instance.transform.localScale = new Vector3(10, 10, 10);
}
else if (slideIndex == 4)
{
header.text = slideIndex.ToString();
instance = Instantiate(createObjects[3], temp, Quaternion.Euler(-90,0,0));
instance.transform.localScale = new Vector3(120, 120, 120);
}
}
}
Here is the screen shot Screen shot of previous scene
It very easy to persist your one game object into antoher scene on loading, just attached thsi script to your desired gameobject.
public class DontDestroyOnLoad : MonoBehaviour {
void Awake () {
DontDestroyOnLoad(this);
}
}
What DontDestroyOnLoad work like this:
Makes the object target not be destroyed automatically when loading a
new scene.
When loading a new level all objects in the scene are destroyed, then
the objects in the new level are loaded. In order to preserve an
object during level loading call DontDestroyOnLoad on it. If the
object is a component or game object then its entire transform
hierarchy will not be destroyed either.
I am trying to show a text say "Scan for a surface" until it scans a plane surface.Once the device picks up a plane surface the message should disappear.Where should I write code for this purpose.I found two instances where prefab plane is initialised,one in UnityARGeneratePlane.cs and UnityARUtility.cs .I added a Gameobject as Text in UnityARGeneratePlane.cs and used setactive to true and false to show and hide the text.It is showing when i used it in the start method.How to hide the text is where i struggle.Where to use the code to hide the text when a plane is detected?Is it in UnityARGeneratePlane.cs or UnityARUtility.cs.
public class UnityARGeneratePlane : MonoBehaviour
{
public GameObject planePrefab;
public GameObject scantxt;
private UnityARAnchorManager unityARAnchorManager;
// Use this for initialization
void Start () {
unityARAnchorManager = new UnityARAnchorManager();
UnityARUtility.InitializePlanePrefab (planePrefab);
scantxt.SetActive(true); //Tried to show the text working when app opens
}
void OnDestroy()
{
unityARAnchorManager.Destroy ();
scantxt.SetActive(false); //Here is where I tried to hide the text until a Vertical or Horizontal plane is detected
}
void OnGUI()
{
IEnumerable<ARPlaneAnchorGameObject> arpags = unityARAnchorManager.GetCurrentPlaneAnchors ();
foreach(var planeAnchor in arpags)
{
//ARPlaneAnchor ap = planeAnchor;
//GUI.Box (new Rect (100, 100, 800, 60), string.Format ("Center: x:{0}, y:{1}, z:{2}", ap.center.x, ap.center.y, ap.center.z));
//GUI.Box(new Rect(100, 200, 800, 60), string.Format ("Extent: x:{0}, y:{1}, z:{2}", ap.extent.x, ap.extent.y, ap.extent.z));
}
}
}
The code below is UnityARUtility.cs
public class UnityARUtility
{
private MeshCollider meshCollider; //declared to avoid code stripping of class
private MeshFilter meshFilter; //declared to avoid code stripping of class
public static GameObject planePrefab = null;
// public Text text1;
public static void InitializePlanePrefab(GameObject go)
{
planePrefab = go;
}
public static GameObject CreatePlaneInScene(ARPlaneAnchor arPlaneAnchor)
{
GameObject plane;
if (planePrefab != null)
{
plane = GameObject.Instantiate(planePrefab); //I dont understand why again Plane prefab is initialized.Other than Generate planes.
// text1.text = "Select Any Painting from panel";
}
else {
plane = new GameObject (); //put in a blank gameObject to get at least a transform to manipulate
}
//plane.name = arPlaneAnchor.identifier;
ARKitPlaneMeshRender apmr = plane.GetComponent<ARKitPlaneMeshRender> ();
if (apmr != null) {
apmr.InitiliazeMesh (arPlaneAnchor);
}
return UpdatePlaneWithAnchorTransform(plane, arPlaneAnchor);
}
public static GameObject UpdatePlaneWithAnchorTransform(GameObject plane, ARPlaneAnchor arPlaneAnchor)
{
//do coordinate conversion from ARKit to Unity
plane.transform.position = UnityARMatrixOps.GetPosition (arPlaneAnchor.transform);
plane.transform.rotation = UnityARMatrixOps.GetRotation (arPlaneAnchor.transform);
ARKitPlaneMeshRender apmr = plane.GetComponent<ARKitPlaneMeshRender> ();
if (apmr != null) {
apmr.UpdateMesh (arPlaneAnchor);
}
MeshFilter mf = plane.GetComponentInChildren<MeshFilter> ();
if (mf != null) {
if (apmr == null) {
//since our plane mesh is actually 10mx10m in the world, we scale it here by 0.1f
mf.gameObject.transform.localScale = new Vector3 (arPlaneAnchor.extent.x * 0.1f, arPlaneAnchor.extent.y * 0.1f, arPlaneAnchor.extent.z * 0.1f);
//convert our center position to unity coords
mf.gameObject.transform.localPosition = new Vector3(arPlaneAnchor.center.x,arPlaneAnchor.center.y, -arPlaneAnchor.center.z);
}
}
return plane;
}
}
}
I am trying to place a sphere on a ground plane and on a button click remove the sphere and place a cube.
This is the script attached to the AR camera object.
using UnityEngine;
using Vuforia;
using System.Collections;
public class ModelSwapper : MonoBehaviour {
public AnchorStageBehaviour theTrackable;
public Transform myModelPrefab;
private bool mSwapModel = false;
// Use this for initialization
void Start () {
Transform myModelTrf = GameObject.Instantiate(myModelPrefab) as Transform;
myModelTrf.parent = theTrackable.transform;
myModelTrf.localPosition = new Vector3(0f, 0f, 0f);
myModelTrf.localRotation = Quaternion.identity;
myModelTrf.localScale = new Vector3(0.05f, 0.05f, 0.05f);
myModelTrf.gameObject.active = true;
}
// Update is called once per frame
void Update () {
if (mSwapModel && theTrackable != null) {
SwapModel();
mSwapModel = false;
}
}
void OnGUI() {
if (GUI.Button (new Rect(50,50,120,40), "Swap Model")) {
mSwapModel = true;
}
}
private void SwapModel() {
GameObject trackableGameObject = theTrackable.gameObject;
//disable any pre-existing augmentation
for (int i = 0; i < trackableGameObject.transform.GetChildCount(); i++)
{
Transform child = trackableGameObject.transform.GetChild(i);
child.gameObject.active = false;
}
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
// Re-parent the cube as child of the trackable gameObject
cube.transform.parent = theTrackable.transform;
// Adjust the position and scale
// so that it fits nicely on the target
cube.transform.localPosition = new Vector3(0, 0.2f, 0);
cube.transform.localRotation = Quaternion.identity;
cube.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
// Make sure it is active
cube.active = true;
}
}
But instead, it spawns multiple cubes along with the sphere.
Multiple clones
Since, there isn't much documentation or anything else on Vuforia ground plane, it's hard to get things done. Why is this behaving strange?
You could avoid the Update and it should fix your issue, replace these two methods in your script with the following:
void Update () {
}
void OnGUI() {
if (GUI.Button (new Rect(50,50,120,40), "Swap Model")) {
if(theTrackable)
{
SwapModel();
}
}
}
i'm using Unity to track some image target (ARCamera - Vuforia) and everything works fine. I've inserted some 2D sprites on the image target etc... but now i don't want to show all the targets as soon as the image target is found, so i went to DefaultTrackableEventHandler.cs and instantiated my custom class Example like this:
#region PRIVATE_MEMBER_VARIABLES
private TrackableBehaviour mTrackableBehaviour;
#endregion // PRIVATE_MEMBER_VARIABLES
public Example mainClass;
...
private void OnTrackingFound()
{
...
Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
mainClass = new Example();
mainClass.OnTrackableFound(); // Public method on 'Example' Class
}
Now on my Example class:
public class Example : MonoBehaviour {
public SpriteRenderer forYou;
public SpriteRenderer map;
public Example () {}
private bool isInTransition = false;
private float transition = 1;
private bool isShowing = false;
private float duration = 0;
void Start()
{
forYou = new SpriteRenderer();
map = new SpriteRenderer();
}
public void OnTrackableFound() {
StartCoroutine(FadeCoroutine());
}
private IEnumerator FadeCoroutine() {
yield return new WaitForSeconds(1);
Fade(true, .1f);
}
public void OnTrackableLost() {}
public void Fade (bool showing, float duration)
{
isShowing = showing;
isInTransition = true;
this.duration = duration;
transition = (isShowing) ? 0 : 1;
}
void Update ()
{
if (Input.GetMouseButtonDown (0)) {
Fade (true, .1f);
}
if (Input.GetMouseButtonUp (0)) {
Fade (false, .1f);
}
if (!isInTransition) {
return;
}
transition += (isShowing) ? Time.deltaTime * (1 / duration) : -Time.deltaTime * (1 / duration);
forYou.color = Color.Lerp (Color.white, new Color (1, 1, 1, 0), transition);
map.color = Color.Lerp (Color.white, new Color (1, 1, 1, 0), transition);
if (transition > 1 || transition < 0) {
isInTransition = false;
}
}
}
Sorry for the amount of code, but basically this is just for fading in/out some sprites. I want to yield as soon as my OnTrackingFound is called at Example class and after idk .5 seconds fade in some of my sprites.
Thank you!
EDIT - SOLVED
So, to attach a script to a game object it must inherit from Monobehaviour but than it cannot be instantiated so what i did was:
I needed the OnTrackingFound() from the DefaultTrackerHandler... so i coded everything on that class [i know it's far from the best solution but...] instead of instantiate another script because as i said it wouldn't be possible to attach it to a game object since it couldn't inherit from Monobehaviour. With public game objects, i attached everything directly from this class that also inherits from Monobehvr. If you instantiate a script that inherits monobehv it will be null. Thanks everyone!
The issue with your code is that you try to instantiate component Example that derives from MonoBehaviour calling constructor with new keyword.
Never do this.
All components in Unity3D Engine needs to be added to existing GameObject with GameObject.AddComponent<T>()
So what you need to do is:
private void OnTrackingFound()
{
...
Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
// create new GameObject and add component
mainClass = new GameObject("MyNewGameobject").AddComponent<Example>();
mainClass.OnTrackableFound(); // Public method on 'Example' Class
}
Seems like you are calling mainClass = new Example(); every time a new target is tracked. This will create a new object even-though there might be an existing objects. I don't believe you want this.
Instead try call the GameObject gobj = GameObject.Find() (because the GameObject already exists right?) function to get the existing GameObject that the script is attached to. Then do a gobj.GetComponent<Example>(). OnTrackableFound() to call the event on the GameObject. Hope that helps.
I've answered a question which needs a delay like this in gamedev.
Basically you will increment a float value overtime, and when desired amount of time passed you will fade the sprite.
Pseudo:
float counter;
bool isFadeReady = false;
void Update ()
{
if (OnTrackingFound)
{
isFadeReady = true;
}
if (isFadeReady == true)
{
counter +=0.1f; //change the value for increase speed
}
if(counter>=1.0f) //change the value for desired time
{
//Fade your sprite here
isFadeReady = false;
}
}
Hope this helps! Cheers!