Game Development Community

Scene Unload Problems

by Matthew Hoesterey · in Torque X 2D · 07/26/2009 (7:38 pm) · 10 replies

Hi, So my game as reached a point were I am ready to unload and reload levels. Right now I'm unloading the first level, then loading a menu, then I'm reloading the first level.

I'm having two issues:

1) When the level is re-loaded my characters are not getting assigned input maps :(.
(I should mention I have the following code fix in playermanager.cs)
set
                {
                    if (value == _controlObject)
                        return;
                    if (_controlObject != null)
                    {
                        ProcessList.Instance.ClearMoveManager(_controlObject);
                        _moveManager._Consumer = null;
                    }
                    if (value != null)
                        ProcessList.Instance.SetMoveManager(value, _moveManager);
                    _controlObject = value;
                }

2) I'm getting some kind of object that is calling TestMove after the scene is unloaded.

> GarageGames.TorqueX.Framework2D.dll!GarageGames.Torque.T2D.T2DCollisionComponent.TestMove(ref float dt = 0.03, Microsoft.Xna.Framework.Vector2 velocity = {X:12.54905 Y:-85}, System.Collections.Generic.List<GarageGames.Torque.T2D.T2DCollisionInfo> collisions = Count = 0) Line 887 C#
GarageGames.TorqueX.Framework2D.dll!GarageGames.Torque.T2D.T2DPhysicsComponent.ProcessTick(GarageGames.Torque.Sim.Move move = null, float dt = 0.03) Line 353 + 0x32 bytes C#
GarageGames.TorqueX.Framework.dll!GarageGames.Torque.Sim.ProcessList._TickObjects(float tickSec = 0.03) Line 698 + 0x2b bytes C#
GarageGames.TorqueX.Framework.dll!GarageGames.Torque.Sim.ProcessList.AdvanceTick(float floatMS = 30.0) Line 500 + 0x16 bytes C#
GarageGames.TorqueX.Framework.dll!GarageGames.Torque.XNA.TorqueEngineComponent._UpdateSim(string eventName = "TimeEvent", float elapsed = 30.0) Line 781 + 0x1f bytes C#
GarageGames.TorqueX.Framework.dll!GarageGames.Torque.Core.TorqueEvent<float>._Trigger(System.Delegate d = {Method = {Void _UpdateSim(System.String, Single)}}) Line 74 + 0x1d bytes C#
GarageGames.TorqueX.Framework.dll!GarageGames.Torque.Core.TorqueEventManager._TriggerEvent(GarageGames.Torque.Core.TorqueEventManager.TorqueEventBase ev = {GarageGames.Torque.Core.TorqueEvent<float>}) Line 582 + 0xd bytes C#
GarageGames.TorqueX.Framework.dll!GarageGames.Torque.Core.TorqueEventManager.MgrProcessEvents() Line 433 + 0xb bytes C#
GarageGames.TorqueX.Framework.dll!GarageGames.Torque.Core.TorqueEventManager.ProcessEvents() Line 287 + 0x10 bytes C#
GarageGames.TorqueX.Framework.dll!GarageGames.Torque.XNA.TorqueEngineComponent.Update(Microsoft.Xna.Framework.GameTime gameTime = {Microsoft.Xna.Framework.GameTime}) Line 431 + 0x5 bytes C#
[External Code]
Game.exe!ChuDynasty.Game.Update(Microsoft.Xna.Framework.GameTime gameTime = {Microsoft.Xna.Framework.GameTime}) Line 226 + 0xa bytes C#
[External Code]
Game.exe!ChuDynasty.Game.Main() Line 114 + 0xe bytes C#

#1
07/26/2009 (8:04 pm)
Here is the pertinent code:

//Loading the menu and unloading the level
List<ISceneContainerObject> _allObjects = new List<ISceneContainerObject>();  
                    T2DSceneGraph _sceneGraph = (T2DSceneGraph)TorqueObjectDatabase.Instance.FindObject<T2DSceneGraph>("DefaultSceneGraph");  
                    Vector2 _vector = new Vector2();  
                    _sceneGraph.FindObjects(_vector, 9999, TorqueObjectType.AllObjects, (uint)0xFFFFFFFF, _allObjects);  
                    foreach (T2DSceneObject _obj in _allObjects)  
                    {  
                        if (_obj is T2DSceneObject)  
                        {  
                           _obj.MarkForDelete = true;  
                        }  
                    }  
                    Game.Instance.SceneLoader.UnloadLastScene();
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    _currentMenu = MenuSelect.winning;
                    guiWinning = new GuiWinScreen();
                    GUICanvas.Instance.SetContentControl(guiWinning);

//Loading the next level
public void LoadLevelData()
        {

            switch (_currentMenu)
            {
                case MenuSelect.levelTest:
                    Game.Instance.SceneLoader.OnSceneLoaded = SceneLoaded;
                    Game.Instance.SceneLoader.Load(@"data\levels\trible.txscene"); 
                    break;
            }
        }

public static void SceneLoaded(string sceneFile, TorqueSceneData scene)
        {
            GuiLoadingControl.Instance.LoadingFinished = true;
            Game.Instance.LoadSharedLevelData();
        }

public void LoadSharedLevelData()
        {
            //Creating MainCamera###############################################################################
            MainCamera = new T2DSceneCamera();
            MainCamera.Name = "MainCamera";
            MainCamera.Extent = new Vector2(1280, 720);
            MainCamera.FarDistance = 10000.0f;
            TorqueObjectDatabase.Instance.Register(MainCamera);
  
            //Creating Main Style
            GUIStyle playStyle = new GUIStyle();
            playStyle.PreserveAspectRatio = true;
            playStyle.IsOpaque = false;
            playStyle.Name = "GuiPlay";

            //Creating Main Scene View
            MainView = new GUISceneview();
            MainView.Style = playStyle;
            MainView.Camera = Game.Instance.MainCamera;
            MainView.Size = new Vector2(1280f * Game.Instance.UISizeXMult, 720f * Game.Instance.UISizeYMult);
            MainView.Position = new Vector2(0, 0);
            MainView.HorizSizing = HorizSizing.Center;
            MainView.VertSizing = VertSizing.Center;
            MainView.Folder = GuiPlayMainControl.Instance.ViewContainer;
            //Debug GUIs
            guiDebug = new ChuDynasty.debug.GuiDebug();


            //Finding and Initilizing Camera
            T2DStaticSprite _cameraMarker = TorqueObjectDatabase.Instance.FindObject<T2DStaticSprite>("CameraMarker");
            CameraController _camcont = _cameraMarker.Components.FindComponent<CameraController>();
            _camcont.SetupCamera();
            HitPriorityResolve.Setup(NumberOfPlayers);
        }

if (GuiLoadingControl.Instance.LoadingFinished)
            {
                sound.loading.SoundLoading.Instance.StopCue("animals001");
                Game.Instance.StartLevel();
            }

 public void StartLevel()
        {
            //Loading GUI Elements ########################################################################################
            GuiCinimatics guiCinimatics = new GuiCinimatics();
            GuiPlayMainControl guiPlayMain = new GuiPlayMainControl();
            GuiCombo guiCombo = new GuiCombo();    

            //Setting Canvus Content Control
            GUICanvas.Instance.SetContentControl(GuiPlayMainControl.Instance.ViewContainer);
            //GUICanvas.Instance.SetContentControl(MainView);

            //Setting up Level based on Game Type
            _gameTypeSetup.SetupGameType();

            switch (_gameTypeSetup.GameMode)
            {
                case ChuDynasty.gametype.Setup.GameModeEnum.chuBall:
                    gametype.chuball.ChuBallProperties _playerChuBallProperties = gametype.chuball.ChuBallProperties.Instance;
                    break;
            }
        }

I should mention I took out the code for the loading screen (all it does though is call the functions in the above code while displaying cool backgrounds with the GUIBitmap class. :)
#2
07/26/2009 (8:19 pm)
Hey Matt,

Try Scott's input map fix mentioned in this thread http://www.garagegames.com/community/forums/viewthread/97879. You might be getting the toggling effect and that is why you are losing your input.
#3
07/26/2009 (8:42 pm)
Ya that was it for the input map, Also I think I found the crash bug just now.

So In the process tick of one of my components I create Collectible orb spawns like so


if (_currentOrbCount < _maxOrbCount && Game.Instance.WinHandling.PlayerWin == 0)
            {
                if (_tick >= _spawnDelay)
                {
                    x = Game.Instance.SharedHelp.RandomFloat(0, _width);
                    x = x - _width / 2 + _offsetX;
                    y = Game.Instance.SharedHelp.RandomFloat(0, _height);
                    y = (y * -1) + _height / 2 + _offsetY;
                    
                    _orb = (T2DSceneObject)_collectableOrb.Clone();
                    _orb.Position = new Vector2(x, y);
                    TorqueObjectDatabase.Instance.Register(_orb);
                    
                    _tick = 0;
                    _currentOrbCount += 1;
                }
                _tick += 1;
            }

I just added in the check to see if a player has won and thus it won't spawn any more orbs as the board ends.

There must be some kind of race condition where spawning objects with a component can cause issues.


Now my only issue is the input map remembers the last input the user has pressed as the board unloads and will lock on the new board until you hit that same input again (ie.. if they had a held down it is locked up in the new board until you hit a)

I haven't gone in on this one yet but I'll post the fix if I find it.
#4
10/06/2009 (8:33 pm)
I posted this in another post but here is the solution to the input problem.

So here is how to fix it.

Whats happening is the move manager isn't set to null when the level is unloaded and a new level is loaded.

So in anycase I created this function in inputmap.cs


public void ClearControls()
{
_ResetMoveManager();
}

I then call it in my player input map component right after I create the input map.

Fixes the problem for me.

I feel like their must be something I'm missing though. I wouldn't be surprised if my level isn't unloading properly. It seams everyone is having trouble with that.

Well hope this helps.

John said this method will be added in the next release of torque for the rest of you guys that don't have the source.
#5
11/14/2009 (2:52 pm)
Hey Matthew I've just run into your issue about TestMove being called - mine's doing the exact same thing even after I implemented what you did. Did your error message also say, "Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object"?
The code I'm using to change levels follows:
Game.Instance.SceneLoader.UnloadLastScene();
Game.Instance.SceneLoader.Load(@"datalevelslevelTemplate.txscene");

GUIStyle stylePlayGui = new GUIStyle();
GUISceneview playGui = new GUISceneview();
playGui.Style = stylePlayGui;
GUICanvas.Instance.SetContentControl(playGui);
And right after the last line is when I get the error. I even set up boolean values in my component that moves automatically to not move the object, and I use the same one with my player who moves. Did you have to put this for every object that uses the physics component? Was there anything else that caused this error?

Thanks!
#6
11/14/2009 (3:07 pm)
Hi Travis,

So you may need to add in some code to manually kill all objects in the scene when you unload it. I have heard there is a bug with the unload method and had issues with it myself.

Here is how I unload scenes:
//Unload Main Scene
                    
                    List<ISceneContainerObject> _allObjects = new List<ISceneContainerObject>();  
                    T2DSceneGraph _sceneGraph = (T2DSceneGraph)TorqueObjectDatabase.Instance.FindObject<T2DSceneGraph>("DefaultSceneGraph");  
                    Vector2 _vector = new Vector2();  
                    _sceneGraph.FindObjects(_vector, 9999, TorqueObjectType.AllObjects, (uint)0xFFFFFFFF, _allObjects);  
                    foreach (T2DSceneObject _obj in _allObjects)  
                    {  
                        if (_obj is T2DSceneObject)  
                        {  
                           _obj.MarkForDelete = true;  
                        }  
                    }
//stopLevelSound
                    sound.loading.SoundLoading.Instance.EndLevelSound();
                    //unload
                    //unload level data
                    Game.Instance.SceneLoader.UnloadLastScene();

maybe an object isn't unloading properly. It's hard to say without seeing all of your code.

Do you know what object is causing the null reference?
#7
11/14/2009 (4:14 pm)
Hey Matthew,

I'm really new to this and don't know what to include to get ISceneContainerObject - it's currently unknown. Everything makes sense what you're saying, and I understand what each step is doing. I don't know what object it is that's messing up; the debugging information seems quite vague.
#8
11/14/2009 (6:13 pm)
Erm never mind found it in SceneGraph, but unfortunately this does not solve my problem. I get the below error:

Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
at GarageGames.Torque.T2D.T2DCollisionComponent.TestMove(Single& dt, Vector2
velocity, List`1 collisions) in C:Documents and SettingsXPMy DocumentsProgra
mming1501projectprojectTorque2DT2DComponentsT2DCollisionComponent.cs:line
887
.. and so on like yours.

Perhaps it's the way I'm implementing it? In my component when the player gets hit I want it to reload the level, so I call my class that has static variables FROM my component:
LevelTransitions.LoadLevel(LevelTransitions.getCurrLevel());
Where I then delete all objects, and attempt to load the scene (with all the code from above). Can I not call this action within the component because it's referencing it some how?
#9
11/14/2009 (6:45 pm)
That might do it. It sounds like the object your loading a level with gets deleted when you unload the level.

I wouldn't load or unload a level in a component but rather have the component call a method in an external class that takes care of your unloading and loading.

That will probably fix it for you. :)
#10
11/17/2009 (12:47 pm)
Ahhhhh success! The way my Ball Objects were set up were to create 2 smaller balls during _OnUnregister. duh! So when I deleted them they tried to create new ones; thus messing everything up. So just like yours, Matthew, some object was being created/spawned when it shouldn't be.