How can I set the display to stereoscopic programmatically in Unity for an app deployed to an Android device?
I want a UI menu where the user can toggle between "VR mode" and normal mode. I do not want VR mode by default as it should be an option at run-time. I know there is a setting for "Virtual Reality Supported" in the build settings, but again, I do not want this enabled by default.
Include using UnityEngine.XR; at the top.
Call XRSettings.LoadDeviceByName("") with empty string followed by XRSettings.enabled = false; to disable VR in the start function to disable VR.
When you want to enable it later on, call XRSettings.LoadDeviceByName("daydream") with the VR name followed by XRSettings.enabled = true;.
You should wait for a frame between each function call. That requires this to be done a corutine function.
Also, On some VR devices, you must go to Edit->Project Settings->Player and make sure that Virtual Reality Supported check-box is checked(true) before this will work. Then you can disable it in the Start function and enable it whenever you want.
EDIT:
This is known to work on some VR devices and not all VR devices. Although, it should work on Daydream VR. Complete code sample:
IEnumerator LoadDevice(string newDevice, bool enable)
{
XRSettings.LoadDeviceByName(newDevice);
yield return null;
XRSettings.enabled = enable;
}
void EnableVR()
{
StartCoroutine(LoadDevice("daydream", true));
}
void DisableVR()
{
StartCoroutine(LoadDevice("", false));
}
Call EnableVR() to enable vr and DisableVR() to disable it. If you are using anything other than daydream, pass the name of that VR device to the LoadDevice function in the EnableVR() function.
For newer builds of Unity (e.g. 2019.4.0f1) you can use the XR Plugin Management package.
To enable call:
XRGeneralSettings.Instance.Manager.InitializeLoader();
To disable call:
XRGeneralSettings.Instance.Manager.DeinitializeLoader();
I'm using Unity 2021 but this probably works in earlier versions, I'm also using XR Plug-in Management.
Start:
XRGeneralSettings.Instance.Manager.StartSubsystems();
Stop:
XRGeneralSettings.Instance.Manager.StopSubsystems();
Full documentation at:
https://docs.unity3d.com/Packages/com.unity.xr.management#4.0/manual/EndUser.html
2020.3.14f1
Doesn't work for me, I get this error when running my Android app.
Call to DeinitializeLoader without an initialized manager.Please make
sure wait for initialization to complete before calling this API.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
static void TryToDeinitializeOculusLoader()
{
XRGeneralSettings.Instance.Manager.DeinitializeLoader();
}
More context.
I try to unload the Oculus loader, before he manages to load the plugin.
I have an Android app, and the Oculus loader calls Application.Quit because the device is not an Oculus headset.
Waiting for XRGeneralSettings.Instance.Manager.isInitializationComplete takes too long.
Tried all RuntimeInitializeLoadType annotations.
OculusLoader.cs
#elif (UNITY_ANDROID && !UNITY_EDITOR)
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
static void RuntimeLoadOVRPlugin()
{
var supported = IsDeviceSupported();
if (supported == DeviceSupportedResult.ExitApplication)
{
Debug.LogError("\n\nExiting application:\n\nThis .apk was built with the Oculus XR Plugin loader enabled, but is attempting to run on a non-Oculus device.\nTo build for general Android devices, please disable the Oculus XR Plugin before building the Android player.\n\n\n");
Application.Quit();
}
if (supported != DeviceSupportedResult.Supported)
return;
try
{
if (!NativeMethods.LoadOVRPlugin(""))
Debug.LogError("Failed to load libOVRPlugin.so");
}
catch
{
// handle Android standalone build with Oculus XR Plugin installed but disabled in loader list.
}
}
#endif
SOLUTION
Made my build class extend IPreprocessBuildWithReport
public void OnPreprocessBuild(BuildReport report)
{
DisableXRLoaders(report);
}
///https://docs.unity3d.com/Packages/com.unity.xr.management#3.2/manual/EndUser.html
/// Do this as a setup step before you start a build, because the first thing that XR Plug-in Manager does at build time
/// is to serialize the loader list to the build target.
void DisableXRLoaders(BuildReport report)
{
XRGeneralSettingsPerBuildTarget buildTargetSettings;
EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings);
if (buildTargetSettings == null)
{
return;
}
XRGeneralSettings settings = buildTargetSettings.SettingsForBuildTarget(report.summary.platformGroup);
if (settings == null)
{
return;
}
XRManagerSettings loaderManager = settings.AssignedSettings;
if (loaderManager == null)
{
return;
}
var loaders = loaderManager.activeLoaders;
// If there are no loaders present in the current manager instance, then the settings will not be included in the current build.
if (loaders.Count == 0)
{
return;
}
var loadersForRemoval = new List<XRLoader>();
loadersForRemoval.AddRange(loaders);
foreach (var loader in loadersForRemoval)
{
loaderManager.TryRemoveLoader(loader);
}
}
public void Awake() {
StartCoroutine(SwitchToVR(()=>{
Debug.Log("Switched to VR Mode");
}));
//For disable VR Mode
XRSettings.enabled = false;
}
IEnumerator SwitchToVR(Action callback) {
// Device names are lowercase, as returned by `XRSettings.supportedDevices`.
// Google original, makes you specify
// string desiredDevice = "daydream"; // Or "cardboard".
// XRSettings.LoadDeviceByName(desiredDevice);
// this is slightly better;
string[] Devices = new string[] { "daydream", "cardboard" };
XRSettings.LoadDeviceByName(Devices);
// Must wait one frame after calling `XRSettings.LoadDeviceByName()`.
yield return null;
// Now it's ok to enable VR mode.
XRSettings.enabled = true;
callback.Invoke();
}
Related
I'm trying to build a Unity game on android/iOS by checking the development build option (so that I can use the profiler for debugging), but the SceneManager.LoadScene function doesn't work. I tried the scene name and index as the function parameters, but both didn't work.
It's important to note that the game works perfectly fine if I uncheck the development build option. I'm currently using Unity 2019.3.5.f1.
Edit:
I noticed that the scene loads without any problems when I run SceneManager.LoadScene from the Start() function. However, in most parts of my code, I run SceneManager.LoadScene from inside other functions, such as event handlers.
Here are a few code examples:
I run this function in Awake(). It reaches SceneManager.LoadScene("Credentials"); then stops:
private void ConfirmGooglePlayerServicesRequirements()
{
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
app = Firebase.FirebaseApp.DefaultInstance;
InitializeFirebase();
if (!signedIn)
SceneManager.LoadScene("Credentials");
}
else
{
Debug.LogError(System.String.Format(
"Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
}
This is a Firebase function which is called whenever a user signs in/out. It invokes an event called signedInEvent, which is handled by a function in another script that runs SceneManager.LoadScene.
void AuthStateChanged(object sender, System.EventArgs eventArgs)
{
if (auth.CurrentUser != user)
{
signedIn = user != auth.CurrentUser && auth.CurrentUser != null;
if (!signedIn && user != null)
{
Debug.Log("Signed out " + user.UserId);
}
user = auth.CurrentUser;
if (signedIn)
{
Debug.Log("Signed in " + user.UserId);
displayName = user.DisplayName ?? "";
emailAddress = user.Email ?? "";
userId = user.UserId ?? "";
signedInEvent.Invoke(displayName, emailAddress, userId);
}
}
}
Notes:
Scenes/FirstScene is enabled. It was disabled when I took the screenshot above because I was doing some tests.
I got this error using adb (android debug bridge) a while ago when I ran the game in development mode. It might be related to this issue: FindGameObjectWithTag can only be called from the main thread
Thanks!
FindGameObjectWithTag can only be called from the main thread
This is indeed related! Now that you posted your code:
ContinueWith might get called on a background thread. Most of the Unity API may only be called from the main thread otherwise it might either show an error such as the one you mentioned or sometimes it just fails silently.
In general this is often solved by something like e.g. UnityMainThreadDispatcher.
However, specifically for Firebase there already exists an extension called ContinueWithOnMainThread in the Firebase.Extensions namespace which makes sure the callback code is actually executed in the main thread where the Unity API works.
Extension methods for System.Threading.Tasks.Task and System.Threading.Tasks.Task<T> that allow the continuation function to be executed on the main thread in Unity.
using Firebase.Extensions;
private void ConfirmGooglePlayerServicesRequirements()
{
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
app = Firebase.FirebaseApp.DefaultInstance;
InitializeFirebase();
if (!signedIn)
{
SceneManager.LoadScene("Credentials");
}
}
else
{
Debug.LogError($"Could not resolve all Firebase dependencies: {dependencyStatus}", this);
// Firebase Unity SDK is not safe to use here.
}
});
}
I want to open a PWM pin to my buzzer. But If I try to call the pwmController.OpenPin(6) method, the app crashes with an System.Runtime.InteropServices.SEHException.
I had already double checked the sample sources like the ms-iot-samples. But I cannot see what my problems are.
An idea was that some permissions are missing, but if I try to add for exmaple <iot:Capability Name="lowLevelDevices" />, I cannot longer build the application.
Source
private PwmPin buzzerPin;
private PwmController pwmController;
public RainbowHAT()
{
// ... do something else
InitAsync();
}
private async void InitAsync()
{
Logger.Log(this, "Init");
// Setup PWM controller.
if (LightningProvider.IsLightningEnabled)
{
LowLevelDevicesController.DefaultProvider = LightningProvider.GetAggregateProvider();
}
var pwmControllers = await PwmController.GetControllersAsync(LightningPwmProvider.GetPwmProvider());
if (pwmControllers == null || pwmControllers.Count < 2)
{
throw new OperationCanceledException("Operation canceled due missing GPIO controller");
}
pwmController = pwmControllers[1];
pwmController.SetDesiredFrequency(50);
// Setup buzzer
buzzerPin = pwmController.OpenPin(13); <-- CRASH
buzzerPin.SetActiveDutyCyclePercentage(0.05);
buzzerPin.Start();
}
I also tried the following tip to reduce the min required Windows version, but this does not help, too.
PWM Controller needs Lightning support. So you need to set the controller driver as Direct Memory Mapped Driver. Here is a sample about PWM on Raspberry Pi.
You also need to modify the code as following:
private async void InitAsync()
{
Logger.Log(this, "Init");
// Setup PWM controller.
if (LightningProvider.IsLightningEnabled)
{
var pwmControllers = await PwmController.GetControllersAsync(LightningPwmProvider.GetPwmProvider());
if (pwmControllers == null || pwmControllers.Count < 2)
{
throw new OperationCanceledException("Operation canceled due missing GPIO controller");
}
pwmController = pwmControllers[1];
pwmController.SetDesiredFrequency(50);
// Setup buzzer
buzzerPin = pwmController.OpenPin(13);
buzzerPin.SetActiveDutyCyclePercentage(0.05);
buzzerPin.Start();
}
}
I'm trying to use a cloud TTS within my Unity game.
With the newer versions (I am using 2019.1), they have deprecated WWW in favour of UnityWebRequest(s) within the API.
I have tried the Unity Documentation, but that didn't work for me.
I have also tried other threads and they use WWW which is deprecated for my Unity version.
void Start()
{
StartCoroutine(PlayTTS());
}
IEnumerator PlayTTS()
{
using (UnityWebRequestMultimedia wr = new UnityWebRequestMultimedia.GetAudioClip(
"https://example.com/tts?text=Sample%20Text&voice=Male",
AudioType.OGGVORBIS)
)
{
yield return wr.Send();
if (wr.isNetworkError)
{
Debug.LogWarning(wr.error);
}
else
{
//AudioClip ttsClip = DownloadHandlerAudioClip.GetContent(wr);
}
}
}
The URL in a browser (I used Firefox) successfully loaded the audio clip allowing me to play it.
What I want it to do is play the TTS when something happens in the game, it has been done within the "void Start" for testing purposes.
Where am I going wrong?
Thanks in advance
Josh
UnityWebRequestMultimedia.GetAudioClip automatically adds a default DownloadHandlerAudioClip which has a property streamAudio.
Set this to true and add a check for UnityWebRequest.downloadedBytes in order to delay the playback before starting.
Something like
public AudioSource source;
IEnumerator PlayTTS()
{
using (var wr = new UnityWebRequestMultimedia.GetAudioClip(
"https://example.com/tts?text=Sample%20Text&voice=Male",
AudioType.OGGVORBIS)
)
{
((DownloadHandlerAudioClip)wr.downloadHandler).streamAudio = true;
wr.Send();
while(!wr.isNetworkError && wr.downloadedBytes <= someThreshold)
{
yield return null;
}
if (wr.isNetworkError)
{
Debug.LogWarning(wr.error);
}
else
{
// Here I'm not sure if you would use
source.PlayOneShot(DownloadHandlerAudioClip.GetContent(wr));
// or rather
source.PlayOneShot((DownloadHandlerAudioClip)wr.downloadHandler).audioClip);
}
}
}
Typed on smartphone so no warranty but I hope you get the idea
Update
I've checked with the different version of unity, it is working with Unity 2018.2.6f1 Personal which is installed on another laptop. But I've Unity 2018.2.12f1 Personal which gives the error. Is it a unity error?
I am using basic free plan of vuforia and working with Cloud recognition with vuforia. Cloud recognition part is working fine and trackable handler print the cloud recognized image name too. But when I trying to enable tracking for the tacked image target, it is only working for the very first image. After the first one, it gives the following error:
TargetSearchResult cloud-image-name could not be enabled for tracking.
UnityEngine.Debug:LogError(Object)
Vuforia.TargetFinder:EnableTracking(TargetSearchResult, GameObject)
CloudRec:OnNewSearchResult(TargetSearchResult) (at
Assets/Scripts/CloudRec.cs:66)
Vuforia.ObjectRecoBehaviour:Update()
Above error indicates the following line as the issue:
m_ObjectTracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
ImageTargetBehaviour imageTargetBehaviour = (ImageTargetBehaviour)m_ObjectTracker.TargetFinder.EnableTracking(targetSearchResult, ImageTargetTemplate.gameObject);
Tech version:
Vuforia version: 7.5.20 |
Unity 2018.2.12f1 Personal
Full code is here:
public class CloudRec : MonoBehaviour, ICloudRecoEventHandler
{
private CloudRecoBehaviour mCloudRecoBehaviour;
private bool mIsScanning = false;
private string mTargetMetadata = "";
public ImageTargetBehaviour ImageTargetTemplate;
ObjectTracker m_ObjectTracker;
TargetFinder m_TargetFinder;
// Use this for initialization
void Start()
{
// register this event handler at the cloud reco behaviour
mCloudRecoBehaviour = GetComponent<CloudRecoBehaviour>();
if (mCloudRecoBehaviour)
{
mCloudRecoBehaviour.RegisterEventHandler(this);
}
}
public void OnInitialized()
{
m_ObjectTracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
Debug.Log("Cloud Reco initialized");
}
public void OnInitError(TargetFinder.InitState initError)
{
Debug.Log("Cloud Reco init error " + initError.ToString());
}
public void OnUpdateError(TargetFinder.UpdateState updateError)
{
Debug.Log("Cloud Reco update error " + updateError.ToString());
}
public void OnStateChanged(bool scanning)
{
mIsScanning = scanning;
if (scanning)
{
// clear all known trackables
ObjectTracker tracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
tracker.TargetFinder.ClearTrackables(false);
}
}
// Here we handle a cloud target recognition event
public void OnNewSearchResult(TargetFinder.TargetSearchResult targetSearchResult)
{
GameObject newImageTarget = Instantiate(ImageTargetTemplate.gameObject) as GameObject;
GameObject augmentation = null;
if (augmentation != null)
augmentation.transform.SetParent(newImageTarget.transform);
if (ImageTargetTemplate)
{
m_ObjectTracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
ImageTargetBehaviour imageTargetBehaviour = (ImageTargetBehaviour)m_ObjectTracker.TargetFinder.EnableTracking(targetSearchResult, ImageTargetTemplate.gameObject);
//ImageTracker imageTracker = TrackerManager.Instance.GetTracker<ImageTracker>();
//ImageTargetBehaviour imageTargetBehaviour = (ImageTargetBehaviour)imageTracker.TargetFinder.EnableTracking(targetSearchResult, newImageTarget);
}
if (mIsScanning)
{
mCloudRecoBehaviour.CloudRecoEnabled = true;
}
}
// Update is called once per frame
void Update()
{
}
public void OnInitialized(TargetFinder targetFinder)
{
m_ObjectTracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
m_TargetFinder = targetFinder;
}
}
After almost a week of the search, I got the error cause. When running with unity the error occurs but when I build to Android or iOS it is working fine. So stopped doubt on the code and it made me think out of the box. So I decided to test on various versions of unity and vuforia with the same machine. It doesn't help to overcome the error. Eventually, I've tested with other machines and I got the error cause. It's because of hardware compatibility.
In my case, I am using mac pro-2009 mid which is not supporting ObjectTracking But I tested with the same code and same versions of tech on MacBook Air 2017 and Mac Pro mid-2014 it is working fine. So I conclude this as a hardware compatibility issue!
I've successfully got Akavache working for a Windows desktop, .NET 4.5 WPF project, but when I try to build it for the Xamarin (iOS and Android) targets, the BlobCache singleton is not properly initialized. BlobCache.Secure is null. (I've tried both the SQLite and 'vanilla' builds)
I'll be honest, I find the examples/documentation for Akavache a bit thin. I'm not a user of the Reactive stuff, I find much of Paul's code very opaque.
I'm just trying to do some very simple, secure caching of app state for a cross-platform app.
// where we store the user's application state
BlobCache.ApplicationName = "myApp";
BlobCache.EnsureInitialized();
public AppState State
{
get
{
return _appState;
}
set
{
_appState = value;
}
}
public void Load()
{
try
{
State = BlobCache.Secure.GetObjectAsync<AppState>.FirstOrDefault();
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
State = new AppState();
}
}
public void Save()
{
try
{
BlobCache.Secure.InsertObject("AppState", State);
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
So, there are some dumb tricks you have to do right now on Xamarin, that I've only very recently found out. I'm going to add these to the docs (or in the Android case, just fix the bug)
Xamarin.iOS
On iOS, Type.GetType() won't load assemblies, which isn't the same as any other platform. So, you have to run this silly goose code in your AppDelegate:
var r = new ModernDependencyResolver();
(new ReactiveUI.Registrations()).Register((f,t) => r.Register(f, t));
(new ReactiveUI.Cocoa.Registrations()).Register((f,t) => r.Register(f, t));
(new ReactiveUI.Mobile.Registrations()).Register((f,t) => r.Register(f, t));
RxApp.DependencyResolver = r;
(new Akavache.Registrations()).Register(r.Register);
(new Akavache.Mobile.Registrations()).Register(r.Register);
(new Akavache.Sqlite3.Registrations()).Register(r.Register);
Normally, this code runs AutoMagically™.
Xamarin.Android
Registration works fine on Xamarin.Android, but because of what I suspect is a bug in Akavache, you may have to register for AutoSuspend (even if you don't use it).
In all your activities, declare AutoSuspendActivityHelper autoSuspendHelper;
In the constructor, add:
autoSuspendHelper = new AutoSuspendActivityHelper(this);
autoSuspendHelper.OnCreate(bundle);
Override OnPause, OnResume, and OnSaveInstanceState and call the appropriate autoSuspendHelper method i.e:
autoSuspendHelper.OnPause();
More trouble?
Please let me know, either by Emailing me at paul#github.com or filing issues at github/akavache. I've shipped a production application with Akavache that runs on both iOS and Android, and it definitely works, but I realize it might be a bit Tricky™ to get stuff to work.