animation (動畫) 靜宜大學資工系 蔡奇偉 副教授 © 2011. 大綱 introduction game...
TRANSCRIPT
Animation (動畫)靜宜大學資工系蔡奇偉 副教授© 2011
大綱• Introduction• Game Loop• Motion Animation• Game Components• 範例• 參考資料
簡介
• 動畫是由隨時間而改變的圖片所產生
• 2D 遊戲動畫可由下列三種技巧產生:Motion Animation
Sprite Animation
Motion + Sprite Animation
XNA Game Loop
Initialize() LoadContent ()
Game quit?
Draw ()
UnLoadContent ()
Y
Update ()
N
Exit
Ga
me
Lo
op
Fixed-Step Game Loops
A fixed-step Game tries to call its Update method on the fixed interval
specified in TargetElapsedTime. Setting Game.IsFixedTimeStep to
true causes a Game to use a fixed-step game loop. A new XNA project
uses a fixed-step game loop with a default TargetElapsedTime of
1/60th of a second.
In a fixed-step game loop, Game calls Update once the
TargetElapsedTime has elapsed. After Update is called, if it is not time
to call Update again, Game calls Draw. After Draw is called, if it is not
time to call Update again, Game idles until it is time to call Update.
If Update takes too long to process, Game sets IsRunningSlowly to
true and calls Update again, without calling Draw in between. When
an update runs longer than the TargetElapsedTime, Game responds
by calling Update extra times and dropping the frames associated with
those updates to catch up. This ensures that Update will have been
called the expected number of times when the game loop catches up
from a slowdown. You can check the value of IsRunningSlowly in your
Update if you want to detect dropped frames and shorten your Update
processing to compensate. You can reset the elapsed times by calling
ResetElapsedTime.
Variable-Step Game Loops
A variable-step game calls its Update and Draw methods in a
continuous loop without regard to the TargetElapsedTime. Setting
Game.IsFixedTimeStep to false causes a Game to use a variable-step
game loop.
Animation and Timing
Using a fixed step allows game logic to use the TargetElapsedTime as
its basic unit of time and assume that Update will be called at that
interval. Using a variable step requires the game logic and animation
code to be based on ElapsedGameTime to ensure smooth gameplay.
Because the Update method is called immediately after the previous
frame is drawn, the time between calls to Update can vary. Without
taking the time between calls into account, the game would seem to
speed up and slow down. The time elapsed between calls to the
Update method is available in the Update method's gameTime
parameter. You can reset the elapsed times by calling
ResetElapsedTime.
When using a variable-step game loop, you should express rates—
such as the distance a sprite moves—in game units per millisecond
(ms). The amount a sprite moves in any given update can then be
calculated as the rate of the sprite times the elapsed time. Using this
approach to calculate the distance the sprite moved ensures that the
sprite will move consistently if the speed of the game or computer
varies.
Motion Animation
• 遊戲角色的位置隨時間而變。
• 玩家控制角色移動
• 自主性移動:遊戲角色按照既定路徑而移動, 不受外界因素的干擾(碰撞除外)。
• 被動性移動:遊戲角色的移動受到其他角色的 牽引,如追逐與逃脫。
• 如何設定移動路徑?
移動路徑 直線路徑
),( dydx
速率 (velocity) = 單位時間的位移向量 (dx, dy)
速度 (speed) = 單位時間的位移長度 = | (dx, dy) |
距離 (distance) = 速度 時間
多線段路徑
• 計算簡單• 足夠密的多線段可以用來模擬曲線路徑
曲線路徑
• explicit formula: 簡單曲線如二次錐線• parametric formula: 複雜曲線或 Bezier curves
• 足夠密的多線段
範例一:顯示棒球圖片
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D baseball;
Vector2 baseball_position;
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw
textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
baseball =
Content.Load<Texture2D>("images/baseball");
baseball_position = new Vector2(0,
(GraphicsDevice.Viewport.Height -
baseball.Height) / 2);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
spriteBatch.Draw(baseball, baseball_position,
Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
範例二:讓棒球水平飛
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D baseball;
Vector2 baseball_position, baseball_velocity;
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw
textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
baseball =
Content.Load<Texture2D>("images/baseball");
baseball_position = new Vector2(0,
(GraphicsDevice.Viewport.Height -
baseball.Height) / 2);
baseball_velocity = new Vector2(5,0);
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back
== ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
// update baseball position
baseball_position += baseball_velocity;
base.Update(gameTime);
}
範例三:不讓棒球飛出界外
W
w
W - w
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D baseball;
Vector2 baseball_position, baseball_velocity;
int maxRight;
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw
textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
baseball =
Content.Load<Texture2D>("images/baseball");
baseball_position = new Vector2(0,
(GraphicsDevice.Viewport.Height -
baseball.Height) / 2);
baseball_velocity = new Vector2(5,0);
maxRight = GraphicsDevice.Viewport.Width –
baseball.Width;
}
protected override void Update(GameTime gameTime){
// Allows the game to exitif (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here// update baseball positionbaseball_position += baseball_velocity;if (baseball_position.X > maxRight)
baseball_position.X = maxRight;base.Update(gameTime);
}
範例四:讓棒球反彈
(a, 0)
(-a, 0)
(a, 0)
(-a, 0)
protected override void Update(GameTime gameTime){
...
// TODO: Add your update logic here// update baseball positionbaseball_position += baseball_velocity;if (baseball_position.X > maxRight){
baseball_position.X = maxRight;baseball_velocity = -baseball_velocity;
}else if (baseball_position.X < 0){
baseball_position.X = 0;baseball_velocity = -baseball_velocity;
}base.Update(gameTime);
}
Game Components
• 模組化元件• GameComponent
• DrawableGameComponents
• Game.Components
• GameServiceContainer
• Game.Services
使用 Game Component 的步驟
1. 利用 IDE 的專案新增項目功能加入 Game Component 骨架碼。
2. 如果元件不牽涉繪圖,則建立一個繼承 GameComponent 的類別,並依需求改寫其中的 Initialize 與 Update 的方法。
3. 如果元件牽涉繪圖,則建立一個繼承 DrawableGameComponent
的類別,並依需求改寫其中的 Initialize 、 Update 、與 Draw 的方法。
4. 在 Game1 的建構函式中,用 Components.Add 方法把上述類別的元件加入 Game1 物件的元件集中。
加入 Game Component 骨架碼步驟一:在專案名稱上按滑鼠右鍵,然後如下圖所示,加入新增項目:
步驟二:選擇如下圖所示的項目。
1
2
3
Game Services
Game services are a mechanism for maintaining loose coupling between objects that need to interact with each other.
Game Services
Provider
Comsumer
AddService
GetService
public void AddService (Type type,Object provider
)
public Object GetService (Type type)
範例五:利用 game components 來加入動畫元件(DrawableComponent.rar)
// File: BallComponent.cs
public class BallComponent :
Microsoft.Xna.Framework.DrawableGameComponent
{
protected Texture2D sprite; // sprite 的圖片protected SpriteBatch spriteBatch; // 用於繪製 sprite
protected Vector2 position; // sprite 的位置protected Vector2 velocity; // sprite 的速率protected string contentPath; // 圖片的路徑
public BallComponent(Game game, string path)
: base(game)
{
// TODO: Construct any child components here
contentPath = path;
}
protected override void LoadContent()
{
spriteBatch =
Game.Services.GetService(typeof(SpriteBatch));
sprite = Game.Content.Load<Texture2D>(contentPath);
base.LoadContent();
}
File: BallComponent.cs
public override void Draw (GameTime gameTime)
{
spriteBatch.Begin();
spriteBatch.Draw(sprite, position, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
File: BallComponent.cs
public class BaseBall : BallComponent
{
int maxRight;
public BaseBall(Game game, string path)
: base(game, path)
{
// TODO: Construct any child components here
}
File: BaseBall.cs
protected override void LoadContent()
{
base.LoadContent();
position = new Vector2(0,
(GraphicsDevice.Viewport.Height -
sprite.Height)/2);
velocity = new Vector2(3, 0);
maxRight = GraphicsDevice.Viewport.Width -
sprite.Width;
}
File: BaseBall.cs
public override void Update(GameTime gameTime){
// TODO: Add your update code hereposition += velocity;if (position.X > maxRight){
position.X = maxRight;velocity.X = -velocity.X;
}else if (position.X < 0){
position.X = 0;velocity.X = -velocity.X;
}
base.Update(gameTime);}
File: BaseBall.cs
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
Components.Add(new BaseBall(this, "images/baseball"));
Components.Add(new BasketBall(this,
"images/basketball"));
}
File: Game1.cs
建立 Baseball 物件與 BasketBall 物件並加入遊戲物件成為其元件
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
File: Game1.cs
執行遊戲元件集( Components )中各元件的 Initialize() 方法(其中也執行該元件的 LoadContent() 方法)。
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch),
spriteBatch);
……
}
File: Game1.cs
把 spriteBatch 加入 Services
protected override void Update(GameTime gameTime){
...
// TODO: Add your update logic here
base.Update(gameTime);}
File: Game1.cs
執行遊戲元件集( Components )中各元件的 Update() 方法。
protected override void Draw(GameTime gameTime){
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
base.Draw(gameTime);}
File: Game1.cs
執行遊戲元件集( Components )中各個可繪元件的 Draw() 方法。
Sprite Animation
• Sprite Sheet• Play List• Frame Rate & Animation Speed
Sprite Sheet
為了減少圖片的數量,我們通常把用來組合動作的 sprites 擺在一張圖片中,稱之為 sprite sheet 。譬如,右圖是一個轉動三環動畫套圖的組合。
Sprite 指定法 – 2D
若把 sprite sheet 中的 sprite 安排如右圖所示的 2D array 結構。 第 (i, j) 的 sprite 所佔據的矩形區域的左上角座標為 (i w, j h)
(0,0) (1,0) (2,0) (3,0) (4,0)0
(0,1) (1,1) (2,1) (3,1) (4,1)1
(0,2) (1,2) (2,2) (3,2) (4,2)2
(0,3) (1,3) (2,3) (3,3) (4,3)3
(0,4) (1,4) (2,4) (3,4) (4,4)4
(0,5) (1,5) (2,5) (3,5) (4,5)5
0 1 2 3 4
h
w
Sprite 指定法 – 1D
若把 sprite sheet 中的 sprite 安排如右圖所示的 2D array 結構。 假定sprite sheet 含有 mn
(行列)個 sprites ,則第 k 個 sprite 所佔據的矩形區域的左上角座標為 (k % m w, k / m h)
0 1 2 3 4
h
w
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24
25 26 27 28 29
整數除法
Play List
Sprite sheet 通常含多個動作組合(如下圖所示)。 Play list
記錄某一動作的所需的 sprite 及其順序。
譬如:出右拳的動作序列為 0 , 10 , 12, 11, 12, 10
0
10
20
30
40
50
60
Frame Rate & Animation Speed
• Frame Rate: 遊戲迴圈的更新率(預設為 60 fps )
• Animation Speed: sprite 動畫的播放速度,由動畫設計師 來決定。
範例: Sprite Animation(AnimatedSprites.rar)
public class Game1 : Microsoft.Xna.Framework.Game{
GraphicsDeviceManager graphics;SpriteBatch spriteBatch;
// Texture stuffTexture2D texture;Point frameSize = new Point(75, 75); // (width, height)Point currentFrame = new Point(0, 0); // (i, j)Point sheetSize = new Point(6, 8); // (col, row)
// Framerate stuffint timeSinceLastFrame = 0;int millisecondsPerFrame = 50;
protected override void Update(GameTime gameTime){
...
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;if (timeSinceLastFrame > millisecondsPerFrame){
timeSinceLastFrame -= millisecondsPerFrame;++currentFrame.X;if (currentFrame.X >= sheetSize.X){
currentFrame.X = 0;++currentFrame.Y;if (currentFrame.Y >= sheetSize.Y)currentFrame.Y = 0;
}}base.Update(gameTime);
}
protected override void Draw(GameTime gameTime){
GraphicsDevice.Clear(Color.White);
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
spriteBatch.Draw(texture, Vector2.Zero, new Rectangle(currentFrame.X * frameSize.X, currentFrame.Y * frameSize.Y, frameSize.X, frameSize.Y), Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0);
spriteBatch.End();
base.Draw(gameTime);}