Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using Movie Class to Divide Picture and Create Animation in XNA, WPXNA (4)

0.00/5 (No votes)
27 May 2013 1  
How to use the Movie class to divide picture and create animation in XNA, WPXNA (4)

The original post can be found here.

Introduction/Catalog

I have developed some games on Windows Phone. Here, I'll share my experiences and gradually upload some classes, no good name, I just call it WPXNA. (Some example code may not be stringent enough.)

  • Movie Sequences
  • Movie
  • Bird2

Movie Sequences

A complete sequence of actions is referred to as a movie sequence, such as: running action. Therefore, I defined the MovieSequence class that contains the information of an action. Here are the fields contained in the MovieSequence:

private readonly List<Point> frames = new List<Point> ( );
internal bool IsLooped;
private readonly bool isLoop;
private int currentFrameIndex;
internal Point CurrentFrame;
internal readonly string Name;
internal readonly int FrameCount;
internal readonly int Rate;

The field frames represent the location of the frames which will make up the whole action, and all of these frames in one picture, we take a look at the picture in example.

We use the coordinate (x, y) to represent the position of the frame, then (1, 1) is the yellow bird, (4, 1) is the black bird. These coordinates are grouped together into a finished action.

Field isLoop, whether you can play again when the action is finished.

Field currentFrameIndex represents the current frame index, starting with 1. CurrentFrame indicating the position of the current frame. FrameCount represents how many frames in the sequence. Rate field represents the playback speed, smaller the value of the Rate, higher the speed.

Next method will bring the sequence into the next frame, Reset method resets the sequence information.

internal static void Reset ( MovieSequence sequence )
{
 sequence.IsLooped = false;
 sequence.currentFrameIndex = 1;

 sequence.CurrentFrame = sequence.frames[sequence.currentFrameIndex - 1];
}

internal static bool Next ( MovieSequence sequence )
{
 bool isEnded = sequence.currentFrameIndex == sequence.frames.Count;

 if ( isEnded )
 {
  sequence.IsLooped = true;

  if ( !sequence.isLoop )
   return isEnded;

  sequence.currentFrameIndex = 1;
 }
 else
  sequence.currentFrameIndex++;

 sequence.CurrentFrame = sequence.frames[sequence.currentFrameIndex - 1];
 return isEnded;
}

Next, whether to play again according to the isLoop field. If isLoop is false, you will stay on the last frame.

Movie

A Movie contains multiple MovieSequence, here are some fields and event of Movie class.

internal event EventHandler<MovieEventArgs> Ended;

internal readonly int Width;
internal readonly int Height;

private readonly float renderWidth;
private readonly float renderHeight;

private readonly int rate;
private int currentRate;
private int currentFrameCount;
private MovieSequence currentSequence;
internal string CurrentSequenceName;
private float rotation = 0;
internal int Rotation
{
 set
 {
  this.rotation = Calculator.Radian ( value );
 }
}
private Vector2 rotationLocation;
private Rectangle frameRectangle;

Field Width, Height represents the size of the movie, and renderWidth, renderHeight represents the size when the movie is drawn, that is, determine how to split an image, by default they are the same.

Although each MovieSequence contains a Rate field, but the Movie is also defined a default rate, when the MovieSequence didn't give a Rate, default rate field will be used, currentRate, currentSequence, CurrentSequenceName represents the information of the current MovieSequence.

Property Rotation is the information of rotation, it can be set to a certain angle, but will eventually be converted to a radian.

Field rotationLocation is the location to rotate the movie. Field frameRectangle represents a rectangle, which used to draw a certain part of the whole picture.

The Ended event will be triggered when a sequence is completed.

Use Play method to play the movie, use NextFrame method to enter the next frame, use Draw method to draw the movie.

internal static void Play ( Movie movie, string sequenceName, bool isRecord, bool isReplay )
{

 if ( !isReplay && movie.CurrentSequenceName == sequenceName )
  return;

 movie.CurrentSequenceName = sequenceName;
 if ( isRecord )
  movie.sequenceNames.Add ( sequenceName );

 if ( string.IsNullOrEmpty ( sequenceName ) || !movie.sequences.ContainsKey ( sequenceName ) )
 {
  movie.isVisible = false;
  return;
 }

 movie.isVisible = true;
 movie.currentSequence = movie.sequences[sequenceName];
 movie.currentRate = movie.currentSequence.Rate == 0 ? movie.rate : movie.currentSequence.Rate;
 movie.currentFrameCount = movie.currentRate;
 MovieSequence.Reset ( movie.currentSequence );

 movie.frameRectangle = new Rectangle ( ( int ) 
 ( ( movie.currentSequence.CurrentFrame.X - 1 ) * movie.renderWidth ), 
 ( int ) ( ( movie.currentSequence.CurrentFrame.Y - 1 ) * movie.renderHeight ), 
 ( int ) movie.renderWidth, ( int ) movie.renderHeight );

}

internal static void NextFrame ( Movie movie, bool isForce )
{

 if ( !movie.isVisible || ( !isForce && --movie.currentFrameCount > 0 ) )
  return;

 if ( movie.currentSequence.FrameCount <= 1 )
 {

  if ( null != movie.Ended )
   movie.Ended ( movie, new MovieEventArgs ( movie ) );

  return;
 }

 movie.currentFrameCount = movie.currentRate;

 if ( MovieSequence.Next ( movie.currentSequence ) )
  if ( null != movie.Ended )
   movie.Ended ( movie, new MovieEventArgs ( movie ) );

 movie.frameRectangle = new Rectangle ( ( int ) 
 ( ( movie.currentSequence.CurrentFrame.X - 1 ) * movie.renderWidth ), 
 ( int ) ( ( movie.currentSequence.CurrentFrame.Y - 1 ) * movie.renderHeight ), 
 ( int ) ( movie.renderWidth ), ( int ) ( movie.renderHeight ) );
}

internal static void Draw ( Movie movie, GameTime time, SpriteBatch batch )
{

 if ( !movie.isVisible )
  return;

 if ( movie.rotation == 0 )
  batch.Draw ( movie.Texture, movie.location * World.Scale, 
  movie.frameRectangle, Color.White, 0, Vector2.Zero, 
  World.TextureScale, SpriteEffects.None, movie.order );
 else
  batch.Draw ( movie.Texture, ( movie.location + movie.rotationLocation ) * 
  World.Scale, movie.frameRectangle, Color.White, movie.rotation, 
  movie.rotationLocation * World.Scale, World.TextureScale, 
  SpriteEffects.None, movie.order );

}

Bird2

Finally, let us use the Movie class.

private readonly ResourceManager resourceManager;
private readonly Movie bird2;
private int bird2Angle = 0;

Field bird2Angle said that bird's angle.

public World ( Color backgroundColor )
 : base ( )
{
 // ...

 this.resourceManager = new ResourceManager ( new Resource[] {
  new Resource ( "bird2.image", ResourceType.Image, @"image\bird2" )
 } );
 this.resourceManager.World = this;

 this.bird2 = new Movie ( "bird2.m", 
 "bird2.image", new Vector2 ( 200, 200 ), 80, 80, 3, 0, "live",
  new MovieSequence ( "live", true, new Point ( 1, 1 ), new Point ( 2, 1 ) ),
  new MovieSequence ( "dead", 30, false, new Point ( 3, 1 ), new Point ( 4, 1 ) )
  );
 this.bird2.Ended += this.bird2MovieEnded;
}

private void bird2MovieEnded ( object sender, MovieEventArgs e )
{
 Debug.WriteLine ( "bird2MovieEnded: e.SequenceName=" + e.SequenceName );
}

protected override void OnNavigatedFrom ( NavigationEventArgs e )
{
 this.bird2.Ended -= this.bird2MovieEnded;

 base.OnNavigatedFrom ( e );
}

In the constructor, the resources defined by the ResourceManager, and then we created the bird's movie. The Bird has two animated, one is live, and the other is dead, and the live by default. We have defined the bird's Ended event, we output name of the sequence in the method bird2MovieEnded.

protected override void OnNavigatedTo ( NavigationEventArgs e )
{
 // ...
 
 this.resourceManager.LoadContent ( );
 this.bird2.InitResource ( this.resourceManager );
 
 base.OnNavigatedTo ( e );
}

In the OnNavigatedTo method, we get the pictures resources.

private void OnUpdate ( object sender, GameTimerEventArgs e )
{
 Movie.NextFrame ( this.bird2 );

 this.bird2.Rotation = this.bird2Angle++;

 if ( e.TotalTime.TotalSeconds > 5 && 
 this.bird2.CurrentSequenceName == "live" )
  Movie.Play ( this.bird2, "dead" );
  
}

private void OnDraw ( object sender, GameTimerEventArgs e )
{
 this.GraphicsDevice.Clear ( this.BackgroundColor );

 this.spiritBatch.Begin ( );
 Movie.Draw ( this.bird2, new GameTime 
 ( e.TotalTime, e.ElapsedTime ), this.spiritBatch );
 this.spiritBatch.End ( );
}

In the OnUpdate method, we need to use the NextFrame method to continuously update the frame information, adjust the bird's angle. And after 5 seconds, we play the bird's death animation.

In the OnDraw method, we draw the bird.

Get the code from here, for more contents, please visit WPXNA.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here