Difference between revisions of "Animation"

From TRCCompSci - AQA Computer Science
Jump to: navigation, search
(How to Use)
 
(26 intermediate revisions by the same user not shown)
Line 1: Line 1:
==Creating Animation Class==
+
This will show you how to animate a character. The idea is to use a texture which is actually a strip of frames, then knowing the width and height of each frame should allow you to only display the part of the texture which represents the current frame.
You need to create a new class, so from the main menu click File, and select New File. Name the class Animation.You will have the following code:
 
  
<syntaxhighlight lang=csharp>
+
=Variables Required=
using System;
+
Firstly, change the name of the Texture2D to something more appropriate eg PlayerAnimation:
namespace TestGame
 
{
 
public class Animation
 
{
 
public Animation()
 
{
 
}
 
}
 
}
 
</syntaxhighlight>
 
 
 
You will need to add the following to the using section:
 
  
 
<syntaxhighlight lang=csharp>
 
<syntaxhighlight lang=csharp>
using Microsoft.Xna.Framework;
+
public Texture2D PlayerAnimation;
using Microsoft.Xna.Framework.Content;
 
using Microsoft.Xna.Framework.Graphics;
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
This will allow the class to access the MonoGame framework and SDK. You need to add the following variables inside the class. Try to add these straight after the { after the public class Animation line:
+
We now need to add the following variables to the player to control the frame displayed:
  
 
<syntaxhighlight lang=csharp>
 
<syntaxhighlight lang=csharp>
// The image representing the collection of images used for animation
+
// Get the width of the player ship
Texture2D spriteStrip;
 
 
 
 
// The time since we last updated the frame
 
// The time since we last updated the frame
 
int elapsedTime;
 
int elapsedTime;
Line 52: Line 35:
 
// Height of a given frame
 
// Height of a given frame
 
public int FrameHeight;
 
public int FrameHeight;
 +
</syntaxhighlight>
 +
 +
==Change the Initialize method==
 +
<syntaxhighlight lang=csharp>
 +
public void Initialize(Texture2D animation, Vector2 position)
 +
{
 +
FrameWidth = 115;
 +
FrameHeight = 69;
 +
frameCount = 8;
  
// The state of the Animation
+
// Set the time to zero
public bool Active;
+
elapsedTime = 0;
 +
currentFrame = 0;
 +
}
 +
</syntaxhighlight>
  
// Determines if the animation will keep playing or deactivate after one run
+
The Initialize method needs to configure the frame information. The sprite strip will have 8 frames, each one 115 pixels by 69 pixels. The elapsed time is to time the change from frame to frame, the current frame will record the frame to be displayed. The other main change is the PlayerAnimation, updating the variable names to match the changes in the previous section.
public bool Looping;
 
  
// The position of the animation
+
==Load Content Method==
public Vector2 Position;
+
We need to load a texture in the LoadContent method, this should be a spritesheet with the dimensions defined in Intialize (ie each frame is 115 pixel wide and 69 pixels high, and it is a strip of 8 frames).
 +
<syntaxhighlight lang=csharp>
 +
PlayerAnimation = Content.Load<Texture2D>("Graphics\\player");
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Now add the following method into your class, this is the Initialize method for the animation:
+
==The Draw Method==
 +
You will need to alter the Draw method, this will draw the PlayerAnimation at the location specified in the destinationRect. But it will only draw the section of the playerAnimation specified in the sourceRect:
  
 
<syntaxhighlight lang=csharp>
 
<syntaxhighlight lang=csharp>
public void Initialize(Texture2D texture, Vector2 position, int frameWidth, int frameHeight, int frameCount, Color color, bool looping)
+
public void Draw(SpriteBatch spriteBatch)
 
{
 
{
// Keep a local copy of the values passed in
+
spriteBatch.Draw(PlayerAnimation, destinationRect, sourceRect, color);
this.color = color;
+
}
this.FrameWidth = frameWidth;
+
</syntaxhighlight>
this.FrameHeight = frameHeight;
 
this.frameCount = frameCount;
 
  
Looping = looping;
+
==The Update Method - AKA the business==
Position = position;
 
spriteStrip = texture;
 
  
// Set the time to zero
+
Firstly make sure the Update method has the following passed into the method:
elapsedTime = 0;
 
currentFrame = 0;
 
  
// Set the Animation to active by default
+
<syntaxhighlight lang=csharp>
Active = true;
+
public void Update(GameTime gameTime)
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The update method involves using currentFrame to identify which frame to display, simple mathematics will allow us to work out which part of the sprite strip needs to be displayed. Every 100 milliseconds the currentFrame will be incremented. When the framecount equals the currentFrame the end of the strip has been reached. The code will then set the currentFrame to 0 to return to the first frame. The code will allow you to not loop an animation and if this is the case the code sets active to false:
+
Now we need to time the duration between switching the frame. Firstly we need to set the time elapsed since the last change of frame. If the elapsed time is greater than 100 milliseconds then increment the current frame by 1. FrameCount is used to check if we need to reset the current frame to zero. Finally we reset the elapsed time in order to time the next change of frame.
  
 
<syntaxhighlight lang=csharp>
 
<syntaxhighlight lang=csharp>
public void Update(GameTime gameTime)
+
elapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;
 +
 
 +
// If the elapsed time is larger than the frame time
 +
// we need to switch frames
 +
if (elapsedTime > 100)
 
{
 
{
    // Do not update the game if we are not active
+
// Move to the next frame
    if (Active == false) return;
+
currentFrame++;
  
    // Update the elapsed time
+
// If the currentFrame is equal to frameCount reset currentFrame to zero
    elapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;
+
if (currentFrame == frameCount)
 +
{
 +
currentFrame = 0;
 +
}
  
    // If the elapsed time is larger than the frame time we need to switch frames
+
// Reset the elapsed time to zero
    if (elapsedTime > frameTime)
+
elapsedTime = 0;
    {
+
}
        // Move to the next frame
 
        currentFrame++;
 
  
        // If the currentFrame is equal to frameCount reset currentFrame to zero
+
// Grab the correct frame in the image strip by multiplying the currentFrame index by the Frame width
        if (currentFrame == frameCount)
 
        {
 
            currentFrame = 0;
 
            // If we are not looping deactivate the animation
 
            if (Looping == false)
 
                Active = false;
 
        }
 
  
        // Reset the elapsed time to zero
+
sourceRect = new Rectangle(currentFrame * FrameWidth, 0, FrameWidth, FrameHeight);
        elapsedTime = 0;
 
    }
 
  
    // Grab the correct frame in the image strip by multiplying the currentFrame index by the Frame width
+
// Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
    sourceRect = new Rectangle(currentFrame * FrameWidth, 0, FrameWidth, FrameHeight);
+
 
 +
destinationRect = new Rectangle((int)Position.X - (int)(FrameWidth) / 2,
 +
    (int)Position.Y - (int)(FrameHeight) / 2, (int)(FrameWidth * scale), (int)(FrameHeight * scale));
  
    // Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
 
    destinationRect = new Rectangle(
 
        (int)Position.X – (int)(FrameWidth * scale) / 2,
 
        (int)Position.Y – (int)(FrameHeight * scale) / 2,
 
        (int)(FrameWidth * scale),
 
        (int)(FrameHeight * scale)
 
        );
 
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Finally add the following draw method:
+
The sourceRect should be the portion of the spritesheet for this frame, and the destinationRect should be where it is drawn on the screen.
 +
 
 +
=Adding the Animation Into Content Folder=
 +
 
 +
Get a example spritesheet from this [https://drive.google.com/file/d/0Bw-0YEA_JX9gTXRGN2hIcFdrZVU/view?usp=sharing zip file]. It also contains the previous non animated player graphics. Extract it in the same way as before and add shipAnimation.xnb file into monogame.
 +
 
 +
Once added, you need to right click on shipAnimation.xnb and chose properties. You should then change Build Action to Content, and the Copy to Output Directory to Copy Always:
 +
 
 +
[[File:Player xnb properties 2.png]]
 +
 
 +
you could alternatively add the png version to the content pipeline and rebuild the pipeline.
 +
 
 +
=How to Use=
 +
This would be best added to some sort of animation class, or within the class for a player or character. If you create a class for your animation you will be able to get the current frame:
 +
 
 +
'''This section will be improved'''
  
<syntaxhighlight lang=csharp>
+
<syntaxhighlight lang=c#>
public void Draw(SpriteBatch spriteBatch)
+
public Texture2D GetCurrentFrame()
 
{
 
{
// Only draw the animation when we are active
+
            Color[] FrameTextureData = new Color[PlayerAnimation.Width * PlayerAnimation.Height];
if (Active)
+
 
{
+
            PlayerAnimation.GetData(FrameTextureData);
spriteBatch.Draw(spriteStrip, destinationRect, sourceRect, color);
+
 
}
+
            Color[] test = new Color[FrameHeight * FrameWidth];
 +
 
 +
            int count = 0;
 +
            for (int c = sourceRect.Top; c < sourceRect.Bottom; c++)
 +
            {
 +
                for (int r = sourceRect.Left; r < sourceRect.Right; r++)
 +
                {
 +
                    Color colorA = FrameTextureData[r + (c * PlayerAnimation.Width)];
 +
                    test[count] = colorA;
 +
                    count++;
 +
                }
 +
            }
 +
            Texture2D frame = Content.Load<Texture2D>("frametext");
 +
            frame.SetData(test);
 +
            map.ObjectGroups["Objects"].Objects["Player"].Texture = frame;
 +
 
 +
            currentPlayerPosition = new Vector2(map.ObjectGroups["Objects"].Objects["Player"].X, map.ObjectGroups["Objects"].Objects["Player"].Y);
 +
 
 +
            playerPosition = new Vector2(map.ObjectGroups["Objects"].Objects["Player"].X - (graphics.PreferredBackBufferWidth / 2),
 +
                map.ObjectGroups["Objects"].Objects["Player"].Y - (graphics.PreferredBackBufferHeight / 2));
 +
 
 +
            return frame;
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>

Latest revision as of 08:57, 14 June 2018

This will show you how to animate a character. The idea is to use a texture which is actually a strip of frames, then knowing the width and height of each frame should allow you to only display the part of the texture which represents the current frame.

Variables Required

Firstly, change the name of the Texture2D to something more appropriate eg PlayerAnimation:

public Texture2D PlayerAnimation;

We now need to add the following variables to the player to control the frame displayed:

// Get the width of the player ship
// The time since we last updated the frame
int elapsedTime;

// The number of frames that the animation contains
int frameCount;

// The index of the current frame we are displaying
int currentFrame;

// The color of the frame we will be displaying
Color color;

// The area of the image strip we want to display
Rectangle sourceRect = new Rectangle();

// The area where we want to display the image strip in the game
Rectangle destinationRect = new Rectangle();

// Width of a given frame
public int FrameWidth;

// Height of a given frame
public int FrameHeight;

Change the Initialize method

public void Initialize(Texture2D animation, Vector2 position)
{
	FrameWidth = 115;
	FrameHeight = 69;
	frameCount = 8;

	// Set the time to zero
	elapsedTime = 0;
	currentFrame = 0;
}

The Initialize method needs to configure the frame information. The sprite strip will have 8 frames, each one 115 pixels by 69 pixels. The elapsed time is to time the change from frame to frame, the current frame will record the frame to be displayed. The other main change is the PlayerAnimation, updating the variable names to match the changes in the previous section.

Load Content Method

We need to load a texture in the LoadContent method, this should be a spritesheet with the dimensions defined in Intialize (ie each frame is 115 pixel wide and 69 pixels high, and it is a strip of 8 frames).

PlayerAnimation = Content.Load<Texture2D>("Graphics\\player");

The Draw Method

You will need to alter the Draw method, this will draw the PlayerAnimation at the location specified in the destinationRect. But it will only draw the section of the playerAnimation specified in the sourceRect:

public void Draw(SpriteBatch spriteBatch)
{
	spriteBatch.Draw(PlayerAnimation, destinationRect, sourceRect, color);
}

The Update Method - AKA the business

Firstly make sure the Update method has the following passed into the method:

public void Update(GameTime gameTime)

Now we need to time the duration between switching the frame. Firstly we need to set the time elapsed since the last change of frame. If the elapsed time is greater than 100 milliseconds then increment the current frame by 1. FrameCount is used to check if we need to reset the current frame to zero. Finally we reset the elapsed time in order to time the next change of frame.

elapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;

// If the elapsed time is larger than the frame time
// we need to switch frames
if (elapsedTime > 100)
{
	// Move to the next frame
	currentFrame++;

	// If the currentFrame is equal to frameCount reset currentFrame to zero
	if (currentFrame == frameCount)
	{
		currentFrame = 0;
	}

	// Reset the elapsed time to zero
	elapsedTime = 0;
}

// Grab the correct frame in the image strip by multiplying the currentFrame index by the Frame width

sourceRect = new Rectangle(currentFrame * FrameWidth, 0, FrameWidth, FrameHeight);  

// Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width

destinationRect = new Rectangle((int)Position.X - (int)(FrameWidth) / 2,
    (int)Position.Y - (int)(FrameHeight) / 2, (int)(FrameWidth * scale), (int)(FrameHeight * scale));

The sourceRect should be the portion of the spritesheet for this frame, and the destinationRect should be where it is drawn on the screen.

Adding the Animation Into Content Folder

Get a example spritesheet from this zip file. It also contains the previous non animated player graphics. Extract it in the same way as before and add shipAnimation.xnb file into monogame.

Once added, you need to right click on shipAnimation.xnb and chose properties. You should then change Build Action to Content, and the Copy to Output Directory to Copy Always:

Player xnb properties 2.png

you could alternatively add the png version to the content pipeline and rebuild the pipeline.

How to Use

This would be best added to some sort of animation class, or within the class for a player or character. If you create a class for your animation you will be able to get the current frame:

This section will be improved

public Texture2D GetCurrentFrame()
{
            Color[] FrameTextureData = new Color[PlayerAnimation.Width * PlayerAnimation.Height];

            PlayerAnimation.GetData(FrameTextureData);

            Color[] test = new Color[FrameHeight * FrameWidth];

            int count = 0;
            for (int c = sourceRect.Top; c < sourceRect.Bottom; c++)
            {
                for (int r = sourceRect.Left; r < sourceRect.Right; r++)
                {
                    Color colorA = FrameTextureData[r + (c * PlayerAnimation.Width)];
                    test[count] = colorA;
                    count++;
                }
            }
            Texture2D frame = Content.Load<Texture2D>("frametext");
            frame.SetData(test);
            map.ObjectGroups["Objects"].Objects["Player"].Texture = frame;

            currentPlayerPosition = new Vector2(map.ObjectGroups["Objects"].Objects["Player"].X, map.ObjectGroups["Objects"].Objects["Player"].Y);

            playerPosition = new Vector2(map.ObjectGroups["Objects"].Objects["Player"].X - (graphics.PreferredBackBufferWidth / 2),
                map.ObjectGroups["Objects"].Objects["Player"].Y - (graphics.PreferredBackBufferHeight / 2));

            return frame;
}