承接前兩章,本章實作 Sprite 的逐幀動畫,並解決播放速度過快的問題。
將動作分解為連續幀,排列在單一圖片。繪製時透過 sourceRectangle 指定裁切區域,依序切換即產生動畫效果。XNA 原生不支援 GIF,需自行拆幀或使用第三方庫;本範例採用手動拆解的 PNG 圖片(每幀 60x110 像素)。
Point m_frameSize = new Point(60, 110);
int m_frameIndex = 0;
protected override void Update(GameTime gameTime)
{
// 鍵盤/滑鼠移動邏輯(略)...
// 簡易幀索引遞增(0~5 循環)
m_frameIndex = m_frameIndex + 1 > 5 ? 0 : m_frameIndex + 1;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(
texture: img,
position: new Vector2(Position.X, Position.Y),
sourceRectangle: new Rectangle(m_frameIndex * m_frameSize.X, 0, m_frameSize.X, m_frameSize.Y),
color: Color.White,
rotation: 0f,
origin: Vector2.Zero,
scale: 1f,
effects: SpriteEffects.None,
layerDepth: 0f);
spriteBatch.End();
base.Draw(gameTime);
}
XNA 預設 60 FPS,直接在 Update 遞增索引會導致動畫過快且 CPU 佔用高。解法:累積經過時間,達閾值才切幀。
int millisecondsPerFrame = 50; // 20 FPS
int timeSinceLastFrame = 0;
protected override void Update(GameTime gameTime)
{
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > millisecondsPerFrame)
{
timeSinceLastFrame -= millisecondsPerFrame;
// 這裡執行幀切換邏輯
m_frameIndex = m_frameIndex + 1 > 5 ? 0 : m_frameIndex + 1;
}
// 其餘移動邏輯...
base.Update(gameTime);
}
if (isActive) // 有輸入或正在移動
m_frameIndex = m_frameIndex + 1 > 5 ? 0 : m_frameIndex + 1;
else
m_frameIndex = 0; // 回到站立姿態
sourceRectangle 實作 2D 逐幀動畫。GameTime.ElapsedGameTime 控制幀率,平衡流暢度與效能。完整原始碼:下載連結