I have developed an unity android application. I want to perform some action when app is in foreground and also in background. Right now i am trying to print log in several interval of time in recursive manner. I am using this method to call my countdown timer.
void OnApplicationFocus(bool hasFocus){
StartBattle();
isPaused = !hasFocus;
Debug.Log("isPaused " + isPaused);
}
void OnApplicationPause(bool pauseStatus){
isPaused = pauseStatus;
StartBattle();
}
And this method to print data in a recursive manner.
public void StartBattle(){
StartCoroutine(BattleRecursive(0));
}
public IEnumerator BattleRecursive(int depth){
// Start Coroutine"
yield return new WaitForSeconds(2f);
if (depth == 0)
yield return StartCoroutine(BattleRecursive(depth + 1));
if (depth == 1)
yield return StartCoroutine(BattleRecursive(depth + 1));
Debug.Log("MyCoroutine is now finished at depth " + depth);
}
Log is printing very well when app is in foreground, But when app is in background, it is not printing anything.
You can't execute your Unity C# code in the background when Unity exit. This code you want to run in the background must be made in Java.
Write the code you want to Execute in Java not in C# then use Android's Intent to start a service. This requires that you send current Unity's Context or Activity to your plugin in order to start the service. You can find examples on how to do that here and here.
Related
I have a simple goal for my Unity application running on Android: create a function, which disables my GUI, creates a screenshot, stores the image and enables the GUI again.
This is my current code:
The whole UI is stored in a GameObject which my handler class holds as property:
private bool makeScreenshotFlag;
public GameObject UserInterface;
makeScreenshotFlag above is used in the Update function:
// Update is called once per frame
void Update()
{
if (makeScreenshotFlag)
{
// Working with this flag is necessary,
// since we need to wait for the next frame,
// where the gui is not visible;
MakeScreenShot();
}
}
If the corresponding button gets pressed I fire the following Method:
public void OnCreateScreenshotButtonPressed()
{
UserInterface.SetActive(false);
makeScreenshotFlag = true;
}
And at last the method for the screenshot itself:
private void MakeScreenShot()
{
if (UserInterface.activeSelf) return; // wait for the next frame if the gui is still active
try
{
string filename = "screenshot_" + DateTime.Now.ToString("HH-mm-ss--dd-MM-yyyy") + ".png";
ScreenCapture.CaptureScreenshot(filename);
}
catch (Exception e)
{
DebugHelper.NewLog = "Error capturing screenshot: " + e.Message;
}
finally
{
makeScreenshotFlag = false;
UserInterface.SetActive(true);
}
}
In theory this should work, however the documentation for ScreenCapture.CaptureScreenshot() states the following:
The CaptureScreenshot returns immediately on Android. The screen capture continues in the background. The resulting screen shot is saved in the file system after a few seconds.
In practice this means, when my screenshot is made, the GUI is already enabled again. So far I couldn't find any event or similar to get notified as soon as the screenshot is made. The only thing I can think of is to store the screenshot filename and check if the file exists each frame after the screenshot is started.
Is there any other, more practical way?
Most probably, you are capturing the frame with UI itself. It's better to wait till the end of the frame using Coroutine.
public IEnumerator MakeScreenShot()
{
// Wait till the last possible moment before screen rendering to hide the UI
yield return null;
// Disable UI
UserInterface.SetActive(false);
// Wait for screen rendering to complete
yield return new WaitForEndOfFrame();
// Take screenshot
Application.CaptureScreenshot("screenshot.png");
// Show UI after we're done
UserInterface.SetActive(true);
}
I made a loading bar following Brackeys' tutorial here (and have changed a few things later). I noticed that when I load a level, and my loading bar comes out, it first shows 0%, waits, then directly 100% - without intermediate values!.
I'm guessing it could be due to some lag that occurs because the game is already using a lot of hardware, and hasn't left enough power for the rest of the processes, because I tried making it load while doing other things at the same time.
Here is the code that loads the level and changes the slider's value:
public void LoadLevel(int sceneIndex) {
StartCoroutine(StartFadeThenLoadLevel(sceneIndex));
}
IEnumerator StartFadeThenLoadLevel(int sceneIndex)
{
yield return new WaitForSeconds(0.9f);
StartCoroutine(LoadAsynchronously(sceneIndex));
}
IEnumerator LoadAsynchronously(int sceneIndex) {
yield return null;
AsyncOperation operation = SceneManager.LoadSceneAsync(sceneIndex);
loadingScreen.SetActive(true);
chooseLevel.SetActive(false);
while (!operation.isDone) {
float progress = Mathf.Clamp01(operation.progress / 0.12f);
if (progress > 1) progress = 1;
slider.value = progress;
progressText.text = (int)(progress * 100f) + "%";
yield return null;
}
}
I saw that this mostly happens on more powerful phones, when it doesn't take more than a second to load the scene, than in older (slower) ones.
How can I fix this? I would like to have a very smooth loading bar that updates at least every 5%!
Thanks in advance for any help!
Edit: I added an FPS counter (Graphy) and i saw that there was a huge frame drop down to 1fps. That is probably what causes everything! Is there any way to prevent that?
(If you think this is likely to be the problem, please answer this question instead of the main one!)
int progress is 0 when operation.progress <= 0 and it is 1 when operation.progress > 0.
So your progress is always 1 because operation.progress is always positive.
Try to simplify your progress calculation:
IEnumerator load(){
yield return null;
//Begin to load the Scene you specify
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneIndex);
//Don't let the Scene activate until you allow it to
asyncOperation.allowSceneActivation = false;
Debug.Log("Pro :" + asyncOperation.progress);
//When the load is still in progress, output the Text and progress bar
while (!asyncOperation.isDone)
{
//Output the current progress
Debug.Log("Loading progress: " + (asyncOperation.progress * 100) + "%");
// Check if the load has finished
if (asyncOperation.progress >= 0.9f)
{
//Change the Text to show the Scene is ready
asyncOperation.allowSceneActivation = true;
}
yield return null;
}
}
If it's super important to show this loading bar, you may need to implement it by showing a platform native UI widget.
I currently have a code which retrieves data from a database and visualizes it in unity3D. However, everytime it retrieves data in the FixedUpdate() function, it spikes dramatically every 1 second. I'm thinking about using threading to do this but i'm not sure what i'm doing wrong.
This is the Function i call in the thread.
public void retrievefromDB(){
if (timeStep - prevTimeStep > 99) {
timeStep -= 1; //special for this dataset
query = "SELECT * FROM GridData2 WHERE timestep=" + timeStep;
if (showParent)
query += " AND (Level != 10)";
else
query += " AND (Level == 10)";
query += " AND temperature >= " + minTemp + " AND temperature <= " + maxTemp;
dt.Rows.Clear ();
dt = sqlDB.ExecuteQuery (query);
prevTimeStep = timeStep;
}
}
This code lags the scene every 1 second therefore i tried to put it into a thread.
void FixedUpdate()
{
Thread testthread = new Thread(new ThreadStart(retrievefromDB));
testthread.Start ();
}
After putting it in a thread, it keeps crashing the scene after awhile.
Can anyone tell me what I did wrongly? And how do i solve it?
The cause of your original issue is relatively obvious: database access is slow. If you put a database call inline in the FixedUpdate method, you're going to essentially pause your game's movement while the DB access happens (which may well take a second if you have to initialise a connection, for example).
The main issue with your threaded code as posted is that you are starting a new thread every time FixedUpdate is called. That means you're starting 60 new threads per second (by default) which will very quickly cripple your game!
While it's fine to use C# threads in Unity for this sort of work, a better approach would be to create a single thread and allow that to manage the timing, rather than creating a new thread each time the job runs. That would mean creating the thread in Awake() or Start() instead, and then using Thread.Sleep or similar to handle the timing.
Coroutines (as suggested by Mihai in his answer) are great for fixing the timing of events, but they still run on the game thread: if you put your DB code in a coroutine, you'll still see pauses when it runs. If you must run this DB access every second, you need it in a proper thread.
That said, have you considered that the DB access might be unnecessary? A more performant model might be to cache all of the data up front and use it from memory when you need it. (This might not be possible if the data is very dynamic, or if you're running in a memory-restricted environment like a mobile device...)
Whatever you do, you need to stop accessing your database every frame.
You only need the result only once every 60 or frames. You can do this easily by using a variable in which you add up the time passed since last call.
As for multi-threading in Unity, you have three options:
A multi-threading framework for Unity, like
https://www.assetstore.unity3d.com/en/#!/content/7285
C# built-in threading
You need to be careful not to call Unity specific API from the secondary threads you spawn. It's OK to send over data structures like Vector3, Color, etc., but don't call reference objects like GameObjects or Components.
https://msdn.microsoft.com/en-us/library/dd321439%28v=vs.110%29.aspx
Unity Coroutines
Coroutines are Unity's way of simulating multiple threads. It's quite a powerful tool for getting things to run asynchronously in the same thread
http://docs.unity3d.com/Manual/Coroutines.html
using System.Threading.Tasks;
public class Example
{
void StartOnDifferentThread()
{
Task.Factory
.StartNew(() =>
{
FunctionToRun();
})
.ContinueWith(task =>
{
if (task.IsCompleted)
{
// handle result
}
else if (task.IsFaulted)
{
// handle error
}
});
}
void FunctionToRun()
{
// do stuff
}
}
It finally works now. Just had to add these in retrievefromDB()
public void retrievefromDB(){
while(true){
if (timeStep - prevTimeStep > 99) {
timeStep -= 1; //special for this dataset
query = "SELECT * FROM GridData2 WHERE timestep=" + timeStep;
if (showParent)
query += " AND (Level != 10)";
else
query += " AND (Level == 10)";
query += " AND temperature >= " + minTemp + " AND temperature <= " + maxTemp;
dt.Rows.Clear ();
dt = sqlDB.ExecuteQuery (query);
prevTimeStep = timeStep;
}
Thread.Sleep(1);
}
}
And put this into the Start() function
testThread = UnityThreadHelper.CreateThread (() =>
{
UnityThreadHelper.TaskDistributor.Dispatch (() => retrievefromDB ());
});
testThread.Start ();
I'm using the threadhelper from
http://forum.unity3d.com/threads/unity-threading-helper.90128/
so u can go and check it out.
Thanks to everyone who helped! :)
I am writing a game with some logic that records player actions and then replays them in the same sequence at the same timing but with other influences involved at a later date.
So far I have the code sort of working, it records perfectly as I require it, and then plays back the sequence perfectly, but the issue has been timing.
As an simple example. If the character is to walk from A -> B -> C but to wait for x time at B, then the code easily traverses the path, but will not stop at B for the given time.
So I wrote a couple of functions that I thought might handle this as you can see here.
private IEnumerator followSequence()
{
// Loop through each action steps in sequence.
foreach (Action step in sequence)
{
//Check if the action time is less than the current clock time.
Debug.Log("Step.tick: " + step.tick + " || Clock.tick: " + clock.getTick());
if (clock.getTick() < step.tick)
{
//If so wait out the difference to resync the action.
yield return new WaitForSeconds(step.tick - clock.getTick());
}
// Carry out the step.
play(step);
}
yield break;
}
private void play(Action step)
{
Debug.Log(step.type);
// Set up a followpath script
FollowPath follow = (player.GetComponent<FollowPath>() ? player.GetComponent<FollowPath>() : player.AddComponent<FollowPath>());
// Tell the follow path script that object is not a guard.
follow.isGuard = false;
// Create a new point object on the map
Transform aPoint = (Transform)Instantiate(PathPoint, step.point, Quaternion.identity);
path.points.Add(aPoint);
// Initiate movement.
follow.Path = path;
follow.TravelPath();
// Check if action is an objective.
if (step.type == Action.Type.Action)
{
step.actions.OnClick();
}
}
Now if I comment out the *yield return new WaitForSeconds()* part in the first function, the code works as expected and completes the step in the sequence just not to the given timing.
When the same code is not commented out, then the timings work perfectly and the 2nd *Debug.log("step.type")* is called at the exact right time, however the character no longer moves, I.E. the *follow.TravelPath()* never seems to be executed.
I have tried making the 2nd function an IEnmerator and yielding to it. I have tried a custom wait() function. I have tried yielding to the TravelPath() function.
Has anyone any more ideas I have follow up or where I could look to try and resolve this?
Edit:
Code that calls followSequence() as requested:
private void playActions()
{
// Reset player immediately to the starting position
player.transform.position = sequence[0].point;
// Stop the character and freeze the scene when alarm sounds!.
player.GetComponent<IncrementController>().freeze();
player.GetComponent<IncrementController>().enabled = false;
// Restart the clock and scene.
clock.Restart();
sceneManager.restart();
// Activate guard and remove sphere.
guard.SetActive(true);
guardShere.SetActive(false);
// Stop recording;
recording = false;
line.SetActive(false);
// Start playback sequence.
StartCoroutine(followSequence());
}
The behavior is as expected, on the line there you do yield return the execution of the context is paused until you do MoveNext() (which does apparently never happen) on the enumerator, which is returned by followSequence.
But as far as I can see from the comments, you just want to wait for some seconds for resync so you don't need to return at all. In your case I don't see any reason why you might need an Enumerable of WaitForSeconds if you just want to skip some time. Without to know how you you call the function and the implementation of WaitForSeconds I would suggest you to change it's return type to void.
private void followSequence()
{
// Loop through each action steps in sequence.
foreach (Action step in sequence)
{
//Check if the action time is less than the current clock time.
Debug.Log("Step.tick: " + step.tick + " || Clock.tick: " + clock.getTick());
if (clock.getTick() < step.tick)
{
//If so wait out the difference to resync the action.
new WaitForSeconds(step.tick - clock.getTick());
}
// Carry out the step.
play(step);
}
}
I am creating an app that uses the devices camera.
I need to implement a function that checks if the user has allowed the app to access the devices camera, if they have not, the scene that uses the camera just shows a black screen.
Because Unity only supports Application.HasUserAuthorization in the webplayer platform I need to add the authorization check to the project through XCode after build.
Firstly I added the following code to didFinishLaunchingWithOptions in the UnityAppController.mm file:
// Determine camera access on iOS >= 7
if ([AVCaptureDevice respondsToSelector:#selector(requestAccessForMediaType:completionHandler:)]) {
// Completion handler will be dispatched on a separate thread
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (YES == granted) {
// User granted access to the camera, continue with app launch
CameraIsAvailable = YES;
CameraCheckDone = YES;
}
else {
CameraIsAvailable = NO;
CameraCheckDone = YES;
}
}];
}
else {
// iOS < 7 (camera access always OK)
CameraCheckDone = YES;
// Continue with app launch...
}
and the follwoing code to applicationDidBecomeActive
while (!CameraCheckDone) { }
if(!CameraIsAvailable){
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Authorize Camera"
message:#"Instructions go here"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
The result is that every time the app is run, a popup appears instructing the user to activate the camera, which is progress, but they still have access to the scene that shows the black screen instead of the camera view.
Is there any way that I can access the CameraIsAvilable variable from within Unity?
Any suggestions would be greatly appreciated! TIA!
I solved the problem by using the UnitySendMessage function.
Firstly I updated didFinishLaunchingWithOptions in UnityAppController.mm, here is the modified code:
// Determine camera access on iOS >= 7
if ([AVCaptureDevice respondsToSelector:#selector(requestAccessForMediaType:completionHandler:)]) {
// Completion handler will be dispatched on a separate thread
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (YES == granted) {
// User granted access to the camera, continue with app launch
CameraIsAvailable = YES;
CameraCheckDone = YES;
// ADDED THE FOLLOWING LINE THAT SENDS VALUE "YES" WHEN CAMERA ALLOWED
UnitySendMessage("iOSManager", "cameraCheck", "YES");
}
else {
CameraIsAvailable = NO;
CameraCheckDone = YES;
// ADDED THE FOLLOWING LINE THAT SENDS VALUE "NO" WHEN CAMERA IS NOT ALLOWED
UnitySendMessage("iOSManager", "cameraCheck", "NO");
}
}];
}
else {
// iOS < 7 (camera access always OK)
CameraCheckDone = YES;
// Continue with app launch...
}
Then in Unity3D, I added a GameObject called "iOSManager" to the first scene of my game (note: the GameObject name must match the first parameter of the UnitySendMessage function).
I created the following script with the cameraCheck() function (the function name matches the second parameter of the UnitySendMessage function):
#if UNITY_IOS
void cameraCheck(string value)
{
// DO SOMETHING WITH VALUE
}
#endif
Where the string "value" is the value sent from UnitySendMessage
Now I can use this value in Unity3D