Use the Movie class image segmentation in XNA and frame by frame animation, WPXN

Recommended for you: Get network issues from WhatsUp Gold. Not end users.

Square has developed some Windows Phone some of the game, not what technology. Share your experiences here, only in order to exchange experiences and friends. Square will gradually will write the class uploaded to the hosting project, no good name, called WPXNA, finally, please master detour, so as not to waste time. (in order to focus and reduce space, some sample code may not strict enough. )

The film series

Square will be a complete movement known as a film series, such as: game player running action. Therefore, square defines the MovieSequence class, he includes a motion information required. The following is the field contained in 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;

Field frames frames, these frames will be composed of the action, and all of these frames in the same picture, we look at the example in the picture.

We use the (x, y) so that the coordinates of the frame position, then (1, 1) represents the yellow bird, and (4, 1) said black birds. These coordinates are combined together to become a completed action.

Field isLoop indicates whether you can loop. Field IsLooped said whether the sequence has a cycle.

CurrentFrameIndex said the current frame field index, from the beginning of 1. CurrentFrame said the current frame position. FrameCount in the series contains many frames. While the Rate field represents the playback speed, Rate value is smaller, the faster the speed of playback. Name said the name of the sequence field.

The Next method will make the sequence into the next frame, the Reset method will reset 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;
}

In the Next method, we according to the isLoop field to determine whether after playing all the frames, loop. If not, it will stay in the last frame.

Film

We will be composed of a plurality of MovieSequence a Movie, here are some fields and events of the 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;

The field Width, Height said the film size, while renderWidth, renderHeight drawing film size, also is to determine how to segment the image, by default they are the same.

Although each MovieSequence contains a Rate field of its own, but Movie also defines a default rate, when MovieSequence is not given to Rate, the rate field is used by default. While currentRate, currentSequence, CurrentSequenceName field represents the information about the current MovieSequence.

The attribute Rotation said rotary information of Movie, can be set to a certain angle, but in the end will be converted to radians.

Field rotationLocation said in what position rotating film, if you set the Movie angle. Field frameRectangle represents a rectangular, one part is to draw the whole picture of the.

Ended will trigger events in a film series finished playing time.

Methods Play was used to broadcast the film, NextFrame film to let into the next frame, use Draw to draw the film.

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 );

}

The bird

Finally, we use the Movie class.

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

The bird2Angle said the bird Perspective.

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, we define the resources needed by ResourceManager, and then create the bird movie. The bird has two animation, one is live, which is alive when animation, another is dead, is a dying animation. The default live player. We define the Ended event, in the method of bird2MovieEnded, the name we will output sequence is finished playing.

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

In the OnNavigatedTo method, we obtain the required image 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 update the frame using the NextFrame method to keep the information, and modify the bird perspective. In 5 seconds, we play the bird death animations.

In the OnDraw method, we draw a bird.

This video
Project address

More about WPXNA
Square the development of the game
QQ group 213685539

Welcome to visit the same article I published in other locations

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Posted by Amy at November 23, 2013 - 2:03 PM