XNA4.0学习笔记2:控制精灵和碰撞检测

                                           精灵的控制和碰撞检测


        上一节我们了解了精灵的绘制,移动以及动画的实现,并且在最后我们实现了三个环的简单动画,今天我们学习下如何通过设备控制精灵,这一节的内容比较简单,很好理解,在这里我先把控制精灵的全部代码贴上来,方便接下来的学习!

 

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函数,如果碰撞,就马上终止程序(虽然这不符合逻辑,这里只是为了演示).


关于碰撞检测,需要对于矩形的大小和游戏效率做出权衡,保证在用户眼睛无法分辨的时候最佳,不可盲目追求碰撞精确性而做过多操作,只会牺牲游戏效率这是不值得的,

由于本节内容很简单,到此为止


下一期:用户自定义精灵类!





posted @ 2013-04-07 12:07  坚固66  阅读(208)  评论(0编辑  收藏  举报