Unity3D - Gear VR Input doesn't work between scenes - c#

I'm creating a project using the Gear VR, where you can rotate an object and display information based on the swipe and tap controls on the side of the headset.
Everything works great, I can rotate and select stuff when I use the touchpad on the side of the Gear VR, but when I change scenes and return to the main menu, and then go back into the scene I was just on, the functionality stops working.
I am using this script I've made:
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
using System;
public class GearVRTouchpad : MonoBehaviour
{
public GameObject heart;
public float speed;
Rigidbody heartRb;
void Start ()
{
OVRTouchpad.Create();
OVRTouchpad.TouchHandler += Touchpad;
heartRb = heart.GetComponent<Rigidbody>();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
SceneManager.LoadScene("Main Menu");
}
}
void Touchpad(object sender, EventArgs e)
{
var touches = (OVRTouchpad.TouchArgs)e;
switch (touches.TouchType)
{
case OVRTouchpad.TouchEvent.SingleTap:
// Do some stuff
break;
case OVRTouchpad.TouchEvent.Up:
// Do some stuff
break;
//etc for other directions
}
}
}
I've noticed that when I start my game, an OVRTouchpadHelper is created. I don't know if that has anything to do with my problem.
The error I am getting is:
MissingReferenceException: The object of type 'GearVRTouchpad' has
been destroyed but you are still trying to access it. Your script
should either check if it is null or you should not destroy the
object.
BUT I have not referenced this script anywhere else.
When I check my scene in play mode, the script is still there with the variable assignments still present.
Any help would be great!

OVRTouchpad.TouchHandler is a static EventHandler (so it will persist through the lifetime of the game). Your script is subscribing to it when it's created but isn't unsubscribing when it's destroyed. When you reload the scene the old subscription is still in the event but the old GearVRTouchpad instance is gone. This will result in the MissingReferenceException next time the TouchHandler event fires. Add this to your class:
void OnDestroy() {
OVRTouchpad.TouchHandler -= Touchpad;
}
Now, whenever a GameObject with the GearVRTouchpad behaviour is destroyed, the static event in OVRTouchpad will no longer have a reference to it.

Related

Unity editor / standalone build crash when loading a small scene

I have a small scene setup with the UFPS asset from unity assetstore. When I load DIRECTLY the scene its working with less then 1 sec loading time, BUT when i load it from my menu the editor crashes. I also have a "loader" scene with the async method that one is working also so i think something went off with the button (I also try loading the async scene from menu and also try loading the game scene from menu crash each time) The same with builded versions I could't find ANY solutions. Here is the button's script (c#) :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class ButtonEvents : MonoBehaviour
{
[SerializeField]
public int selected;
public Button ok;
void Start()
{
Button okbtn = ok.GetComponent<Button>();
okbtn.onClick.AddListener(StartGame);
}
public void Quit()
{
Application.Quit();
}
public void markabreakdown()
{
selected = 1;
}
void Update()
{
Button okbtn = ok.GetComponent<Button>();
okbtn.onClick.AddListener(StartGame);
Debug.Log(selected);
}
public void StartGame()
{
SceneManager.LoadScene(selected);
}
}
Why do you use AddListener in Update -> adding a callback each and every frame?! When you click on that button you are trying to call the StartGame method hundreds of times depending how long you have spent in that scene .. and then most probably it tries to call that method the second time in a moment when the scene is already changed -> this button and this class already destroyed -> null ref exception.
Remove your entire Update method!
You already added the callback in Start
Further note that ok already is a Button so calling ok.GetComponent<Button>() is completely redundant. Simply do
void Start()
{
ok.onClick.AddListener(StartGame);
}

Changing text programmatically to show score on game over screen in unity

I've been working on a simple 2D game in unity and it just has three scenes, the start scene, the game scene, and the game over scene. I want to display the score from the game in the game over screen. I created a score manager game object in the game scene that uses the DontDestroyOnLoad() function to carry it over into the game over screen and I gave it access to the score which is managed by the game manager. I've been debugging my code and the score is translated over into the score manager and is maintained when the game over screen loads, but for some reason it won't let me update the score text object. Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour {
public static ScoreManager Instance;
private GameController gameController;
private int scoreInstance;
private Text scoreText;
// When scene is first loaded
void Awake() {
this.InstantiateController();
}
// Use this for initialization
void Start () {
GameObject gameControllerObject = GameObject.FindWithTag("GameController");
if (gameControllerObject != null)
{
gameController = gameControllerObject.GetComponent<GameController>();
}
GameObject scoreTextObject = GameObject.FindWithTag("ScoreText");
if (scoreTextObject != null)
{
scoreText = scoreTextObject.GetComponent<Text>();
}
scoreInstance = 0;
scoreText.text = "";
}
// Update is called once per frame
void Update () {
scoreInstance = gameController.score;
Debug.Log("Score: " + scoreInstance.ToString());
scoreText.text = scoreInstance.ToString();
}
private void InstantiateController ()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(this);
}
else if (this != Instance)
{
Destroy(this.gameObject);
}
}
}
So I tried to programmatically gather the "score text" ui component in the start function because I figured I can't just make it public and drag in the text component because the score manager is actually in a different scene than the score text object. I also tried adding this whole bit of code to gather the text component into the update function so that it can do that when the score manager is actually a part of game over screen. Nothing seems to work and I have no idea why. Can anybody please help me with this? Also I keep getting a "NullReferenceException: Object reference not set to an instance of an object" error. Thanks in advance for any help.
Unity Start function is only called the first time the script is enabled, i.e. not every time the scene changes for a DontDestroyOnLoad object.
So if you need to wire up some changes after a scene change, you need to either detect the scene change, or have an object that starts in that scene trigger the code you want to run.
Having another object on the new scene trigger things is easy and pretty fool-proof, but there's a builtin function you can add to your other objects:
void OnLevelWasLoaded(int currentLevel)
{
}
This will be called on level changes, and give you the level's number (not name sadly). However, the above is deprecated and they want you to use Unity's SceneManager, so the proper way to set this up is now:
Unity 5 OnLevelWasLoaded?
Start()
{
SceneManager.sceneLoaded += this.OnLoadCallback;
}
void OnLoadCallback(Scene scene, LoadSceneMode sceneMode)
{
// you can query the name of the loaded scene here
}

Unity 5.3 with C# - Toggle Sound On/Off

I'm working with Unity 5.3 with C# as code editor
These are what I have :
I have 2 scenes in my project : home and options. I have bg object on both scenes. Both bg objects have Audio Source component, which contains same background music that play on awake. I don't use any codes for these background musics, I only click the Add Component button from Unity and add Audio Source.
This is what I want :
Options scene can toggle the background music on/off for all scenes. Therefore, there are btnOn and btnOff in Options scene.
This is my code in Audio Manager.cs :
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class AudioManager : MonoBehaviour {
public Button btnOn;
public Button btnOff;
// Use this for initialization
void Start () {
btnOn = GetComponent<Button>();
btnOff = GetComponent<Button>();
btnOn.onClick.AddListener(() => PlayAudio());
btnOff.onClick.AddListener(() => StopAudio());
}
void PlayAudio()
{
AudioSource.volume = 0.5f;
}
void StopAudio()
{
AudioSource.volume = 0f;
}
}
This is the problem :
I have this error : An object reference is required to access non-static member UnityEngine.AudioSource.volume. Maybe, this is because I don't write public AudioSource audioSource in my code. But, if I write this, I have to add another audio in Get Component box, and I will have double Audio Source in one scene. What should I do? Thanks
As you have a bg object in both scenes and as your AudioManager isn't marked as [DontDestroyOnLoad] (then I assume you also have one AudioManager in both scenes), it's Start() function will fire each time you load a scene.
So you can declare a private AudioSource and get a reference of it by finding your bg object in your scene and calling GetComponent on it :
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class AudioManager : MonoBehaviour
{
public Button btnOn;
public Button btnOff;
private AudioSource audioSource;
// Use this for initialization
void Start()
{
btnOn = GetComponent<Button>();
btnOff = GetComponent<Button>();
btnOn.onClick.AddListener(() => PlayAudio());
btnOff.onClick.AddListener(() => StopAudio());
audioSource = GameObject.Find("bg").GetComponent<AudioSource>();
}
void PlayAudio()
{
audioSource.volume = 0.5f;
}
void StopAudio()
{
audioSource.volume = 0f;
}
}
The error is raised because you need to assign the property on an individual object; the volume is not shared between sources. For this reason you will either need a field to assign in the inspector, or get a reference with GetComponent.
While using different scenes for handling options is not wrong, it is a tad clumsy; the current scene has to be unloaded (destroying all object not marked as DontDestroyOnLoad and information associated with them), after which the options are loaded, to then load the previous scene. While unloading the music most likely stops playing, which, after loading, starts at the beginning again. Not mention any settings on these objects are lost (volume, change of track, etc.).
The afore mentioned DontDestroyOnLoad can help since you make your changes on the same object, but you will have to deal with duplicates each time a scene is loaded where such an object exists... you can use the function OnLevelWasLoaded (documentation is a tad lacking at the moment) as a moment to determine which objects to destroy.
Another point is that you currently have public Button fields. This allows the for the assignment of them via the inspector, but that is rather moot as you overwrite them in Start (new value being the first button component assigned to the same object). I would make these fields private, or at least make sure they are not assigned. But I'm getting slightly of topic on how to keep settings persistent between scenes.
Here is some code to give you an idea, but be warned it is untested as I currently have no access to an environment to test it. This solution uses an object which is persistent between scenes. Keep in mind that any connections established to this object via the editor are lost after another scene has loaded.
public class AudioManager : MonoBehaviour
{
private AudioSource source;
// Called regardless of whether the object is enabled or not.
// Should not be called in a new scene.
private void Awake()
{
// Protect this object from being destroyed so its volume
// is maintained between scenes.
DontDestroyOnLoad(this.gameObject);
source = GetComponent<AudioSource>();
}
public void DestroyPossibleDuplicates()
{
AudioManager[] managers = FindObjectsOfType(typeof(AudioManager)) as AudioManager[];
foreach (AudioManager manager in managers)
{
// Use something to determine whether a manager is a duplicate.
// It must not be this, but have something in common; a name perhaps?
if ((manager != this) && (manager.gameObject.name == this.gameObject.name))
{
// Destroy the duplicates so their sound won't interfere.
Destroy(manager.gameObject);
}
}
}
private void BindToInterface()
{
Slider[] sliders = FindObjectsOfType(typeof(Slider)) as Slider[];
foreach (Slider slider in sliders)
{
// Determine whether the specified slider should have effect on this object.
// If the slider's name contains this object's name assume it should.
if (slider.gameObject.name.indexOf(this.gameObject.name)!=-1)
{
slider.onValueChanged.addListener((float value)=>
{
// In this case a slider is used for more control over the volume.
// Different elements require other logic to function.
source.volume = value;
});
}
}
}
// If my memory serves correct this method is only called on objects
// that were in the scene before it started loading.
// Just to be safe, don't have it do anything depending on this difference.
private void OnLevelWasLoaded()
{
DestroyPossibleDuplicates();
BindToInterface();
}
}

On trigger exit subctract mana

I have perform a c# script to add mana to the slider ontriggerEnter and Subtract mana on trigguerExit wen touch on Enemy object, but it seems to have some thing wrong, the script have no errors but wen its touch the Enemy Object It Take all the mana, and not the value i set.
I am new on c# scripting, tank you in advance.
This is my Script
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class mana : MonoBehaviour {
public void addmana()
{
manaBar.value += 300;
}
public void Takemana()
{
manaBar.value -= 30;
}
public Slider manaBar;
// Use this for initialization
void Start ()
{
manaBar.value = 300;
if (manaBar != null)
{
manaBar.IsActive();
}
}
void OnTriggerEnter(Collider other)
{
// The switch statement checks what tag the other gameobject is, and reacts accordingly.
switch (other.gameObject.tag)
{
case "manapickup":
Debug.Log(other.gameObject.tag);
Invoke("addmana", 0f);
Destroy (other.gameObject);
break;
}
// Finally, this line destroys the gameObject the player collided with.
//Destroy(other.gameObject);
}
void OnTriggerExit(Collider other)
{
// The switch statement checks what tag the other gameobject is, and reacts accordingly.
switch (other.gameObject.tag)
{
case "Enemy":
Debug.Log(other.gameObject.tag);
Invoke("Takemana", 0f);
break;
}
// Finally, this line destroys the gameObject the player collided with.
//Destroy(other.gameObject);
}
Looking at your code it seems like the problem may come from your Slider component : are you sure its maxValue property is set to the right value ?
(by default this value is set to 1 : you can change it in the Inspector or programmatically calling manaBar.maxValue = 1000.0f;)
Also I'd recommend moving the case "Enemy": [...] part of your script inside the switch of your OnTriggerEnter method : I see no benefit from calling it inside the OnTriggerExit method (but I may be wrong depending on your game's logic).
Some side notes to conclude :
Try to keep your code organized as much as possible : one common layout is _Attributes (Properties) / Monobehaviour methods (Start, Update, OnTriggerEnter, ...) / Custom methods. This helps other when trying to solve your problems and will ease later maintenance of your code.
Try to respect some coding standards (same as code being organized, this will vary a lot from one person/compagny to another) : in C# method names usually start with an uppercase while property names start with a lowercase.
Finally try to stick to the "rules" you've set for yourself : here I see one of your tags is capitilized (Enemy) while the other isn't (manapickup).

Utilizing the touchpad on gearVR in Unity

Still learning here and starting of with basics (just a plain Plane and moving as the first person around)
However i can run the app and look around and up and down etc. but can;t make the camera move when touching the touchpad on the GearVR headset.
I have created the script (c#) and attached to the camera in unity:
using UnityEngine;
using System.Collections;
public class Moving : MonoBehaviour {
// Use this for initialization
void Start()
{
if (Input.GetButton("Tap"))
{
// Do something if tap starts
}
if (Input.GetButtonUp("Tap"))
{
// Do something if tap ends
}
}
// Update is called once per frame
void Update () {
if (Input.GetButton("Tap"))
{
// Do something if tap starts
}
if (Input.GetButtonUp("Tap"))
{
// Do something if tap ends
}
}
}
But it still doesn't seem to work. When i build and run the app it does nothing :-(
I know i am doing something wrong but not sure what.
Handling Single Tap in Oculus Gear VR:
void Start()
{
OVRTouchpad.Create();
OVRTouchpad.TouchHandler += OVRTouchpad_TouchHandler;
}
void OVRTouchpad_TouchHandler (object sender, System.EventArgs e)
{
OVRTouchpad.TouchArgs touchArgs = (OVRTouchpad.TouchArgs)e;
OVRTouchpad.TouchEvent touchEvent = touchArgs.TouchType;
if(touchArgs.TouchType == OVRTouchpad.TouchEvent.SingleTap)
{
// Your response to Tap goes here.
}
}
Tap and Hold to Move:
You need to register Tapkey in your input following this
tutorial.
Add OVRPlayerController prefab to move around the scene. This will
let you move using keyboard W/A/S/D keys in Unity Editor.
Next you need to integrate Tap key with OVR Player controller script
to move in forward direction.
here are some useful links:
http://forum.unity3d.com/threads/gear-vr-touchpad-fps-script.373103/
http://rifty-business.blogspot.co.uk/2014/04/unity-pro-4-using-ovrcameracontroller.html

Categories

Resources