Loading delay exceeded warning - Programmer Instrument - Unity - fmod

I'm getting an warning that says ": Loading delay exceeded, sound may play at incorrect time" whenever I try to use the example code from FMOD for Programmer Instrument
I thought maybe I need to wait for the FMOD.Sound openState == READY, but that doesn't seem to have worked. Am I missing something? Here's my code:
public static void PlayDialogue(string key, Action callback = null)
{
// Create Instance
FMOD.Studio.EventInstance dialogueInstance = FMODUnity.RuntimeManager.CreateInstance(instance.dialogueEvent);
// Pin the key string in memory and pass a pointer through the user data
GCHandle stringHandle = GCHandle.Alloc(key, GCHandleType.Pinned);
dialogueInstance.setUserData(GCHandle.ToIntPtr(stringHandle));
dialogueInstance.setCallback(instance.dialogueCallback);
}
[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
static FMOD.RESULT DialogueCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
{
// Get Instance
FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
// Retrieve the user data
IntPtr stringPtr;
instance.getUserData(out stringPtr);
// Get the string object
GCHandle stringHandle = GCHandle.FromIntPtr(stringPtr);
String key = stringHandle.Target as String;
switch (type)
{
case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
{
FMOD.MODE soundMode = FMOD.MODE.LOOP_NORMAL
| FMOD.MODE.CREATECOMPRESSEDSAMPLE
| FMOD.MODE.NONBLOCKING;
FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES programmerSoundProperties =
(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
FMOD.Sound dialogueSound;
if (key.Contains("."))
{
// Load Sound by Given Path
FMOD.RESULT soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(
Application.streamingAssetsPath + "/" + key, soundMode, out dialogueSound);
if (soundResult == FMOD.RESULT.OK)
{
programmerSoundProperties.sound = dialogueSound.handle;
programmerSoundProperties.subsoundIndex = -1;
Marshal.StructureToPtr(programmerSoundProperties, parameterPtr, false);
// Wait To Play
WaitForLoadThenPlay(instancePtr, parameterPtr);
}
}
else
{
// Load Sound Path
FMOD.Studio.SOUND_INFO dialogueSoundInfo;
FMOD.RESULT keyResult = FMODUnity.RuntimeManager.StudioSystem.getSoundInfo(key, out dialogueSoundInfo);
if (keyResult != FMOD.RESULT.OK) break;
// Load Sound
FMOD.RESULT soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(
dialogueSoundInfo.name_or_data, soundMode | dialogueSoundInfo.mode, ref dialogueSoundInfo.exinfo, out dialogueSound);
if (soundResult == FMOD.RESULT.OK)
{
programmerSoundProperties.sound = dialogueSound.handle;
programmerSoundProperties.subsoundIndex = dialogueSoundInfo.subsoundindex;
Marshal.StructureToPtr(programmerSoundProperties, parameterPtr, false);
// Wait To Play
WaitForLoadThenPlay(instancePtr, parameterPtr);
}
}
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
{
FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES parameter =
(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
FMOD.Sound sound = new FMOD.Sound(parameter.sound);
sound.release();
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
{
// Now the event has been destroyed, unpin the string memory so it can be garbage collected
stringHandle.Free();
break;
}
}
return FMOD.RESULT.OK;
}
private static void WaitForLoadThenPlay(IntPtr instancePtr, IntPtr parameterPtr)
=> instance.StartCoroutine(instance.WaitForLoadThenPlayRoutine(instancePtr, parameterPtr));
private IEnumerator WaitForLoadThenPlayRoutine(IntPtr instancePtr, IntPtr parameterPtr)
{
// Get Instance
FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
// Grab Sound Reference
FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES parameter =
(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
FMOD.Sound sound = new FMOD.Sound(parameter.sound);
// Wait for Load
FMOD.OPENSTATE state = FMOD.OPENSTATE.BUFFERING;
uint percentbuffered;
bool starving;
bool diskbusy;
while (state != FMOD.OPENSTATE.READY)
{
yield return null;
sound.getOpenState(out state, out percentbuffered, out starving, out diskbusy);
}
instance.start();
instance.release();
}

Here's what I ended up doing. I don't know if it makes sense or not, but it does get rid of the warnings. I really wish they'd fix the documentation. The example script doesn't work properly.
public static void PlayDialogue(string key, Action callback = null) => instance.StartCoroutine(PlayDialogueRoutine(key, callback));
public static IEnumerator PlayDialogueRoutine(string key, Action callback = null)
{
// Check if Already Playing
if (instance.activeDialogue.ContainsKey(key))
{
Debug.LogError("Tried to play already playing dialogue");
callback?.Invoke();
yield break;
}
// Load Sound Path
FMOD.Studio.SOUND_INFO dialogueSoundInfo;
FMOD.RESULT keyResult = FMODUnity.RuntimeManager.StudioSystem.getSoundInfo(key, out dialogueSoundInfo);
if (keyResult != FMOD.RESULT.OK)
{
Debug.LogError("Couldn't find dialogue with key: " + key);
callback?.Invoke();
yield break;
}
// Load Sound
FMOD.Sound dialogueSound;
FMOD.MODE soundMode = FMOD.MODE.LOOP_NORMAL
| FMOD.MODE.CREATECOMPRESSEDSAMPLE
| FMOD.MODE.NONBLOCKING;
FMOD.RESULT soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(
dialogueSoundInfo.name_or_data, soundMode | dialogueSoundInfo.mode,
ref dialogueSoundInfo.exinfo, out dialogueSound);
if (soundResult != FMOD.RESULT.OK)
{
Debug.LogError("Couldn't load sound: " + key);
callback?.Invoke();
yield break;
}
// Wait to Load
int maxFrameWait = 120;
FMOD.OPENSTATE openstate = FMOD.OPENSTATE.BUFFERING;
uint percentbuffered;
bool starving;
bool diskbusy;
while (openstate != FMOD.OPENSTATE.READY)
{
yield return null;
dialogueSound.getOpenState(out openstate, out percentbuffered, out starving, out diskbusy);
if (--maxFrameWait <= 0)
{
dialogueSound.release();
Debug.LogError("Failed to load dialogue sound " + key);
yield break;
}
}
// Create Instance
FMOD.Studio.EventInstance dialogueInstance = FMODUnity.RuntimeManager.CreateInstance(instance.dialogueEvent);
// Store Reference (and remove in play stopped callback)
instance.activeDialogue[key] = dialogueInstance;
// Pin Memory
FMODDialogueParameterWrapper soundWrapper = new FMODDialogueParameterWrapper(dialogueSound, dialogueSoundInfo, () =>
{
instance.activeDialogue.Remove(key);
callback?.Invoke();
});
GCHandle soundWrapperHandle = GCHandle.Alloc(soundWrapper, GCHandleType.Pinned);
dialogueInstance.setUserData(GCHandle.ToIntPtr(soundWrapperHandle));
// Set Callback
dialogueInstance.setCallback(instance.dialogueCallback,
FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND
| FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND
| FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED
| FMOD.Studio.EVENT_CALLBACK_TYPE.STOPPED);
// Play One Shot
dialogueInstance.start();
dialogueInstance.release();
}
[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
static FMOD.RESULT DialogueCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
{
// Get Instance
FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
// Retrieve the user data FMODDialogueParameterWrapper
IntPtr dialogueParameterWrapperPtr;
FMOD.RESULT result = instance.getUserData(out dialogueParameterWrapperPtr);
if (result != FMOD.RESULT.OK)
{
Debug.LogError("Failed to fetch user data for dialogue callback: " + result);
}
else if (dialogueParameterWrapperPtr != IntPtr.Zero)
{
GCHandle dialogueParameterWrapperHandle = GCHandle.FromIntPtr(dialogueParameterWrapperPtr);
FMODDialogueParameterWrapper dialogueParameterWrapper =
(FMODDialogueParameterWrapper)dialogueParameterWrapperHandle.Target;
switch (type)
{
case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
{
FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES programmerSoundProperties =
(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
programmerSoundProperties.sound = dialogueParameterWrapper.sound.handle;
programmerSoundProperties.subsoundIndex = dialogueParameterWrapper.soundInfo.subsoundindex;
Marshal.StructureToPtr(programmerSoundProperties, parameterPtr, false);
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
{
FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES parameter =
(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
FMOD.Sound sound = new FMOD.Sound(parameter.sound);
sound.release();
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
{
// Now the event has been destroyed, unpin the string memory so it can be garbage collected
Debug.Log("Freeing");
dialogueParameterWrapperHandle.Free();
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.STOPPED:
{
dialogueParameterWrapper.onStopCallback?.Invoke();
break;
}
}
}
return FMOD.RESULT.OK;
}
[StructLayout(LayoutKind.Sequential)]
class FMODDialogueParameterWrapper
{
public FMOD.Sound sound;
public FMOD.Studio.SOUND_INFO soundInfo;
public Action onStopCallback;
public FMODDialogueParameterWrapper(FMOD.Sound sound, FMOD.Studio.SOUND_INFO soundInfo, Action onStopCallback)
{
this.sound = sound;
this.soundInfo = soundInfo;
this.onStopCallback = onStopCallback;
}
}

Related

How to setting firebase remote config for unity?

lets to the point, so i want to make some configuration with my game to show Ads but I avoid to update game version too much, because of that I choose firebase remote config with this I can update setting/configuration without update the game version.
there is document for this but not very clear for newbie like me, you can check it here https://firebase.google.com/docs/remote-config/use-config-unity
I already make the script like on doc, but I don't understand how its work on show data because error string format, which is what I know on this firebase console is int format
the script like this :
public static _FirebaseRemoteConfig instance;
private void Awake()
{
instance = this;
}
Firebase.DependencyStatus dependencyStatus = Firebase.DependencyStatus.UnavailableOther;
// Use this for initialization
void Start()
{
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
{
dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
InitializeFirebase();
}
else
{
Debug.LogError(
"Could not resolve all Firebase dependencies: " + dependencyStatus);
}
});
}
public void InitializeFirebase()
{
System.Collections.Generic.Dictionary<string, object> defaults =
new System.Collections.Generic.Dictionary<string, object>();
defaults.Add("config_test_string", "default local string");
defaults.Add("config_test_int", 1);
defaults.Add("config_test_float", 1.0);
defaults.Add("config_test_bool", false);
Firebase.RemoteConfig.FirebaseRemoteConfig.SetDefaults(defaults);
Debug.Log("Remote config ready!");
}
public void FetchFireBase()
{
FetchDataAsync();
}
public void ShowData()
{
Debug.Log("maxCountToShowAdmob: " +
Firebase.RemoteConfig.FirebaseRemoteConfig.GetValue("MaxCountShowIntersitialAds").LongValue);
}
// Start a fetch request.
public Task FetchDataAsync()
{
Debug.Log("Fetching data...");
System.Threading.Tasks.Task fetchTask = Firebase.RemoteConfig.FirebaseRemoteConfig.FetchAsync(
TimeSpan.Zero);
return fetchTask.ContinueWith(FetchComplete);
}
void FetchComplete(Task fetchTask)
{
if (fetchTask.IsCanceled)
{
Debug.Log("Fetch canceled.");
}
else if (fetchTask.IsFaulted)
{
Debug.Log("Fetch encountered an error.");
}
else if (fetchTask.IsCompleted)
{
Debug.Log("Fetch completed successfully!");
}
var info = Firebase.RemoteConfig.FirebaseRemoteConfig.Info;
switch (info.LastFetchStatus)
{
case Firebase.RemoteConfig.LastFetchStatus.Success:
Firebase.RemoteConfig.FirebaseRemoteConfig.ActivateFetched();
Debug.Log(String.Format("Remote data loaded and ready (last fetch time {0}).",
info.FetchTime));
break;
case Firebase.RemoteConfig.LastFetchStatus.Failure:
switch (info.LastFetchFailureReason)
{
case Firebase.RemoteConfig.FetchFailureReason.Error:
Debug.Log("Fetch failed for unknown reason");
break;
case Firebase.RemoteConfig.FetchFailureReason.Throttled:
Debug.Log("Fetch throttled until " + info.ThrottledEndTime);
break;
}
break;
case Firebase.RemoteConfig.LastFetchStatus.Pending:
Debug.Log("Latest Fetch call still pending.");
break;
}
}

Looking for a way to add a progress bar for asynchronic task (Unity, C#)

I'm pretty new to writing asynchronous stuff in Unity. It is pretty easy to create a progress bar for asynchronous operation, but when it comes to doing it for the asynchronous task I'm not even sure when to start :(
public async void GetPhotoFromAndroidAndAddTexture(RawImage mainImage, string path)
{
await AddTextureToImage(mainImage, path);
}
public async Task<Texture2D> GetTextureFromAndroid(string path)
{
if(path != "" && path != " ")
{
#if UNITY_ANDROID
path = "file://" + path;
#endif
UnityWebRequest www = UnityWebRequestTexture.GetTexture(path);
var asyncOperation = www.SendWebRequest();
while(!asyncOperation.isDone)
{
await Task.Delay( 1000/30 );
}
return DownloadHandlerTexture.GetContent(www);
}
else
{
Debug.Log("The path is not correct!");
return null;
}
}
public async Task AddTextureToImage(RawImage mainImage, string path)
{
IProgress<int> progress;
Texture2D photoFromDevice = await GetTextureFromAndroid(path);
float textureH = photoFromDevice.height;
float textureW = photoFromDevice.width;
float aspectRat = textureH > textureW ? textureH/textureW : textureW/textureH;
mainImage.GetComponent<AspectRatioFitter>().aspectRatio = aspectRat;
mainImage.texture = photoFromDevice;
}
While the photo from android is being add as a texture I want to create a progress bar.
Thank you in advance for any suggestions
I can see no reason in your code why you should be using async at all. The only thing you actually are waiting for is the UnityWebRequest. async hasn't really any advantage over a simple Coroutine(IEnumerator) here.
In addition async stuff always also brings the problem of multi-threading ... Unity isn't thread safe so most of the API can only be used in the Unity mainthread.
You should rather stick to a Coroutine like e.g.
// wherever you want to show/use the progress
public float progress;
public void GetPhotoFromAndroidAndAddTexture(RawImage mainImage, string path)
{
StartCoroutine(AddTextureToImage(mainImage, path));
}
public IEnumerator AddTextureToImage(RawImage mainImage, string path)
{
Texture2D photoFromDevice;
if (path != "" && path != " ")
{
#if UNITY_ANDROID
path = "file://" + path;
#endif
UnityWebRequest www = UnityWebRequestTexture.GetTexture(path);
var asyncOperation = www.SendWebRequest();
while (!asyncOperation.isDone)
{
// wherever you want to show the progress:
progress = www.downloadProgress;
yield return null;
// or if you want to stick doing it in certain intervals
//yield return new WaitForSeconds(0.5f);
}
// you should also check for errors
if(www.error != null)
{
Debug.LogError("Something went wrong loading!", this);
yield break;
}
photoFromDevice = DownloadHandlerTexture.GetContent(www);
}
else
{
Debug.LogError("The path is not correct!", this);
yield break;
}
float textureH = photoFromDevice.height;
float textureW = photoFromDevice.width;
float aspectRat = textureH > textureW ? textureH / textureW : textureW / textureH;
mainImage.GetComponent<AspectRatioFitter>().aspectRatio = aspectRat;
mainImage.texture = photoFromDevice;
}
Alternatively often recommended is a kind of "dispatcher" or I prefer to call it a main thread worker where you can pass actions from other threads back to the main thread like e.g.
private ConcurrentQueue<Action> callbacks = new ConcurrentQueue<Action>();
private void Update()
{
while(callbacks.TryDequeue(var out a))
{
// Invokes the action in the main thread
a?.Invoke();
}
}
// example method to call
private void UpdateProgressBar(float progress)
{
// use progress
}
public async Task<Texture2D> GetTextureFromAndroid(string path)
{
if(path != "" && path != " ")
{
#if UNITY_ANDROID
path = "file://" + path;
#endif
UnityWebRequest www = UnityWebRequestTexture.GetTexture(path);
var asyncOperation = www.SendWebRequest();
while(!asyncOperation.isDone)
{
// add a callback for the main thread using a lambda expression
callbacks.Add(()=> { UpdateProgressBar(www.downloadProgress); });
await Task.Delay( 1000/30 );
}
return DownloadHandlerTexture.GetContent(www);
}
else
{
Debug.Log("The path is not correct!");
return null;
}
}
A cool way to have a simple progress bar for example can be achieved by using an UI.Image with
Image Type = Filled
Fill Method = Horizontal
Fill Origin = Left
now all you have to do is update the image.fillAmount

Cloud load failed with error code 7 using Unity GoolePlayGames plugin

I get the following log during on-device debugging
Error:
*** [Play Games Plugin DLL] ERROR: Cloud load failed with status code 7
Basically the OnStateLoaded() callback function always returns the boolean success = false and I can't figure out the reason why.
All that the plugin debugging logs mention is "Cloud load failed with status code 7".
According to the android doc, 7 is a generic "developer error", see https://developer.android.com/reference/com/google/android/gms/appstate/AppStateStatusCodes.html#STATUS_DEVELOPER_ERROR
I tried a quick sample and everything worked ok. Here are my steps:
Created a new game in the play console
(https://play.google.com/apps/publish)
Made sure Saved Games is set
to ON
Linked an Android Application Remembering the application ID
(the number after the title) and the package ID
Created a new project in Unity
Added the play games plugin (Assets/Import Package.../Custom
Package)
Set the application ID (Google Play Games/Android Setup...)
Switched the platform to Android (File/Build Settings...)
Set the player settings (bundle identifier and the keystore info)
Added a new script component to the camera
Saved everything and hit build and run.
Here are the contents:
using UnityEngine;
using System.Collections;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using System;
public class SaveSample : MonoBehaviour {
System.Action<bool> mAuthCallback;
GameData slot0;
void Start () {
slot0 = new GameData(0,"waiting for login....");
mAuthCallback = (bool success) => {
if (success) {
Debug.Log("Authentication was successful!");
slot0.Data =" loading....";
slot0.LoadState();
}
else {
Debug.LogWarning("Authentication failed!");
}
};
// make Play Games the default social implementation
PlayGamesPlatform.Activate();
// enable debug logs
PlayGamesPlatform.DebugLogEnabled = true;
//Login explicitly for this sample, usually this would be silent
PlayGamesPlatform.Instance.Authenticate(mAuthCallback, false);
}
protected void OnGUI() {
Screen.fullScreen = true;
int buttonHeight = Screen.height / 20;
int buttonWidth = Screen.width / 5;
GUI.skin.label.fontSize = 60;
GUI.skin.button.fontSize = 60;
Rect statusRect = new Rect(10,20,Screen.width,100);
Rect dataRect = new Rect( 10, 150, Screen.width,100);
Rect b1Rect = new Rect(10, 400, buttonWidth, buttonHeight);
Rect b2Rect = new Rect(b1Rect.x + 20 + buttonWidth,
b1Rect.y, buttonWidth, buttonHeight);
if(!Social.localUser.authenticated) {
if(GUI.Button(b1Rect, "Signin")) {
Social.localUser.Authenticate(mAuthCallback);
}
}
else {
// logged in, so show the load button and the contents of the saved data.
if(GUI.Button(b1Rect, "Load")) {
slot0.LoadState();
}
GUI.Label(dataRect, slot0.Data);
}
if(GUI.Button(b2Rect, "Save")) {
// just save a string, incrementing the number on the end.
int idx = slot0.Data.IndexOf("_");
if (idx > 0) {
int val = Convert.ToInt32(slot0.Data.Substring(idx+1));
val++;
slot0.Data = "Save_" + val;
}
else {
slot0.Data = "Save_0";
}
slot0.SaveState();
}
GUI.Label(statusRect, slot0.State);
}
// Class to handle save/load callbacks.
public class GameData : OnStateLoadedListener {
int slot;
string data;
string state;
public GameData(int slot, string data) {
this.slot = slot;
this.data = data;
this.state = "Initialized, modified";
}
public void LoadState() {
this.state += ", loading";
((PlayGamesPlatform)Social.Active).LoadState(0, this);
}
public void SaveState() {
byte[] bytes = new byte[data.Length * sizeof(char)];
System.Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
this.state += ", saving";
((PlayGamesPlatform) Social.Active).UpdateState(slot, bytes, this);
}
public void OnStateLoaded(bool success, int slot, byte[] data) {
if (success) {
Debug.Log ("Save game slot : " + slot + " loaded: " + data);
if (data != null) {
char[] chars = new char[data.Length / sizeof(char)];
System.Buffer.BlockCopy(data, 0, chars, 0, data.Length);
this.data = new string(chars);
this.state = "loaded";
} else {
Debug.Log ("Saved data is null");
this.data = "";
this.state = "loaded, but empty";
}
} else {
// handle failure
Debug.LogWarning ("Save game slot : " + slot + " failed!: ");
this.data = "";
this.state = "loading failed!";
}
}
public byte[] OnStateConflict(int slot, byte[] local, byte[] server) {
// resolve conflict and return a byte[] representing the
// resolved state.
Debug.LogWarning("Conflict in saved data!");
state = "conflicted";
// merge or resolve using app specific logic, here
byte[] resolved = local.Length > server.Length ? local : server;
char[] chars = new char[resolved.Length / sizeof(char)];
System.Buffer.BlockCopy(resolved, 0, chars, 0, resolved.Length);
this.data = new string(chars);
return resolved;
}
public void OnStateSaved(bool success, int slot) {
Debug.Log ("Save game slot : " + slot + " success: " + success);
state = "saved";
}
public string Data {
get {
return data;
}
set {
data = value;
state += ", modified";
}
}
public int Slot {
get {
return slot;
}
}
public string State {
get {
return state;
}
}
}
}
The error code 7 is because the Cloud Save API has been deprecated and is only currently accessible to existing games that have used the API. The Unity plugin version 0.9.11 has been updated to use the SavedGames API.
I tried a quick sample and everything worked ok. Here are my steps:
Created a new game in the play console
(https://play.google.com/apps/publish)
Made sure Saved Games is set to ON
Linked an Android Application Remembering the application ID
(the number after the title) and the package ID Created a new
project in Unity
Added the play games plugin (Assets/Import
Package.../Custom Package)
Set the application ID (Google Play
Games/Android Setup...)
Switched the platform to Android (File/Build
Settings...)
Set the player settings (bundle identifier and the
keystore info)
Added a new script component to the camera Saved
everything and hit build and run.
Here is my script:
using UnityEngine;
using System.Collections;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using System;
using GooglePlayGames.BasicApi.SavedGame;
public class SaveSample : MonoBehaviour {
System.Action<bool> mAuthCallback;
GameData slot0;
bool mSaving;
private Texture2D mScreenImage;
// Use this for initialization
void Start () {
slot0 = new GameData("New game");
mAuthCallback = (bool success) => {
if (success) {
Debug.Log("Authentication was successful!");
slot0.State = "Click load or save";
}
else {
Debug.LogWarning("Authentication failed!");
}
};
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
.EnableSavedGames()
.Build();
PlayGamesPlatform.InitializeInstance(config);
// Activate the Play Games platform. This will make it the default
// implementation of Social.Active
PlayGamesPlatform.Activate();
// enable debug logs (note: we do this because this is a sample; on your production
// app, you probably don't want this turned on by default, as it will fill the user's
// logs with debug info).
PlayGamesPlatform.DebugLogEnabled = true;
//Login explicitly for this sample, usually this would be silent
PlayGamesPlatform.Instance.Authenticate(mAuthCallback, false);
}
public void CaptureScreenshot() {
mScreenImage = new Texture2D(Screen.width, Screen.height);
mScreenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
mScreenImage.Apply();
}
protected virtual void OnGUI() {
Screen.fullScreen = true;
int buttonHeight = Screen.height / 20;
int buttonWidth = Screen.width / 5;
GUI.skin.label.fontSize = 60;
GUI.skin.button.fontSize = 60;
Rect statusRect = new Rect(10,20,Screen.width,200);
Rect dataRect = new Rect( 10, 250, Screen.width,100);
Rect b1Rect = new Rect(10, 800, buttonWidth, buttonHeight);
Rect b2Rect = new Rect(b1Rect.x + 20 + buttonWidth, b1Rect.y, buttonWidth, buttonHeight);
if(!Social.localUser.authenticated) {
if(GUI.Button(b1Rect, "Signin")) {
Social.localUser.Authenticate(mAuthCallback);
}
}
else {
if(GUI.Button(b1Rect, "Load")) {
mSaving = false;
((PlayGamesPlatform)Social.Active).SavedGame.ShowSelectSavedGameUI("Select game to load",
4,false,false,SavedGameSelected);
}
GUI.Label(dataRect, slot0.Data);
}
if(GUI.Button(b2Rect, "Save")) {
int idx = slot0.Data.IndexOf("_");
if (idx > 0) {
int val = Convert.ToInt32(slot0.Data.Substring(idx+1));
val++;
slot0.Data = "Save_" + val;
}
else {
slot0.Data = "Save_0";
}
mSaving = true;
CaptureScreenshot();
((PlayGamesPlatform)Social.Active).SavedGame.ShowSelectSavedGameUI("Save game progress",
4,true,true,SavedGameSelected);
}
GUI.Label(statusRect, slot0.State);
}
public void SavedGameSelected(SelectUIStatus status, ISavedGameMetadata game) {
if (status == SelectUIStatus.SavedGameSelected) {
string filename = game.Filename;
Debug.Log("opening saved game: " + game);
if(mSaving && (filename == null || filename.Length == 0)) {
filename = "save" + DateTime.Now.ToBinary();
}
if (mSaving) {
slot0.State = "Saving to " + filename;
}
else {
slot0.State = "Loading from " + filename;
}
//open the data.
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithAutomaticConflictResolution(filename,
DataSource.ReadCacheOrNetwork,
ConflictResolutionStrategy.UseLongestPlaytime,
SavedGameOpened);
} else {
Debug.LogWarning("Error selecting save game: " + status);
}
}
public void SavedGameOpened(SavedGameRequestStatus status, ISavedGameMetadata game) {
if(status == SavedGameRequestStatus.Success) {
if( mSaving) {
slot0.State = "Opened, now writing";
byte[] pngData = (mScreenImage!=null) ?mScreenImage.EncodeToPNG():null;
Debug.Log("Saving to " + game);
byte[] data = slot0.ToBytes();
TimeSpan playedTime = slot0.TotalPlayingTime;
SavedGameMetadataUpdate.Builder builder = new
SavedGameMetadataUpdate.Builder()
.WithUpdatedPlayedTime(playedTime)
.WithUpdatedDescription("Saved Game at " + DateTime.Now);
if (pngData != null) {
Debug.Log("Save image of len " + pngData.Length);
builder = builder.WithUpdatedPngCoverImage(pngData);
}
else {
Debug.Log ("No image avail");
}
SavedGameMetadataUpdate updatedMetadata = builder.Build();
((PlayGamesPlatform)Social.Active).SavedGame.CommitUpdate(game,updatedMetadata,data,SavedGameWritten);
} else {
slot0.State = "Opened, reading...";
((PlayGamesPlatform)Social.Active).SavedGame.ReadBinaryData(game,SavedGameLoaded);
}
} else {
Debug.LogWarning("Error opening game: " + status);
}
}
public void SavedGameLoaded(SavedGameRequestStatus status, byte[] data) {
if (status == SavedGameRequestStatus.Success) {
Debug.Log("SaveGameLoaded, success=" + status);
slot0 = GameData.FromBytes(data);
} else {
Debug.LogWarning("Error reading game: " + status);
}
}
public void SavedGameWritten(SavedGameRequestStatus status, ISavedGameMetadata game) {
if(status == SavedGameRequestStatus.Success) {
Debug.Log ("Game " + game.Description + " written");
slot0.State = "Saved!";
} else {
Debug.LogWarning("Error saving game: " + status);
}
}
public class GameData {
private TimeSpan mPlayingTime;
private DateTime mLoadedTime;
string mData;
string mState;
static readonly string HEADER = "GDv1";
public GameData(string data) {
mData = data;
mState = "Initialized, modified";
mPlayingTime = new TimeSpan();
mLoadedTime = DateTime.Now;
}
public TimeSpan TotalPlayingTime {
get {
TimeSpan delta = DateTime.Now.Subtract(mLoadedTime);
return mPlayingTime.Add(delta);
}
}
public override string ToString () {
string s = HEADER + ":" + mData;
s += ":" + TotalPlayingTime.TotalMilliseconds;
return s;
}
public byte[] ToBytes() {
return System.Text.ASCIIEncoding.Default.GetBytes(ToString());
}
public static GameData FromBytes (byte[] bytes) {
return FromString(System.Text.ASCIIEncoding.Default.GetString(bytes));
}
public static GameData FromString (string s) {
GameData gd = new GameData("initializing from string");
string[] p = s.Split(new char[] { ':' });
if (!p[0].StartsWith(HEADER)) {
Debug.LogError("Failed to parse game data from: " + s);
return gd;
}
gd.mData = p[1];
double val = Double.Parse(p[2]);
gd.mPlayingTime = TimeSpan.FromMilliseconds(val>0f?val:0f);
gd.mLoadedTime = DateTime.Now;
gd.mState = "Loaded successfully";
return gd;
}
public string Data {
get {
return mData;
}
set {
mData = value;
mState += ", modified";
}
}
public string State {
get {
return mState;
}
set {
mState = value;
}
}
}
}

WP - How to constantly update my location

Currently my location not updating when I change it to different location via emulator. But it will change after I restart my application. This is what I write when the app launch
private void Application_Launching(object sender, LaunchingEventArgs e)
{
IsolatedStorageSettings Settings = IsolatedStorageSettings.ApplicationSettings;
GeoCoordinate DefaultLocation = new GeoCoordinate(-6.595139, 106.793801);
Library.GPSServices MyGPS;
if (!Settings.Contains("FirstLaunch") || (bool)Settings["FirstLaunch"] == true)
{
Settings["FirstLaunch"] = false;
Settings["LastLocation"] = DefaultLocation;
Settings["SearchRadius"] = 1;
}
//If key not exist OR key value was set to false, ask for permission to use location
if (!Settings.Contains("LocationService") || (bool)Settings["LocationService"] == false)
{
var result = MessageBox.Show(
"Jendela Bogor need to know your location to work correctly, do you want to allow it?",
"Allow access to your location?",
MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
Settings["LocationService"] = true;
MyGPS = new Library.GPSServices();
}
else
{
Settings["LocationService"] = false;
}
Settings.Save();
}
else if ((bool)Settings["LocationService"] == true)
{
MyGPS = new Library.GPSServices();
}
}
I store my location in my application setting IsolatedStorage with name Settings["LastLocation"]
How should I do to constantly update my location in the Background using MVVM Pattern (MVVM-Light) so my PushPin on map in the ThirdPageView always updated?
EDIT
public GPSServices()
{
if ((bool)Settings["LocationService"] == true)
{
if (_watcher == null)
{
_watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
_watcher.MovementThreshold = 20;
}
StartWatcher();
_watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
_watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
}
else if ((bool)Settings["LocationService"] == false)
{
StopWatcher();
}
}
private void StartWatcher()
{
_watcher.Start();
}
private void StopWatcher()
{
if (_watcher != null)
_watcher.Stop();
}
private void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (e.Position.Location.IsUnknown)
{
MessageBox.Show("Please wait while your position is determined....");
return;
}
Settings["LastLocation"] = e.Position.Location;
Settings.Save();
}
System.Device.Location.GeoCoordinateWatcher provides what you need.
var geoWatcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
// This event fires every time the device location changes
geoWatcher.PositionChanged += (s, e) => {
//e.Position.Location will contain the current GeoCoordinate
};
geoWatcher.TryStart(false, TimeSpan.FromMilliseconds(2000));
is this helpful for you ? using GeocoordinateWatcher.PositionChanged event?
public Location()
{
GeoCoordinateWatcher location == new GeoCoordinateWatcher();
location.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(location_PositionChanged);
location.Start();
}
//event to track the location change
public void location_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
}

Debug Assertion Failed while using ActiveX control

Am newbie to this forum & MFC... Am getting Debug Assertion Failed
while using ActiveX control. Please guide me on this..My code looks
like this:
//CMyProjectDlg.h
class CMyProjectDlg: public CDialog
{
public:
CMyProject(CWnd* pParent = NULL);
enum { IDD = IDD_CMYPROJECT_DIALOG };
CMiDocView m_MIDOCtrl;
//Here CMiDocView is the class defined in other header file
protected:
BOOL bReadOCRByMODIAXCtrl(CString csFilePath, CString &csText);
};
//CMyProjectDlg.cpp
BOOL CMyProjectDlg::bReadOCRByMODIAXCtrl(CString csFilePath, CString &csText)
{
BOOL bRet = TRUE;
HRESULT hr = 0;
csText.Empty();
IUnknown *pVal = NULL;
IDocument *IDobj = NULL;
ILayout *ILayout = NULL;
IImages *IImages = NULL;
IImage *IImage = NULL;
IWords *IWords = NULL;
IWord *IWord = NULL;
try{
pVal = (IUnknown *) m_MIDOCtrl.GetDocument();
//After Executing this statement, I used to Debug Assertion failed...
if ( pVal != NULL )
{
hr = pVal->QueryInterface(IID_IDocument,(void**) &IDobj);
if ( SUCCEEDED(hr) )
{
hr = IDobj->OCR(miLANG_SYSDEFAULT,1,1);
if ( SUCCEEDED(hr) )
{
IDobj->get_Images(&IImages);
long iImageCount=0;
IImages->get_Count(&iImageCount);
for ( int img =0; img<iImageCount;img++)
{
IImages->get_Item(img,(IDispatch**)&IImage);
IImage->get_Layout(&ILayout);
long numWord=0;
ILayout->get_NumWords(&numWord);
ILayout->get_Words(&IWords);
IWords->get_Count(&numWord);
for ( long i=0; i<numWord;i++)
{
IWords->get_Item(i,(IDispatch**)&IWord);
CString csTemp;
BSTR result;
IWord->get_Text(&result);
char buf[256];
sprintf(buf,"%S",result);
csTemp.Format("%s",buf);
csText += csTemp;
csText +=" ";
//Release all objects
IWord->Release();
IWords->Release();
ILayout->Release();
IImage->Release();
}
IImages->Release();
} else {
bRet = FALSE;
}
} else {
bRet = FALSE;
}
IDobj->Close(0);
IDobj->Release();
pVal->Release();
} else {
bRet = FALSE;
}
pVal = NULL;
IDobj = NULL;
ILayout = NULL;
IImages = NULL;
IImage = NULL;
IWords = NULL;
IWord = NULL;
}
catch(...)
{
}
return bRet;
}
void CMyProjectDlg::OnBnClickedOCR()
{
//Dynamic object creation:
CMyProjectDlg *ob = new CMyProjectDlg;
((CMiDocView *)GetDlgItem(IDC_MIDOCVIEW1))->SetFileName("E:\\aaa.tiff");
//IDC_MIDOCVIEW is the ID for the ActiveX control..
((CMiDocView *) GetDlgItem( IDC_MIDOCVIEW1 ))->SetFitMode(1);
CString cs;
ob->bReadOCRByMODIAXCtrl("E:\\aaa.tiff",cs);
//Release the memory:
delete ob;
}
After clicking OCR button, I used to get Debug assertion failed on the line:
pVal = (IUnknown *) m_MIDOCtrl.GetDocument();
When i press retry, the control goes to
ASSERT(m_pCtrlsite != NULL ) in winocc.cpp
while Debugging i came to know that {CMIDOCView hWnd = 0x0000000}.
Please can anyone suggest me what am doing wrong here ??
Thank you all..