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); } }}
下一节我们介绍如何利用游戏组件,把这些类有机的组合在一起,他们的使用会让游戏程序变得那么的面向对象化!
- XNA学习笔记3:创建自定义精灵类
- XNA4.0学习笔记1:XNA解析及精灵动画
- DirectX11学习笔记 创建两个不同的精灵精灵
- 使用 Spirit 类在 XNA 中创建游戏中的基本单位精灵(十三)
- XNA学习笔记3 鼠标响应
- XNA学习笔记1
- XNA 学习笔记2
- cocos2d-x(3): 创建精灵基础学习
- 【Cocos2d-html5游戏引擎学习笔记(10)】自定义精灵动画
- COCOS学习笔记--精灵Sprite的3种创建方法总结
- ToLua学习笔记,创建自定义矩阵类
- cocos2d-x 学习笔记—缓存中创建精灵
- cocos2d-x学习笔记——创建精灵
- 【学习笔记】Cocos2d-JS 如何创建精灵?(一)
- Cocos2dx学习笔记(2)——精灵的创建
- cocos2d-x初探学习笔记--精灵类
- XNA学习笔记(2)-XNA的编程模型
- XNA学习笔记——用顶点缓冲和索引缓冲创建地形
- nefu 473 Drainage DitchesHal Burch 最大流
- 在不学习就变老了
- codeforces 2 C. Commentator problem(模拟退火)
- miracast前景分析
- java中形参与实参的一点总结
- XNA学习笔记3:创建自定义精灵类
- poj3667
- MyEclipse 2013 破解
- ubuntu里apt-get install java的情况解决
- Hadoop NameNode结点不能启动
- mysql查询操作
- 嵌入式学习计划
- java连接数据库
- MyEclipse 2013 破解