一.设置两个精灵
要加载第二个动画--骷髅头,先通过命名通道加载,然后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);
}
}
}