Using the SunBurn editor with IGF

Dec 20, 2011 at 1:15 PM

I've been watching the development of IGF for some time from the SunBurn site and after watching the tutorials, I'm really excited to start a project using IGF and SunBurn! It looks very well designed.

One part that I'm struggling with is how the SunBurn editor is used with IGF. From the tutorial, it seems like it goes a different route and one of the things I love about SunBurn is the editor. Can you give me some pointers on how the SunBurn editor would integrate with IGF? How would the tutorial have been different?

Thanks!

Coordinator
Dec 20, 2011 at 2:34 PM

Hi,

First thanks for the nice comments. I wish you good luck developing your game with IGF and SunBurn ;)

Now, the IGF plugs nicely with the SunBurn Editor as it is designed from the groud up the same way SynapseGaming design their SunBurn features. However, you must be advise that some IGF features aren't yet fully integrated within the SunBurn Editor.

For instance, the InstancingEntity and SpriteObject classes aren't yet ready to be plugged within the Editor (planned for v1.0).

You still can easily add for instance ParticleSystems using the SunBurn Editor (look on page 2 of the videos I posted in my website to see how it can be achieved: http://indiefreaks.com/videos/).

The tutorials didn't make use of the Editor at all but since I'm recording back all the tutorial videos (see my blog post explaining why: http://indiefreaks.com/2011/12/20/ace-on-steroids-a-new-take/), I decided to show how to use the SunBurn Editor for some scene management.

If you're willing to start right now using the SunBurn Editor in your game using IGF, that's pretty straightforward:

In your overriden Application.InitializeSunBurn() method, add the following code:

SunBurn.AddManager(new SunBurnEditor(SunBurn));

Then add a ContentRepository item and a Scene item to your Content project as you'd do using SunBurn in a "simple" Windows Xna project.

Finally, in a GameState inherited class where you'd add a SunBurnLayer instance (follow the existing tutorials to see how this is done), instead of calling adding a basic SunBurnLayer instance as:

public class MyGameState : GameState
{
    // I omitted required code to focus on the purpose of this thread...
    public override void Initialize()
    {
          AddLayer(new SunBurnLayer(this));
     }
}

You'll just create the instance this way:

public class MyGameState : GameState
{
    // I omitted required code to focus on the purpose of this thread...
    public override void Initialize()
    {
          // SunBurnLayer class has a constructor which accepts a path to a given ContentRepository item and Scene item that will be automatically loaded by the framework.
          AddLayer(new SunBurnLayer(this, "Content/MyContentRepositoryPath", "Content/MyScenePath"));
     }
}

With such call, you'll just have to launch your game and hit the standard F11 key to see the SunBurn Editor showing up and letting you import Content in your ContentRepository and adding stuff to your Scene instance.

If you have any difficulty using it, let me know and I'll try to help you out. ;)  

 

 

Dec 20, 2011 at 7:57 PM

Thank you for such a great reply! It is very helpful!

I've got two weeks off from work right now and I really hope to get this project off the ground. I've been thinking it for a while now and your project really fills in a lot of holes, so thanks again! :)

Dec 21, 2011 at 3:04 PM

Using your directions, I did get the Editor wired up. One thing that is missing is an 'Environment' though. I added an Environment to my Content, but I'm not sure how to wire that into the Editor through IGF.

Looking at an 'out-of-the-box' SunBurn project, I don't really see how that's wired up there either. The only thing I see is:

environment = Content.Load<SceneEnvironment>("Environment/Environment");

I'm not sure if just loading it causes it to be found by the Editor.

Dec 21, 2011 at 3:22 PM

Nevermind, I think I figured it out. I implemented an IContentHost and added this code to the LoadContent method:

manager.Load<SynapseGaming.LightingSystem.Core.SceneEnvironment>("Environment/Environment"

);

Once that was loaded, the Editor picked up the Environment.

Dec 21, 2011 at 4:22 PM

One thing I've noticed in trying to create a "PlayerAgent" and attach it to my object in the Editor is that the PlayerAgent class in IGF isn't serializable, so the Editor throws an exception while saving. I'll create an Issue to track this.

Coordinator
Dec 22, 2011 at 8:00 AM

Hi MustangDude,

For the SceneEnvironment, here is how it should be applied when you want it to be linked to the Editor & IGF compliant:

In order to have the SceneEnvironment instance in the Editor, you need to load it using the ContentManager as you've done it: SunBurn uses this pattern to know all SceneEnvironments that are available in a project (looking in the Content Project).

But if you want to be compliant to IGF, you'll need to attach it to a Camera.
In IGF, the SunBurn SceneState instance and SunBurn SceneEnvironment instance used for rendering are created and stored within a Camera (Indiefreaks.Xna.Rendering.Camera namespace) which is activated by submitting it to the CameraManager (retrieved from the SunBurn SceneInterface instance using SceneInterface.GetManager<ICameraManager>(true)).
This is made so that you can therefore have multiple cameras in your game and potentially have different settings for the SceneEnvironment values ;)

So, to sum up best practice:

// In GameState Initialize override:
public void ovderride Initialize()
{
     // create your camera and submit it to the CameraManager:
     var camera = new TargetCamera3D(...);
     SunBurn.GetManager<ICameraManager>(true).Submit(camera);

     // load your SceneEnvironment
     var environment = Application.Content.Load<SceneEnvironment>("MySceneEnvironment");

     // and apply to the camera
     camera.SceneEnvironment = environment;
}

Voilà ;)

 

As for the PlayerAgent not being serialized, this is part of the things that need to be Editor compliant, it doesn't just requires Serialization but also in Editor Creation logic which isn't implemented yet: a PlayerAgent (as well as a NonPlayerAgent) need to be created using SessionManager.CurrentSession.CreatePlayerAgent(IdentifiedPlayer identifiedPlayer) so that its Behaviors and Commands get processed.

I'm also planning to add some specific features to the Logic stuff in IGF so that you can create and manage directly from the Editor your logic scripts adding a Script editor to it :) But we're not quite there yet...

Until then, you'll have to create PlayerAgent instances in your code.

 

Dec 22, 2011 at 12:27 PM

Hi Philippe,

Thanks again for all your help!

One problem I'm having with adding that PlayerAgent through the code is that when I do open the Editor after that point, the Editor finds the PlayerAgent I added and wants to serialize it as part of the scene and I run into that same serialization problem. If I get past that part with my "custom" DLLs with the Serialization attribute, I'm afraid that when it loads the scene next time, it won't be properly initialized since it wasn't created through CreatePlayerAgent.

Here's how I'm attaching the Agent to my scene object created in the Editor from the GameplayGameState class:

 // Create the player instance
var player = SessionManager.CurrentSession.CreatePlayerAgent<Player>(SessionManager.CurrentSession.LocalPlayers[0]);

LoadingCompleted += delegate
{
    SceneObject playerObject;

    if (!SunBurn.ObjectManager.Find<SceneObject>("BroomBot", false, out playerObject))
        throw new ApplicationException("BroomBot object not found in scene.");

    playerObject.Components.Add(player);
};

Any thoughts? Should I hold off on trying to use the Editor until v1.0?

Thanks!
Andy

Coordinator
Dec 22, 2011 at 1:18 PM
Edited Dec 22, 2011 at 1:34 PM

Nope Andy, the Editor is great for certain things and shouldn't be avoided at all. You are actually raising an issue that the framework should handle ;)

Normally, since the PlayerAgent inherits from BaseComponent and not one of the serialized ones, it raises an exception.

I have to check on my own, but until I implement full editor support to PlayerAgent, maybe the best should be to let it serialize using the SerializeAttribute and inherit from the BaseComponentManualSerialization and not implement the GetObjectData and SetObjectData methods.

Even better, I think I'll ask SynapseGaming to add a Type check in the Save/Load of the editor where if a Component is using BaseComponent and one of the Serialized ones, it shouldn't try to save/load it first place ;)

[Edit] By the way, you may also consider creating game entities by code only instead of adding a PlayerAgent to an existing SceneObject in the scene that you'll get from the ObjectManager.Find() method.

If you need to spawn it somewhere, you still can create a SpawnEntity inheriting from SceneEntity or SceneObject and use a custom model (down arrow) to place it in the Scene.
You'd then grab the SpawnEntity instance from the Scene using ObjectManager.Find("player_spawn", false, out playerSpawn) to then use the playerSpawn.World to set your player BroomBot SceneObject ;)

Just another option...

Dec 22, 2011 at 5:31 PM
Edited Dec 22, 2011 at 5:35 PM

Thanks for making that code update and for the additional ideas!

Let me run what I've been trying to accomplish by you...

What I was trying to do was use the Editor to import my Models and place them all in the scene just the way I want. I was then thinking of adding components/agents to each object depending on its role in the game. For the object the player controls, I'd add my "PlayerAgent" derived class as a component via the Editor.

The Agent would configure the BEPU physics object like this:

public override void OnAddedToParentObject()
{
        base.OnAddedToParentObject();

        ((SceneObject)ParentObject).CollisionMove = new BoxCollisionMove((SceneObject)ParentObject);
}

For the objects controlled by AI, I'd do something similar but adding a NonPlayerAgent derived class.

All the other objects that are BEPU static, I created an Agent derived class that created a static collision object like this:

public override void OnAddedToParentObject()
{
        base.OnAddedToParentObject();

        SceneObject sceneObject = ParentObject as SceneObject;
            
        sceneObject.CollisionMove = new StaticMeshCollisionMove(sceneObject, sceneObject.ModelAsset.Asset);
}


(I have run into a really strange problem when anything collides with a StaticMeshCollisionMove object when following this pattern though. The code that casts the StaticMeshCollisionMove object to an ICollisionMove object throws an type cast exception, even though it's derived from ICollisionMove! I believe there is something going on with how the types are loaded from the Editor because the same type loaded from different assemblies aren't equal.)

Do you think I'm going down a bad direction here? I kind of like it because I can create new levels pretty easily without a lot of code knowing the specifics.

Thanks!
Andy


Coordinator
Dec 22, 2011 at 7:56 PM

This StaticMeshCollisionMove shouldn't raise any issue when applied to a SceneObject.CollisionMove instance.
What is exactly the exception you're getting?

Dec 22, 2011 at 8:20 PM

Here ya go. This is with the latest changeset. Looking at it closer, the StaticMeshCollisionMove object is an ICollisionMove not an ICollisionObject, so that's why it's throwing.

System.InvalidCastException was unhandled
  Message=Unable to cast object of type 'Indiefreaks.Xna.Physics.Entities.StaticMeshCollisionMove' to type 'SynapseGaming.LightingSystem.Collision.ICollisionObject'.
  Source=Indiefreaks.Game.Physics
  StackTrace:
       at Indiefreaks.Xna.Physics.BEPUCollisionMove.OnCollisionDetected(Collidable sender, Collidable other, CollidablePairHandler pair) in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\Physics\BEPUCollisionMove.cs:line 94
       at Indiefreaks.Xna.Physics.BEPUEntityCollisionMove`2.OnEntityCollisionDetected(EntityCollidable sender, Collidable other, CollidablePairHandler pair) in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\Physics\BEPUEntityCollisionMove.cs:line 121
       at BEPUphysics.Collidables.Events.ContactEventManager`1.DispatchEvents() in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\BEPU\Collidables\Events\ContactEventManager.cs:line 191
       at BEPUphysics.Collidables.Events.EntryEventManager`1.BEPUphysics.OtherSpaceStages.IDeferredEventCreator.DispatchEvents() in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\BEPU\Collidables\Events\EntryEventManager.cs:line 163
       at BEPUphysics.OtherSpaceStages.DeferredEventDispatcher.UpdateStage() in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\BEPU\OtherSpaceStages\DeferredEventDispatcher.cs:line 81
       at BEPUphysics.ProcessingStage.Update() in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\BEPU\ProcessingStage.cs:line 34
       at BEPUphysics.Space.DoTimeStep() in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\BEPU\Space.cs:line 499
       at BEPUphysics.Space.Update() in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\BEPU\Space.cs:line 510
       at Indiefreaks.Xna.Physics.BEPUPhysicsManager.Update(GameTime gameTime) in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Physics\Physics\BEPUPhysicsManager.cs:line 121
       at SynapseGaming.LightingSystem.Core.SceneInterface.Update(GameTime gameTime)
       at Indiefreaks.Xna.Core.GameState.Indiefreaks.Xna.Core.IUpdate.Update(GameTime gameTime) in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Framework\Core\GameState.cs:line 117
       at Indiefreaks.Xna.Core.Application.Update(GameTime gameTime) in C:\PhoneCode\Xna\IGF\1 - Source\Main\Indiefreaks Game Framework Solution\Indiefreaks.Game.Framework\Core\Application.cs:line 323
       at Microsoft.Xna.Framework.Game.Tick()
       at Microsoft.Xna.Framework.Game.HostIdle(Object sender, EventArgs e)
       at Microsoft.Xna.Framework.GameHost.OnIdle()
       at Microsoft.Xna.Framework.WindowsGameHost.RunOneFrame()
       at Microsoft.Xna.Framework.WindowsGameHost.ApplicationIdle(Object sender, EventArgs e)
       at System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FDoIdle(Int32 grfidlef)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.Run(Form mainForm)
       at Microsoft.Xna.Framework.WindowsGameHost.Run()
       at Microsoft.Xna.Framework.Game.RunGame(Boolean useBlockingRun)
       at Microsoft.Xna.Framework.Game.Run()
       at FlyingCodeMonkeys.BroomBots3D.Boot.Main() in C:\PhoneCode\Xna\BroomBots3D\BroomBots3D\BroomBots3D\BroomBotsApplication.cs:line 22
  InnerException:

Additional Debugger Info:

+  other.Tag is {Indiefreaks.Xna.Physics.Entities.StaticMeshCollisionMove} 

+  other.Tag.GetType() {Name = "StaticMeshCollisionMove" FullName = "Indiefreaks.Xna.Physics.Entities.StaticMeshCollisionMove"} System.Type {System.RuntimeType}

+  other.Tag.GetType().GetInterfaces()[0] {Name = "ICollisionMove" FullName = "SynapseGaming.LightingSystem.Collision.ICollisionMove"} System.Type {System.RuntimeType}



Coordinator
Dec 22, 2011 at 10:36 PM

Ok, got the bug in my code: I'll change it in the next changeset but until then in the StaticMeshCollisionMove.cs file under the Physics library, replace:

        public override void Initialize()
        {
            StaticMesh.Events.InitialCollisionDetected += OnStaticMeshCollisionDetected;
            StaticMesh.Tag = this;

            base.Initialize();
        }

By this:

        public override void Initialize()
        {
            StaticMesh.Events.InitialCollisionDetected += OnStaticMeshCollisionDetected;
            StaticMesh.Tag = ParentObject;

            base.Initialize();
        }

And you should be ok.

 

Coordinator
Dec 22, 2011 at 10:37 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Dec 22, 2011 at 11:58 PM

That fixed it! Thanks again!

Feb 13, 2012 at 1:28 AM

I wanted to try and see if I can get this working but I get exception error on this line of code:

           

SunBurn.AddManager(new SunBurnEditor(SunBurn));

and the exception says:

System.Exception was unhandled
  Message=SunBurn Editor requires the main thread to use a single threaded apartment model. Please add the '[STAThread]' attribute to the Program.Main() method.
  Source=SynapseGaming-SunBurn-Pro
  StackTrace:
       at SynapseGaming.LightingSystem.Editor.SunBurnEditor..ctor(IManagerServiceProvider sceneinterface)
       at Indiefreaks.AceOnSteroids.AceOnSteroidsApplication.InitializeSunBurn() in C:\Users\dwc2987@msn.com\Desktop\New folder\Ace on Steroids Solution\Ace on Steroids\Ace on Steroids\AceOnSteroidsApplication.cs:line 47
       at Indiefreaks.Xna.Core.Application.CreateSceneInterface()
       at Indiefreaks.Xna.Core.Application.get_SunBurn()
       at Indiefreaks.Xna.Core.GameState..ctor(String name, Application application, Boolean useApplicationContent, Int32 bufferWidth, Int32 bufferHeight, DetailPreference precisionMode, DetailPreference lightingRange)
       at Indiefreaks.Xna.Core.GameState..ctor(String name, Application application, Int32 bufferWidth, Int32 bufferHeight, DetailPreference precisionMode, DetailPreference lightingRange)
       at Indiefreaks.Xna.Core.GameState..ctor(String name, Application application)
       at Indiefreaks.AceOnSteroids.IntroductionGameState..ctor(Application application) in C:\Users\dwc2987@msn.com\Desktop\New folder\Ace on Steroids Solution\Ace on Steroids\Ace on Steroids\IntroductionGameState.cs:line 22
       at Indiefreaks.AceOnSteroids.AceOnSteroidsApplication.Initialize() in C:\Users\dwc2987@msn.com\Desktop\New folder\Ace on Steroids Solution\Ace on Steroids\Ace on Steroids\AceOnSteroidsApplication.cs:line 83
       at Microsoft.Xna.Framework.Game.RunGame(Boolean useBlockingRun)
       at Microsoft.Xna.Framework.Game.Run()
       at Indiefreaks.AceOnSteroids.Boot.Main() in C:\Users\dwc2987@msn.com\Desktop\New folder\Ace on Steroids Solution\Ace on Steroids\Ace on Steroids\AceOnSteroidsApplication.cs:line 22
  InnerException:

Feb 13, 2012 at 4:31 AM

I figured it out as I added [STAThread] to the Boot class though now got to figure out how to get the ship to draw back on the screen.