I have a game that I developed, and there are three scenes: the pause menu, the home screen menu, and the gameplay itself. During the gameplay, when the game is paused, it brings up the pause scene with:
[[CCDirector sharedDirector] pushScene:[PauseScene node]];
From the pause scene, there are three options: restart, resume, home menu. When clicking on resume it just pops the pause scene and goes back to the game scene to resume gameplay. When clicking on the home menu it keeps the gameplay scene in the background, and from the home menu screen, the gameplay is still running. So far, after much research, I have not been able to find a way to popScene (kick it out of the RAM pretty much) the gameplay scene from any scene but the gameplay scene. From the pause screen, I can use:
[[CCDirector sharedDirector] popScene];
to get rid of the pause scene, and if I run that code from a method on the gameplay scene, I can get rid of that instance of gameplay, but I need a way to popScene the gameplay node that is running in the background from the pause node. Overall, the basic question is: How can I pop a specific scene in Cocos2d from another scene?
There are two main features to change scenes in Cocos2d: pushScene/popScene and replaceScene.
The first is pushScene:
(void) pushScene: (CCScene *) scene
Suspends the execution of the running scene, pushing it on the stack
of suspended scenes. The new scene will be executed. Try to avoid big
stacks of pushed scenes to reduce memory allocation. ONLY call it if
there is a running scene.
This function utilizes a stack . It stores all the scenes in a Last In-First Out (LIFO) data structure. Basically it overlays the scenes and removes them in the reverse order of which they came. This is likely what you want to "push" (or bring up) the pause menu, and "pop" (remove) the pause scene back off to then resume gameplay.
To go back a layer, you simply call popScene:
(void) popScene
Pops out a scene from the queue. This scene will replace the running
one. The running scene will be deleted. If there are no more scenes in
the stack the execution is terminated. ONLY call it if there is a
running scene.
2.The other option, which I believe you will want, is replaceScene. This stops a currently running scene and replaces it with a completely new one. This is likely what you want for ending games or transitioning from the original main menu to the gameplay for the first time:
(void) replaceScene: (CCScene *) scene
Replaces the running scene with a new one. The running scene is
terminated. ONLY call it if there is a running scene
*Keep in mind: Use pushScene sparingly. It stores all of the scenes in memory so that they can later be popped of the stack. So don't store too many scenes and forget about them.
**Source: cocos2D website
EDIT 1:
You have a few options. Remember that replaceScene only replaces the currently running scene and is usually what you want to use. It does not "clear" the entire scene stack, so often you rarely want to use pushScene/popScene. In my demo below, I show you the issues with both. I do, however, try to conserve memory by when possible.
Using push/popScene:
1.Start running with the Home Screen.
2.When the user clicks "Play" button, use replaceScene to stop the Home Screen scene and start running the Game Play scene (keep in mind, Home Screen will no longer be running or saved anywhere in memory. if it has to remember any information, then save it to the stack with pushScene. The reason I do this is because the Home Screen will likely be the same each time and needs not continue to run while we play).
3.The user can now play for a bit. Then they want to pause for a bathroom break. They click a "Pause" button. Then use pushScene to save the current state of Game Play scene and begin running the Pause Menu scene. (Game Play is "paused" so to speak).
Your stack now looks like this:
//Bottom ----------------------> Top
[Game Play scene], [Pause Menu Scene]
4.After pausing for a bit, they could use pushScene to remove Pause Menu Scene from memory, and load the Game Play scene that is "paused" and resume play immediately.
The problem then becomes, well, what if you wanted to go from pause -> main menu. If you just used replaceScene on the Pause Menu, then the old game would linger in memory. You would need to find a way to purge that old Game Play scene.
The other option would have to be keeping Home Screen around by keeping it on the stack with a pushScene and then popping twice. This often causes "jumpy" transitions from my experience.
The preferred approach tends to be: create a Singleton Class and have that save the memory while you pause. (A Singleton Class is just a class that only ever has one instance. It is basically our "current game state" data. I recommend this tutorial.)
Using Replace Scene:
1.Start running with the Home Screen.
2.When the user clicks "Play" button, use replaceScene to stop the Home Screen scene and start running the Game Play scene (keep in mind, Home Screen will no longer be running or saved anywhere in memory. if it has to remember any information, then have a section of the Singleton Class for Home Screen data).
3.The user can now play for a bit. Then they want to pause for a bathroom break. They click a "Pause" button. Immediately "pause" your game and save all necessary sprites, game logic, etc to the Singleton Class, and then use replaceScene to quit from the Game Play scene and load Pause Scene. Then depending on their choice, you either continue play (step 4), or go to the Home Screen(step 5)
4.Reload everything necessary to render the game from the Singleton Class and replaceScene to quit from the Pause Menu and load up Game Play scene.
5.Use replaceScene to quit from the Pause Menu and load up the Home Screen.
Related
I'm building an iOS game and I had the game set up to start whenever the user first touched the screen. This was working out perfectly fine for testing until I added menu buttons as an overlay that would disappear when the game starts. Obviously I can no longer just check for user input, because the game now starts even if I tap a UI button.
I tried adding an invisible UI panel with a box collider behind my UI buttons with an OnPointerDown that would start the game, but for some reason it was acting very inconsistent and would sometime require 5+ taps to start the game. It also wouldn't start if you tapped on the player because it would hit his collider instead.
Is there a better way to do this?
I've been working on a really simple game for iOS that has just three scenes; the start scene, game scene, and game over scene. I was running the game and analyzing performance with the profiler and I noticed that when I changed scenes, which utilized the "SceneManager.LoadLevelAsync()" function, the CPU usage was around 90%. This is only for less than a second of course, and then the CPU usage drops again and I get around 70-80 fps, but this got me wondering if it would be more efficient for a simple game like this for me to simply have several UI "screens" (basically just a group of objects that I can activate and inactivate), which is what I did with my pause screen (it is just an overlay).
Of course this could have difficulties of its own, like I would have to restart the game on the same scene but it might help me with my problem of running the mute function I've built on my music manager, which is instantiated in the start scene, from a button that is on my game scene, and I wouldn't have to use the "DontDestroyOnLoad()" function on the music manager or my score manager (which stores the score to be displayed on the game over scene). But would it be inefficient to have so many inactivated objects, or is Unity pretty good at managing things like that?
In my personal opinon, menu system should be in one scene.
Having different scenes for every menu screen will cause overhead. Even it is for few micro seconds it will affect user experience when navigating between menus frequently. This is because of loading and destroying of each UI gameObject in scene.
While menu system based in one scene requires only activating and deactivating of gameobjects instead of instantiating and destroying.
You can have one root object for menu and assign it a canvas component and then you can add different panels for each screen and assign them canvas groups to toggle them on and off smoothly.
Here is a good tutorial for creating menu system: https://www.youtube.com/watch?v=DNqTUwnpLvI
Hope this helps.
You are correct, If your game / app is small you should keep it as simple as possible, if the only difference between your scenes is just UI & your "in game" scene is not that big, you can for sure keep using the same scene and have the UI activate/deactivate.
*Inactive objects DO eat memory.
I am trying to make a loading animation while using LoadLevelAsync to load the next scene.
The loading animation is a loading circle that continuously rotates in the middle of the screen, and I use LoadLevelAsync("NextScene") in the code. The problem is that every time right before the scene switch happens the circle will freeze for a few milliseconds before the next scene appears.
I do know that the less things the next scene contains, the shorter the freeze is, but I thought that LoadLevelAsync's purpose was for letting the user experience no lag during scene transitions. My next scene contains a background sprite, some buttons, scripts, and a 3MB audio file. The screen will always freeze for half a second before showing the next scene.
I am using Unity 4.6.3 with Pro feature. I'm also testing the results on a device. I have also tried out the AsyncOperation.allowSceneActivation with Coroutines, and unfortunately they don't work.
If anyone has a solution, or has any suggestions on smooth scene transitions, I would greatly appreciate them.
Thanks in advance.
I currently have a Pause scene in which the animation is a closing door. (Added to the gameworld via add layer). If I choose resume, it will open up and resume the game.
I've also a Quit button in which player will go back to the game menu. I like the
effect to be when quit is selected the door will open to the game menu. How do I go about doing this?
I created the same animation onto the game menu. So when I replace scene the animation will
be played from the game menu.
How do I do this?
On one of my UIViews (nib file) I have ImageView and a Button that starts full screen Cocos2d scene. From within that scene I want to close it and go back to UIView, so that user can later open the scene again but from different image in ImageView (and with different content on scene).
However, after the first time I "close" the scene the whole app reacts a lot slower to all touch events. It works fast as long as the scene is on, but when closed then performance goes down.
How should I properly close the scene so that I can restart it again? I've browsed through a lot of code samples and everywhere is just replaceWithScene:newScene.
Adding a UIView to a viewController that houses a running openGL layer slows the app, as anyone who has tried to incorporate a UIScrollView into a Cocos2D game can tell you. You are closing the scene, but likely not stopping the CCDirector, which is now running its own scheduler aside the application's scheduler. As they fight for dominance over the CPU, OpenGL invariably wins as it needs more power more often.
Ensure you are completely shutting down cocos2D and OpenGL. Even if only calling [[CCDirector sharedDirector] pause].