精灵的控制和碰撞检测
上一节我们了解了精灵的绘制,移动以及动画的实现,并且在最后我们实现了三个环的简单动画,今天我们学习下如何通过设备控制精灵,这一节的内容比较简单,很好理解,在这里我先把控制精灵的全部代码贴上来,方便接下来的学习!
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 WindowsGame6 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Texture2D ringtexture; //三环动画的相关变量 Point ringframeSize = new Point(75, 75); Point ringcurrentFrame = new Point(0, 0); Point ringsheetSize = new Point(6, 8); Vector2 ringpos1 = Vector2.Zero; const float ringspeed = 5; Texture2D skulltexture; //骷髅头动画相关变量 Point skullframeSize = new Point(75, 75); Point skullcurrentFrame = new Point(0, 0); Point skullsheetSize = new Point(6, 8); MouseState prevmousestate; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { // TODO: 在此处添加初始化逻辑 base.Initialize(); } protected override void LoadContent() { // 创建新的 SpriteBatch,可将其用于绘制纹理。 spriteBatch = new SpriteBatch(GraphicsDevice); ringtexture = Content.Load<Texture2D>("Images//threerings"); //加载两幅位图 skulltexture = Content.Load<Texture2D>("Images//skullball"); // TODO: 在此处使用 this.Content 加载游戏内容 } protected override void UnloadContent() { // TODO: 在此处取消加载任何非 ContentManager 内容 } protected override void Update(GameTime gameTime) { // 允许游戏退出 if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); ++ringcurrentFrame.X; //循环三环精灵位图 if (ringcurrentFrame.X >= ringsheetSize.X) { ringcurrentFrame.X = 0; ++ringcurrentFrame.Y; if (ringcurrentFrame.Y >= ringsheetSize.Y) ringcurrentFrame.Y = 0; } ++skullcurrentFrame.X; //循环骷髅精灵位图 if (skullcurrentFrame.X >= skullsheetSize.X) { skullcurrentFrame.X = 0; ++skullcurrentFrame.Y; if (skullcurrentFrame.Y >= skullsheetSize.Y) skullcurrentFrame.Y = 0; } // TODO: 在此处添加更新逻辑 KeyboardState keyboardstate = Keyboard.GetState(); //键盘控制三环动画语句 if (keyboardstate.IsKeyDown(Keys.Left)) ringpos1.X -= ringspeed; if (keyboardstate.IsKeyDown(Keys.Right)) ringpos1.X += ringspeed; if (keyboardstate.IsKeyDown(Keys.Up)) ringpos1.Y -= ringspeed; if (keyboardstate.IsKeyDown(Keys.Down)) ringpos1.Y += ringspeed; MouseState mouseState = Mouse.GetState(); //鼠标控制三环动画语句 if (mouseState.X != prevmousestate.X || mouseState.Y != prevmousestate.Y) ringpos1 = new Vector2(mouseState.X, mouseState.Y); prevmousestate = mouseState; if (ringpos1.Y < 0) //使三环精灵一直保持在窗口中 ringpos1.Y = 0; if (ringpos1.Y > Window.ClientBounds.Height - ringframeSize.Y) ringpos1.Y = Window.ClientBounds.Height - ringframeSize.Y; if (ringpos1.X < 0) ringpos1.X = 0; if (ringpos1.X > Window.ClientBounds.Width - ringframeSize.X) ringpos1.X = Window.ClientBounds.Width - ringframeSize.X; base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend); spriteBatch.Draw( //绘制三环精灵 ringtexture,ringpos1, new Rectangle(ringcurrentFrame.X * ringframeSize.X, ringcurrentFrame.Y * ringframeSize.Y, ringframeSize.X, ringframeSize.Y), Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0); spriteBatch.Draw( //绘制骷髅精灵 skulltexture, new Vector2(100,100), 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(); // TODO: 在此处添加绘图代码 base.Draw(gameTime); } } }
1:更多精灵的绘制
在昨天那个三环精灵程序代码的基础上,现在我们需要再多加一个精灵位图进来,还是老办法:
这是一副骷髅精灵位图,跟三环动画一样在类最前面加上那几行代码,并且在loadcontent函数中加载位图,并且在draw函数中绘制出位图,为了不与第一幅重复,我们这里设置绘制的坐标为(100,100),然后运行,可以看到屏幕中有两个动画在旋转!
2:使用键盘控制精灵的移动!
这里需要一点新知识了,键盘输入是通过Microsoft.XNA.Framework.Input命名空间中的Keyboard类来处 理的。Keyboard 类有一个叫做 GetState 的静态方法,用 KeyboardState 结构的形式返 回键盘目前的状态。
KeyboardState 结构中包含三个能够满足您大部分的功能需求的关键方法如下:
Keys[] GetPressedKeys() 返回一个在方法被调用时被按下的键的数组
bool IsKeyDown(Keys key) 返回 true 或 false,取决于方法调用时参数所代表的 按键是否被按下
bool IsKeyUp(Keys key) 返回 true 或 false,取决于方法调用时参数所代表的 按键是否被释放
举个例子,如果要检测键盘的A键是否被按下,则该这样来写代码:
if(Keyboard.GetState( ).IsKeyDown(Keys.A));
好了,现在我们可以来着手实现键盘的上下左右控制三环动画的移动了,这里跟昨天一样,需要加一个Vector2的变量ringpos1,并且在draw的方法中改变三环动画的位置为ringpos1,
接下来就应该在update方法中实现移动的距离更新,代码如下:
KeyboardState keyboardstate = Keyboard.GetState(); //键盘控制三环动画语句 if (keyboardstate.IsKeyDown(Keys.Left)) ringpos1.X -= ringspeed; if (keyboardstate.IsKeyDown(Keys.Right)) ringpos1.X += ringspeed; if (keyboardstate.IsKeyDown(Keys.Up)) ringpos1.Y -= ringspeed; if (keyboardstate.IsKeyDown(Keys.Down)) ringpos1.Y += ringspeed;
这里就如同上面贴出来的一样,我们之所以不用if else,而是用4个if是因为这样可以同时从两个方向移动,比如上左,右下,如果改为if else就只能向其中一个方向移动,现在可以编译运行,使用键盘的上下左右,就可以移动我们的三环动画了!!
3:使用鼠标控制三环动画!
XNA 提供了一个和 Keyboard 类行为很相似的 Mouse 类来和鼠标进行交互。Mouse 类也 有一个 GetState 方法,能以 MouseState 结构的形式从鼠标返回数据。
MouseState 有一些属 性将会帮助您了解到当您调用 GetState 时鼠标在特定时刻发生了什么。具体如下:
LeftButton ButtonState 返回鼠标左键的状态
MiddleButton ButtonState 返回鼠标中键的状态
RightButton ButtonState 返 回鼠标右键的状态
ScrollWheelValue int 返回自游戏开始后鼠标滚轮滚动刻度的累加量.要想知 道滚轮滚动了多少,把当前帧的ScrollWheelValue和 上一帧的进行比较.
X int 返回鼠标光标相对于游戏窗口左上角的水平位置(坐 标).如果鼠标光标在游戏窗口的左侧,这个值是负值;如 果在游戏窗口右边,这个值大于游戏窗口的宽度
. XButton1 ButtonState 返回某些鼠标上额外的按键的状态
XButton2 ButtonState 返回某些鼠标上额外的按键的状态
Y int 返回鼠标光标相对于游戏窗口左上角的垂直位置(坐 标).如果鼠标光标在游戏窗口的上方,这个 值是负值; 如果在游戏窗口下方,这个值大于游戏窗口的高度.
为了确定鼠标是否被移动,在 Game1 类顶部添加一个类成员变量:
MouseState preMouseState; (如上图所示)
将以下代码添加到 Update 方法中,位于 base.Update 方法的调用之前:
MouseState mouseState = Mouse.GetState(); //鼠标控制三环动画语句 if (mouseState.X != prevmousestate.X || mouseState.Y != prevmousestate.Y) ringpos1 = new Vector2(mouseState.X, mouseState.Y); prevmousestate = mouseState;
现在编译运行,则三环动画会跟着鼠标走,我们的鼠标控制也就完成了!
4:使动画保留在窗口之间
您可能已经注意到了,三环精灵会在您将它移动得足够远时消失在屏幕的边缘。让玩家控 制的物体能够离开屏幕并且消失不见永远不会是一个好主意。要纠正这个问题,您需要在 Update 函数的结尾更新精灵的位置。如果精灵已经向左、向右、向上或向下移动得太远,更 正它的位置来使其保持在游戏窗口中。将下面的代码添加到 Update 方法的末尾,位于 base.Update 方法的调用之前:
if (ringpos1.Y < 0) //使三环精灵一直保持在窗口中 ringpos1.Y = 0; if (ringpos1.Y > Window.ClientBounds.Height - ringframeSize.Y) ringpos1.Y = Window.ClientBounds.Height - ringframeSize.Y; if (ringpos1.X < 0) ringpos1.X = 0; if (ringpos1.X > Window.ClientBounds.Width - ringframeSize.X) ringpos1.X = Window.ClientBounds.Width - ringframeSize.X;
再次编译运行,现在鼠标和键盘都可以控制三环动画的移动了,这时动画始终是停留在窗口之内的!
5:一般性的碰撞检测
大家知道碰撞检测是游戏中非常重要的一个环节,更是涉及到很多数学问题,可是在我们的XNA中,碰撞问题简化得你都不敢想象,我们首先运行上面的哪一个程序:
这两个精灵,骷髅头暂时是固定的,我们可以用键盘控制三环精灵,实现碰见检测可以利用矩形包装法,也就是用两个矩形包装好精灵,然后检查两个矩形是否相交,则可判断是否碰撞,效果如下:
接下来我们需要做的就是检测两个矩形在屏幕上是否相交,碰巧XNA为我们提供的矩形类里面有这样一个成员函数可以检测矩形相交,新加一个成员函数,代码如下:
protected bool colline() //检测碰撞的代码 { Rectangle ringRect = new Rectangle((int)ringpos1.X, (int)ringpos1.Y, ringframeSize.X, ringframeSize.Y); Rectangle skullErct = new Rectangle((int)skullpos2.X, (int)skullpos2.Y, skullframeSize.X, skullframeSize.Y); return ringRect.Intersects(skullErct); }
在程序中新建了两个矩形区域,构造方法中分别给予了两个精灵的位置,返回值为布尔类型,利用了矩形类的intersects方法,接下来可以在update方法中检测碰撞了
if(colline() ).....则执行相关的事情,在这里我们调用Exit函数,如果碰撞,就马上终止程序(虽然这不符合逻辑,这里只是为了演示).
关于碰撞检测,需要对于矩形的大小和游戏效率做出权衡,保证在用户眼睛无法分辨的时候最佳,不可盲目追求碰撞精确性而做过多操作,只会牺牲游戏效率这是不值得的,
由于本节内容很简单,到此为止
下一期:用户自定义精灵类!