一.設置兩個精靈
要加載第二個動畫--骷髏頭,先通過命名通道加載,然後Game1類頂部添加如下:
Texture2D skullTexture;
Point skullFrameSize = new Point(75,75);
Point skullCurrentFrame = new Point(0,0);
Point skullSheetSize = new Point(6,8);
int skullTimeSincelastFrame = 0;
const int skullMillisecondsPerFrame = 50;
之前的舊變量,即三星動畫代碼改爲:
Texture2D ringsTexture;
Point ringsFrameSize = new Point(75,75);
Point ringsCurrentFrame = new Point(0,0);
Point ringsSheetSize = new Point(6,8);
int ringsTimeSinceLastFrame = 0;
int ringsMillisecondsPerFrame = 50;
然後再LoadContent方法加載兩個變量,將骷髏頭動畫設爲20fps(第一篇筆記),Draw繪製精靈。
二.鍵盤輸入
鍵盤輸入通過Microsoft.XNA.Framework.Input命名空間中的keyboard類進行處理,keyboard中有一個GetState的靜態方法,以KeyboardState結構形式獲取鍵盤當前狀態。
KeyboardState結構包含三個關鍵方法,如下:
Keys[] GetPressedKeys() 返回由調用方法時按下的鍵構成的一個數組
bool IsKeyDown(Keyskey) 根據作爲參數傳遞的鍵在調用方法時是否被按下,返回true或false
bool IsKeyUp(Keyskey) 根據作爲參數傳遞的鍵在調用方法時是否被鬆開,返回true或false
例如:判斷A是否被按下:
if(Keyboard.GetState().IsKeyDown(Keys.A))
//在update方法中檢查輸入:
KeyboardState keyboardState = Keyboard.GetState();
if(keyboardState.IsKeyDown(Keys.Left))
ringsPosition.X -= ringsSpeed;
if(keyboardState.IsKeyDown(Keys.Right))
ringsPosition.X += ringsSpeed;
if(keyboardState.IsKeyDown(Keys.Up))
ringsPosition.Y -= ringsSpeed;
if(keyboardState.IsKeyDown(Keys.Down))
ringsPosition.Y += ringsSpeed;
三.鼠標輸入
XNA提供了Mouse類實現與鼠標交互,與Keyboard類似,它通過一個GetState方法以MouseState結構形式獲得來自鼠標的數據。它的另一個方法void SetPosition(int x,int y
)設置鼠標位置(相對於窗口左上角)。
鼠標經過XNA窗口時默認爲隱藏,將GAME類的IsMouseVisible屬性設爲true。MouseState結構的重要屬性:
LeftButton ButtonState 返回鼠標左鍵狀態
MiddleButton ButtonState 返回鼠標中間狀態
RightButton ButtonState 返回鼠標右鍵狀態
ScrollWheelValue int 返回遊戲啓動以來鼠標滾輪累計移動值
X int 返回鼠標相對窗口左上角水平位置。跑出屏幕左側時爲負值
Y int 返回鼠標相對窗口左上角垂直位置。跑出屏幕頂部時爲負值
XButton1 ButtonState 返回某些鼠標附加按鈕狀態
XButton2 ButtonState 返回某些鼠標附加按鈕狀態
爲了判斷鼠標是否移動,在類頂部添加一個類級MouseState變量:
MouseState prevMouseState;//放在Game1主構造器中
該變量跟蹤上一幀鼠標狀態,與當前狀態比較,決定是否移動精靈位置:
MouseState mouseState = Mouse.GetState();
if(mouseState.X!=prevMouseState.X || mouseState.Y != prevMouseState.Y)
ringsPosition = new Vector2(mouseState.X,mouseState.Y);
prevMouseState = mouseState;
四.遊戲手柄輸入
略
五.不要精靈跑出窗口
在Update方法中添加代碼:
if(ringsPosition.X < 0)
ringsPosition.X = 0;
if(ringsPosition.Y < 0)
ringsPosition.Y = 0;
if(ringsPosition.X > Window.ClientBounds.Width - ringsFrameSize.X)
ringsPosition.X = Window.ClientBounds.Width - ringsFrameSize.X;
if(ringsPosition.Y < Window.ClientBounds.Height - ringsFrameSize.Y)
ringsPosition.Y = Window.ClientBounds.Height - ringsFrameSize.Y;
六.碰撞檢測
Rectangle結構提供了一個Intersects方法,判斷兩個矩形是否相交:
protected bool Collide(){
Rectangle ringsRect = new Rectangle((int)ringsPosition.X,(int)ringsPosition.Y,ringsFrameSize.X,ringsFrameSize.Y);
rectangle skullRect = new Rectangle((int)skullPosition.X,(int)skullPosition.Y,skullFrameSize.X,skullFrameSize.Y);
return ringsRect.Intersects(skullRect);
}
進一步優化該碰撞檢測:
int ringsCollisionRectOffset = 10; //包圍矩形的精靈距實際外框的距離
int skullCollisionRectOffset = 10;
protected bool Collide(){
Rectangle ringsRect = new Rectangle((int)ringsPosition.X+ringsCollisionRectOffset,(int)ringsPosition.Y+ringsCollisionRectOffset,ringsFrameSize.X-
(ringsCollisionRectOffset * 2),ringsFrameSize.Y-(ringsCollisionRectOffset * 2));
Rectangle skullRect = new Rectangle((int)skullPosition.X+skullCollisionRectOffset,(int)skullPosition.Y+skullCollisionRectOffset,skullFrameSize.X-
(skullCollisionRectOffset * 2),skullFrameSize.Y-(skullCollisionRectOffset * 2));
return ringsRect.Intersects(skullRect);
}
一種相關的算法是實用包圍球,尤其是當前動畫精靈是圓形的。也可以將精靈風格成多個矩形,兩個矩形數組查看是否相交。
以下爲完整程序代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace XNA2
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D ringsTexture;
Vector2 ringsPosition = Vector2.Zero;
Point ringsFrameSize = new Point(75, 75);
Point ringsCurrentFrame = new Point(0, 0);
Point ringsSheetSize = new Point(6, 8);
const float ringsSpeed = 6;
int ringsTimeSinceLastFrame = 0;
int ringsMillisecondsPerFrame = 50;
Texture2D skullTexture;
Vector2 skullPosition = new Vector2(100, 100);
Point skullFrameSize = new Point(75, 75);
Point skullCurrentFrame = new Point(0, 0);
Point skullSheetSize = new Point(6, 8);
int skullTimeSincelastFrame = 0;
const int skullMillisecondsPerFrame = 50;
int ringsCollisionRectOffset = 10; //包圍矩形的精靈距實際外框的距離
int skullCollisionRectOffset = 10;
MouseState prevMouseState;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
ringsTexture = Content.Load<Texture2D>(@"Images/threerings");
skullTexture = Content.Load<Texture2D>(@"Images/skullball");
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
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
skullTimeSincelastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (skullTimeSincelastFrame > skullMillisecondsPerFrame)
{
skullTimeSincelastFrame -= skullMillisecondsPerFrame;
// Advance to the next frame
++skullCurrentFrame.X;
if (skullCurrentFrame.X >= skullSheetSize.X)
{
skullCurrentFrame.X = 0;
++skullCurrentFrame.Y;
if (skullCurrentFrame.Y >= skullSheetSize.Y)
skullCurrentFrame.Y = 0;
}
}
ringsCurrentFrame.X++;
skullCurrentFrame.X++;
if (ringsCurrentFrame.X >= ringsSheetSize.X)
{
ringsCurrentFrame.X = 0;
++ringsCurrentFrame.Y;
if (ringsCurrentFrame.Y >= ringsSheetSize.Y)
ringsCurrentFrame.Y = 0;
}
if (skullCurrentFrame.X >= skullSheetSize.X)
{
skullCurrentFrame.X = 0;
++skullCurrentFrame.Y;
if (skullCurrentFrame.Y >= skullSheetSize.Y)
skullCurrentFrame.Y = 0;
}
KeyboardState keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.Left))
ringsPosition.X -= ringsSpeed;
if (keyboardState.IsKeyDown(Keys.Right))
ringsPosition.X += ringsSpeed;
if (keyboardState.IsKeyDown(Keys.Up))
ringsPosition.Y -= ringsSpeed;
if (keyboardState.IsKeyDown(Keys.Down))
ringsPosition.Y += ringsSpeed;
MouseState mouseState = Mouse.GetState();
if (mouseState.X != prevMouseState.X || mouseState.Y != prevMouseState.Y)
ringsPosition = new Vector2(mouseState.X, mouseState.Y);
prevMouseState = mouseState;
if (ringsPosition.X < 0)
ringsPosition.X = 0;
if (ringsPosition.Y < 0)
ringsPosition.Y = 0;
if (ringsPosition.X > Window.ClientBounds.Width - ringsFrameSize.X)
ringsPosition.X = Window.ClientBounds.Width - ringsFrameSize.X;
if (ringsPosition.Y > Window.ClientBounds.Height - ringsFrameSize.Y)
ringsPosition.Y = Window.ClientBounds.Height - ringsFrameSize.Y;
if (Collide())
this.Exit();
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
spriteBatch.Draw(ringsTexture, ringsPosition, new Rectangle(ringsCurrentFrame.X * ringsFrameSize.X, ringsCurrentFrame.Y * ringsFrameSize.Y, ringsFrameSize.X, ringsFrameSize.Y), Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0);
spriteBatch.Draw(skullTexture, skullPosition, new Rectangle(skullCurrentFrame.X * skullFrameSize.X, skullCurrentFrame.Y * skullFrameSize.Y, skullFrameSize.X, skullFrameSize.Y), Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0);
spriteBatch.End();
base.Draw(gameTime);
}
protected bool Collide()
{
Rectangle ringsRect = new Rectangle((int)ringsPosition.X + ringsCollisionRectOffset, (int)ringsPosition.Y + ringsCollisionRectOffset, ringsFrameSize.X -
(ringsCollisionRectOffset * 2), ringsFrameSize.Y - (ringsCollisionRectOffset * 2));
Rectangle skullRect = new Rectangle((int)skullPosition.X + skullCollisionRectOffset, (int)skullPosition.Y + skullCollisionRectOffset, skullFrameSize.X -
(skullCollisionRectOffset * 2), skullFrameSize.Y - (skullCollisionRectOffset * 2));
return ringsRect.Intersects(skullRect);
}
}
}