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

                                           创建自定义精灵类

            前面我们已经能创建精灵和动画,并且对精灵执行输入控制(鼠标和键盘),但是也许我们纵观上一节那个程序,发现变量种类繁多不利于管理,各种控制语句都在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);
        } 

    }
}

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






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值