Playing with sprites

In the last blog entries about IGF v0.4, I used the new Pong sample to demonstrate how to initialize and use Logic & Networking abstraction features but this sample also serves another purpose: showing how to use the new Sprite system that will allow IGF and SunBurn developers to create sprite base games that take all the potential of this great engine.

So far with SunBurn, you could use the SpriteContainer and SpriteManager classes to create and draw sprites to the screen with the built in shaders so you could achieve games that would look like this:

This is great since you can get normal, specular and even parallax maps as well as shadows applied to your sprites so that your game get some more depth. However, it lacks a full integration with the rest of the API (John K. from SynapseGaming stated they were primarily focusing on 3d features for 2.0 and would get back to 2d features later).
For instance, since you would use the SpriteContainer class a bit like you would use SpriteBatch in Xna, you can’t really use SunBurn CollisionManager even if SpriteContainer inherits from SceneObject.

The Indiefreaks Game Framework v0.4 will introduce a new Sprite system that will allow you to get both advantages through a set of new classes:

SpriteObjectManager

The SpriteObjectManager is meant to replace the SpriteManager which it inherits from and adds a new method for you to create SpriteObject instances we’ll look at a bit later in this blog entry.

To use the new Sprite system, as usual, you’ll just have to create and add an instance of the SpriteObjectManager to your SceneInterface through the AddManager() method; ideally in the IGF Application.InitializeSunBurn() overridable method.

/// <summary>
/// Initialize SunBurn the way you want
/// </summary>
/// /// <remarks>
///   This method is automatically called the first time you want to acquire the SceneInterface.
///
///   The minimum Managers we add are:
///   - ResourceManager
///   - CameraManager
///   - ObjectManager
///   - LightManager
///
///   We do not setup the SceneInterface for rendering: you need to call CreateRenderer in this method override.
/// </remarks>
/// <example>
///   protected override void CreateSceneInterface()
///   {
///     CreateRenderer(Renderers.Deferred); // or you could instead use Renderers.Forward depending on your game's lighting requirements
///
///     SunBurn.AddManager(new PostProcessManager(SunBurn.GraphicsDeviceManager);                   // Adds the post process manager
///     SunBurn.AddManager(new CollisionManager(GraphicsDeviceManager, plugin.SceneInterface));     // Adds the collision manager
///     SunBurn.AddManager(new LightingSystemEditor(GraphicsDeviceManager, plugin.SceneInterface)); // Adds the SunBurn editor manager
///   }
/// </example>
protected override void InitializeSunBurn()
{
    SunBurn.AddManager(new CollisionManager(GraphicsDeviceManager, SunBurn));
    SunBurn.AddManager(new SpriteObjectManager(GraphicsDeviceManager));
    SunBurn.AddManager(new LiveSessionManager(this));

    CreateRenderer(Renderers.Forward);
}

In the Pong sample, since we want to use SunBurn’s CollisionManager and the new Logic abstraction and Xbox Live networking features, we also add their corresponding managers too.

SpriteObject

But you still may wonder how to create sprites and use them as any other SceneObject in the game. That is slightly more complicated than the usual because you need to think how you’ll structure your 2d game first.

What you want to achieve is create a SpriteObject instance but before you do so, you must look at your game and think when and how the sprites will be rendered.

First, you’ll want to divide your sprites between static and dynamic sprites.

  • Static sprites are sprites that won’t move at all once positioned in your game such as level backgrounds or the ground for a 2d platformer.
  • Dynamic sprites are all sprites that will either move using player input, AI code or reacting to collision. You’ll also consider dynamic sprites that will get animated using a sprite sheet.

Then, you’ll want to divide your sprites depending on their visibility. If you have several sprites that will be rendered in the screen wherever the camera will be, you may want to consider them inside a group. You’ll also want to group other sprites that will get visible at certain times such as a background volcano.

For each group you’ll define for your game, you’ll want to create a SpriteContainer as you would do with the standard SpriteManager. In my Pong sample, I divided my game between 3 groups or SpriteContainers:

  • One for the ball that will be dynamic because moving in the screen
  • One for the top and bottom walls that will be static and always visible only serving the game as colliders for the ball
  • And finally another one for the two pads players will control
            var spriteManager = (SpriteObjectManager)SunBurn.GetManager<SpriteManager>(true);

            var batsSpriteContainer = spriteManager.CreateSpriteContainer();
            batsSpriteContainer.Visibility = ObjectVisibility.Rendered;
            batsSpriteContainer.Mass = 0;
            var ballSpriteContainer = spriteManager.CreateSpriteContainer();
            ballSpriteContainer.Visibility = ObjectVisibility.Rendered;
            ballSpriteContainer.Mass = 0;
            var wallSpriteContainer = spriteManager.CreateSpriteContainer();
            wallSpriteContainer.Visibility = ObjectVisibility.Rendered;
            wallSpriteContainer.Mass = 0;

Once you made these decisions, all you’ll have to do is create one SpriteObject per sprite you want to control using the SpriteObjectManager.CreateSpriteObject() method with the following signature:

/// <summary>
/// Creates a new SpriteObject instance
/// </summary>
/// <typeparam name="T">Used for custom SpriteObject inherited classes</typeparam>
/// <param name="name">The name of the SpriteObject</param>
/// <param name="materialPath">The path to the material you want to load with the SpriteObject</param>
/// <param name="spriteContainer">The SpriteContainer to associate the SpriteObject with</param>
/// <returns>returns a new SpriteObject instance</returns>
public T CreateSpriteObject<T>(string name, string materialPath, SpriteContainer spriteContainer)
    where T : SpriteObject, new()

The method is generic so that you can create either a SpriteObject class or any inherited class instance easily.

 

_leftBat = spriteManager.CreateSpriteObject<Bat>("Left Bat", "Sprites/Bat", batsSpriteContainer);
_rightBat = spriteManager.CreateSpriteObject<Bat>("Right Bat", "Sprites/Bat", batsSpriteContainer);

_ball = spriteManager.CreateSpriteObject<Ball>("Ball", "Sprites/Ball", ballSpriteContainer);

_topWall = spriteManager.CreateSpriteObject<Wall>("Top Wall", "Sprites/Ball", wallSpriteContainer);
_bottomWall = spriteManager.CreateSpriteObject<Wall>("Bottom Wall", "Sprites/Ball", wallSpriteContainer);

Finally, you’ll want to submit your SpriteObject instances to the ObjectManager as you’d do with a SceneObject or SceneEntity instance so that it gets updated.

Each SpriteObject can then be managed using its properties that are specialized for 2d operations:

  • Vector2 Position
  • Vector2 Size
  • float Rotation
  • Effect Material

And the SpriteObject instance will obviously inherit from all the usual SunBurn features that are attached to any SceneObject (Lighting & Shadow, Collisions, etc…)

Leave a Reply