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.