Tuesday, September 17, 2013

Cross-platform puzzle game animation architecture

I'm playing around with a puzzle game. The goal is simple. I want to see how much code I can put in a cross platform portable library to be shared. I also should say that I'm using Xamarins stuff and MonoGame.

Short story

Create a core-library that uses coordinates that you later on resolve to actual pixels on the device. Decouple from screen resolutions early on and handle animations in the shared library. The platform-specific code should just act as a view.

Longer story

I started out creating what I usually call the Core-library. In this library all the shared stuff is pushed into. The game (not important) is a simple three-in-a-row-with-a-twist kinda game. That means that it has a grid defined in the core library. On top of that we have a Controller that handles your selections by evaluating them and finally telling you if its a valid selection.

All of the above was simple enough. The problems started when I was going to create animations. For example, when combining elements, I want them to animate nicely into a common point. How do I pull that of without having to write a lot of platform specific code?

My approuch to solve that was to create a MoveReport that has MoveItems in it. It simply tells the UI how stuff in the UI is supposed to move. It doesn't tell you exact pixel positions, rather grid positions and an interpolation value.

The MoveReport is a class with two properties

    IsSuccessful : bool 
    Items : List<MoveItems> 

The MoveItem

   From : Position
   To : Position
   FrameNumber : int
   ElementIdentifer : string

The To and From Position is integer-based grid positions. The FrameNumber describes in what frame the animation will be active and the ElementIdentifer describes which element (game specific stuff) to draw.

To govern the animation I created an AnimationController that keeps track of the current animation frame. The blocks ask this controller of the current state of the block and draws whatever the animation controller hands over. The only thing the UI code need to do is to translate the interpolation value into real pixel coordinates.

The UI needs to tell the grid what we selected. If the selection is valid, start an animation.

private void HandleSelection()
{
    // _currentSelection is a List of grid positions {0,2}{0,3} and so on
    var report = _grid.Select( _currentSelection );
    if(report.IsSuccessful)
    {
        // Start the animation and let each frame be 100 milliseconds long. That means that
        // moving one item between to cells will take 100 milliseconds.
       _animationController.Start(report, 100); 
    }
}

On Update the controller is poked

private void Update(GameTime gameTime)
{
    if(_animationController.IsRunning)
    {
        _animationController.Update(gameTime.ElapsedTime.Milliseconds);
    }
}

The UI then draws each block in turn

public void Draw(SpriteBatch batch)
{
     if(_animationController.IsRunning)
     {
          var desc = _animationController.GetAnimationDescription( this.Position );

          if(desc!=null)
          {
              var pixelPosition = ResolvePosition( desc );
              var texture = TextureHelper.ResolveTexture( desc.ElementIdentifier );
              batch.Draw(texture, pixelPosition, Color.White);
              return;
          }
     }
     
     // Draw regular stuff - simplyfied
     batch.Draw(this.Element.Texture, ResolvePosition(), Color.White);
}

The GetAnimationDescription takes a grid position, for example {0,3}, and returns a description of what to draw.

public class AnimationDescription
{
     public Position From;
     public Position To;
     public float LerpValue;
     public string ElementIdentifier;
}

The AnimationController is updated each frame with the number of milliseconds passed since last update and advances an internal framecount to be able to feed the blocks with the description-objects. When the last frame is passed it sets the IsRunning to false to return to basic drawing.

Well, this is just a basic outline. Kinda messy. I'll clean it up if anyone's interested in details :)


No comments:

Post a Comment