學習XNA遊戲編程2:用戶輸入和碰撞檢測


一.設置兩個精靈

    要加載第二個動畫--骷髏頭,先通過命名通道加載,然後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);
        }
    }
}


 

 

發佈了25 篇原創文章 · 獲贊 41 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章