XNA学习笔记3:创建自定义精灵类

来源:互联网 发布:东莞ug编程培训 编辑:程序博客网 时间:2024/06/05 18:24

                                           创建自定义精灵类

            前面我们已经能创建精灵和动画,并且对精灵执行输入控制(鼠标和键盘),但是也许我们纵观上一节那个程序,发现变量种类繁多不利于管理,各种控制语句都在update方法里面,如果有几十个精灵,那这个程序岂不是臃肿不堪,所以我们就需要自定义精灵类,让各种精灵有序的工作,分门别类的进行管理,在XNA中还有一些游戏组件,这些组件更加方便了我们的管理,游戏组件将在下一节介绍,由于本节内容十分重要,所以为分为了两节!


       在我们的游戏精灵中,虽然有很多精灵,但是说白了就只有两个种类,一个 用户控制的(如角色),一个是自动的(如敌人),但是这些精灵都拥有很多相同的属性,例如都会移动,都有坐标,都要绘制,都要更新,经过总结,他们的关系层次图如下:

所以在这里我们先在项目中添加一个新类,命名为Sprite,右键点击项目选项-添加-类,并且在sprite.cs中加入这样的包,以便使用XNA的功能

using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Graphics;

另外由于这个类只是基类,不是实际的类,所以我们修改他的类属性为抽象 abstract属性!

接下来就是要给类添加成员变量了,那么该添加些什么勒?我们在前面已经用到了很多了,这里贴出代码:

        Texture2D textureImage;                                //各种精灵属性的成员变量        protected Point frameSize;        Point currentFrame;        Point sheetSize;        int collisionOffset;         int timeSinceLastFrame = 0;        int millisecondsPerFrame;        const int defaultMillisecondsPerFrame = 16;        protected Vector2 speed;         protected Vector2 position;
可以看到类成员变量中有 位图,控制精灵位图输出的三个point变量,控制精灵的帧率,速度,当前位置等等变量,应有尽有,这些都记载了精灵的属性,其中一些有默认值,一些没有,而且一些为protected属性,这是为了子类更好的使用,接下来看看我们的构造函数:

public sprite(Texture2D textureImage,                  //构造函数1,有默认参数            Vector2 position,            Point frameSize,             int collisionOffset,            Point currentFrame,            Point sheetSize,            Vector2 speed)  : this(textureImage,            position, frameSize,             collisionOffset,             currentFrame,            sheetSize, speed,            defaultMillisecondsPerFrame) { }        public sprite(Texture2D textureImage,                   //构造函数2,没有默认参数            Vector2 position,            Point frameSize,            int collisionOffset,            Point currentFrame,            Point sheetSize,            Vector2 speed,            int millisecondsPerFrame)        {            this.textureImage = textureImage;            this.position = position;            this.frameSize = frameSize;            this.collisionOffset = collisionOffset;            this.currentFrame = currentFrame;            this.sheetSize = sheetSize;            this.speed = speed;            this.millisecondsPerFrame = millisecondsPerFrame;        }
两个构造函数,一个是有默认参数的,一个是没有默认参数的,接下来看看该类需要什么方法:

public virtual void Update(GameTime gameTime, Rectangle clientBounds)     //精灵的绘制更新函数        {            timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;             if (timeSinceLastFrame > millisecondsPerFrame)             {                timeSinceLastFrame = 0; ++currentFrame.X;                if (currentFrame.X >= sheetSize.X)                 {                     currentFrame.X = 0;                    ++currentFrame.Y;                     if (currentFrame.Y >= sheetSize.Y)                        currentFrame.Y = 0;                }            }        }
这是update方法,就跟我们在Game类update方法中的一样,进行精灵位图的位置更新,设计为虚函数是为了可以被子类重写,另外参数列表中也多添加了一个矩形类的变量,这是为了在碰撞检测的时候使用的!

public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch)     //draw方法中需要spritebatch对象,所以需要传递        {            spriteBatch.Draw(textureImage, position,                 new Rectangle(currentFrame.X * frameSize.X,                     currentFrame.Y * frameSize.Y, frameSize.X, frameSize.Y),                Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0);         }
这是我们的精灵类的draw方法,就是拷贝的原Game类的draw方法,绘制出精灵,由于在Game中已经有一个SpriteBatch变量,我们这里没有,所以需要传递一个spriteBatch变量,在绘制的时候使用!

public Rectangle collisionRect                                            //返回精灵的包装矩形,用于碰撞检测        {            get            {                return new Rectangle((int)position.X + collisionOffset,                    (int)position.Y + collisionOffset,                     frameSize.X - (collisionOffset * 2),                     frameSize.Y - (collisionOffset * 2));            }        } 
该方法返回精灵的包装矩形,在碰撞检测的时候需要

public abstract Vector2 direction                                         //精灵移动方向,子类都不相同,所以应该设置成抽象        {             get;        }
该方法为direction的访问器,获取精灵的移动方向,具体如何使用稍后会讲!

好了,我们的精灵基类就设计完成了,下面我们开始设计我们的角色类!


2:设计用户控制的角色类

同样的方法新建一个类,注明继承自Sprite类,并且添加XNA的包,与上面不同的是这里需要该类获取到用户的控制,所以还要多加一行

using Microsoft.Xna.Framework.Input;
这行就可以提供获取用户输入的控制,然后添加构造函数,几乎都跟基类一样,一个一个赋值而已!

public UserControlledSprite(               //带默认参数的构造函数 调用了基类构造函数            Texture2D textureImage,            Vector2 position,            Point frameSize,            int collisionOffset,            Point currentFrame,            Point sheetSize,             Vector2 speed) : base(textureImage,            position,            frameSize,            collisionOffset,            currentFrame,            sheetSize,            speed)        { }        public UserControlledSprite(               //一般的构造函数 调用了基类构造函数            Texture2D textureImage,            Vector2 position,            Point frameSize,            int collisionOffset,            Point currentFrame,            Point sheetSize,            Vector2 speed,            int millisecondsPerFrame) : base(            textureImage,            position,            frameSize,            collisionOffset,            currentFrame,            sheetSize,            speed,            millisecondsPerFrame)        { } 
接下来先实现基类中的抽象方法drection:

public override Vector2 direction                            //游戏方向的确定,由用户输入和自定义的速度共同决定        {            get            {                Vector2 inputDirection = Vector2.Zero;                if (Keyboard.GetState( ).IsKeyDown(Keys.Left))                    inputDirection.X -= 1;                if (Keyboard.GetState( ).IsKeyDown(Keys.Right))                    inputDirection.X += 1;                if (Keyboard.GetState( ).IsKeyDown(Keys.Up))                    inputDirection.Y -= 1;                if (Keyboard.GetState( ).IsKeyDown(Keys.Down))                    inputDirection.Y += 1;                   return inputDirection * speed;            }        }
这个方向矢量表示了精灵的移动距离,在以前我们按下左键精灵直接移动2,按多少次就移动多少个2,这样就无法中途更改移动速度,所以这里用方向乘以速度来决定位移量,如果没有按键,返回的是0!

public override void Update(GameTime gameTime, Rectangle clientBounds)        {                            position += direction;                           MouseState currMouseState = Mouse.GetState( );                   //鼠标控制 就不在使用方向了            if (currMouseState.X != prevMouseState.X ||   currMouseState.Y != prevMouseState.Y)            {                position = new Vector2(currMouseState.X, currMouseState.Y);            }            prevMouseState = currMouseState;                          if (position.X < 0)                                              //保持精灵始终在窗口中间                position.X = 0;             if (position.Y < 0)                position.Y = 0;            if (position.X > clientBounds.Width - frameSize.X)            {                position.X = clientBounds.Width - frameSize.X;            }            if (position.Y > clientBounds.Height - frameSize.Y)            {                position.Y = clientBounds.Height - frameSize.Y;            }              base.Update(gameTime, clientBounds);        } 
这里我们也知道,鼠标控制是要检测上一帧鼠标位置和当前帧有没有发生改变,所以需要一个prevmousestate变量,这个需要在写update方法的时候加入到类成员变量中,设为私有的!

由于draw方法不需要做改变,所以我们这里就不需要重写draw方法了,现在我们的角色类也就已经写完了。


3:设计自动的游戏精灵类

同上建立一个继承自sprite的类,并且加入XNA的包,这里不需要用户输入,所以可以不加第三个包,

这个类设计很简单,我就不用多说了

 class AutomatedSprite:sprite    {        public AutomatedSprite            (Texture2D textureImage,            Vector2 position,            Point frameSize,            int collisionOffset,            Point currentFrame,            Point sheetSize,            Vector2 speed)  : base(            textureImage,            position,            frameSize,            collisionOffset,            currentFrame,            sheetSize,            speed) { }                public AutomatedSprite(            Texture2D textureImage,            Vector2  position,            Point frameSize,            int collisionOffset,            Point currentFrame,            Point sheetSize,            Vector2 speed,            int millisecondsPerFrame)  : base(            textureImage,            position,            frameSize,            collisionOffset,            currentFrame,            sheetSize,            speed,            millisecondsPerFrame) { }        public override Vector2 direction        {            get            {                return speed;                          //他没有方向的控制,返回值直接就是速度            }        }        public override void Update(GameTime gameTime, Rectangle clientBounds)        {            position += direction;            base.Update(gameTime, clientBounds);               //别忘记调用基类的方法,以便不影响精灵位图的更新        }     }

我们的三个类都设计完了,本节的工作也就完成了,为了需要我这里把前面两个类的完整代码贴出来

Sprite类

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Graphics;namespace WindowsGame6{    abstract class sprite    {        Texture2D textureImage;                                //各种精灵属性的成员变量        protected Point frameSize;        Point currentFrame;        Point sheetSize;        int collisionOffset;         int timeSinceLastFrame = 0;        int millisecondsPerFrame;        const int defaultMillisecondsPerFrame = 16;        protected Vector2 speed;         protected Vector2 position;        public sprite(Texture2D textureImage,                  //构造函数1,有默认参数            Vector2 position,            Point frameSize,             int collisionOffset,            Point currentFrame,            Point sheetSize,            Vector2 speed)  : this(textureImage,            position, frameSize,             collisionOffset,             currentFrame,            sheetSize, speed,            defaultMillisecondsPerFrame) { }        public sprite(Texture2D textureImage,                   //构造函数2,没有默认参数            Vector2 position,            Point frameSize,            int collisionOffset,            Point currentFrame,            Point sheetSize,            Vector2 speed,            int millisecondsPerFrame)        {            this.textureImage = textureImage;            this.position = position;            this.frameSize = frameSize;            this.collisionOffset = collisionOffset;            this.currentFrame = currentFrame;            this.sheetSize = sheetSize;            this.speed = speed;            this.millisecondsPerFrame = millisecondsPerFrame;        }        public virtual void Update(GameTime gameTime, Rectangle clientBounds)     //精灵的绘制更新函数        {            timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;             if (timeSinceLastFrame > millisecondsPerFrame)             {                timeSinceLastFrame = 0; ++currentFrame.X;                if (currentFrame.X >= sheetSize.X)                 {                     currentFrame.X = 0;                    ++currentFrame.Y;                     if (currentFrame.Y >= sheetSize.Y)                        currentFrame.Y = 0;                }            }        }        public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch)     //draw方法中需要spritebatch对象,所以需要传递        {            spriteBatch.Draw(textureImage, position,                 new Rectangle(currentFrame.X * frameSize.X,                     currentFrame.Y * frameSize.Y, frameSize.X, frameSize.Y),                Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0);         }        public abstract Vector2 direction                                         //精灵移动方向,子类都不相同,所以应该设置成抽象        {             get;        }        public Rectangle collisionRect                                            //返回精灵的包装矩形,用于碰撞检测        {            get            {                return new Rectangle((int)position.X + collisionOffset,                    (int)position.Y + collisionOffset,                     frameSize.X - (collisionOffset * 2),                     frameSize.Y - (collisionOffset * 2));            }        }     }}

UserControlledSprite类

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Graphics;using Microsoft.Xna.Framework.Input;                //用户控制的类,需要从设备获取数据namespace WindowsGame6{    class UserControlledSprite:sprite    {        private MouseState prevMouseState;        public UserControlledSprite(               //带默认参数的构造函数 调用了基类构造函数            Texture2D textureImage,            Vector2 position,            Point frameSize,            int collisionOffset,            Point currentFrame,            Point sheetSize,             Vector2 speed) : base(textureImage,            position,            frameSize,            collisionOffset,            currentFrame,            sheetSize,            speed)        { }        public UserControlledSprite(               //一般的构造函数 调用了基类构造函数            Texture2D textureImage,            Vector2 position,            Point frameSize,            int collisionOffset,            Point currentFrame,            Point sheetSize,            Vector2 speed,            int millisecondsPerFrame) : base(            textureImage,            position,            frameSize,            collisionOffset,            currentFrame,            sheetSize,            speed,            millisecondsPerFrame)        { }         public override Vector2 direction                            //游戏方向的确定,由用户输入和自定义的速度共同决定        {            get            {                Vector2 inputDirection = Vector2.Zero;                if (Keyboard.GetState( ).IsKeyDown(Keys.Left))                    inputDirection.X -= 1;                if (Keyboard.GetState( ).IsKeyDown(Keys.Right))                    inputDirection.X += 1;                if (Keyboard.GetState( ).IsKeyDown(Keys.Up))                    inputDirection.Y -= 1;                if (Keyboard.GetState( ).IsKeyDown(Keys.Down))                    inputDirection.Y += 1;                   return inputDirection * speed;            }        }        public override void Update(GameTime gameTime, Rectangle clientBounds)        {                            position += direction;                            MouseState currMouseState = Mouse.GetState( );            if (currMouseState.X != prevMouseState.X ||   currMouseState.Y != prevMouseState.Y)            {                position = new Vector2(currMouseState.X, currMouseState.Y);            }            prevMouseState = currMouseState;                         if (position.X < 0)                position.X = 0;            if (position.Y < 0)                position.Y = 0;            if (position.X > clientBounds.Width - frameSize.X)            {                position.X = clientBounds.Width - frameSize.X;            }            if (position.Y > clientBounds.Height - frameSize.Y)            {                position.Y = clientBounds.Height - frameSize.Y;            }              base.Update(gameTime, clientBounds);        }     }}

下一节我们介绍如何利用游戏组件,把这些类有机的组合在一起,他们的使用会让游戏程序变得那么的面向对象化!






原创粉丝点击