平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛。在这里分享一下经验,仅为了和各位朋友交流经验。平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXNA 吧,最后请高手绕道而行吧,以免浪费时间。(为了突出重点和减少篇幅,有些示例代码可能不够严谨。)

Spirit

如果你觉得不习惯,可以使用精灵的另一种写法 Sprite。Spirit 是一个重要的类,表示游戏中的单位,比如:敌人,玩家等。很多类都会从 Spirit 类派生。

下面是 Spirit 的一些成员。

Destroyed 事件会在 Destroy 方法中被调用,而这个事件主要被精灵管理器所使用,精灵管理器会在以后被讲到。DrawOrderChanged 事件会在 DrawOrder 属性中被调用,该属性用来确定精灵的绘制次序。

internal event EventHandler<SpiritEventArgs> Destroyed;
internal event EventHandler<SpiritEventArgs> DrawOrderChanged; internal virtual void Destroy ( )
{ if ( null != this.Destroyed )
this.Destroyed ( this, new SpiritEventArgs ( this ) ); } private int drawOrder = ; internal int DrawOrder
{
get { return this.drawOrder; }
set
{
this.drawOrder = value; if ( null != this.DrawOrderChanged )
this.DrawOrderChanged ( this, new SpiritEventArgs ( this ) ); }
}

字段 scene 用来表示控制精灵的场景,以后我们会将他的类型改为 IPlayScene。字段 world 表示控制场景的 World 类。字段 audioManager 用来控制音乐,我们将从场景中获取 AudioManager,而不是重新创建。

protected readonly IScene scene;
private readonly World world;
protected readonly AudioManager audioManager;

字段 movie 表示精灵使用的电影,多个精灵可以共享同一个电影,字段 movieName 表示电影的名称。字段 extendMovie,extendMovieName 表示扩展的电影以及扩展的电影的名称。扩展电影可以用来播放特殊的效果,比如:玩家受伤后发出红色的光。

属性 Type 表示精灵的类型,你可以根据精灵的类型来确定精灵具体是什么东西。

protected readonly Movie movie;
private readonly string movieName;
protected readonly Movie extendMovie;
private readonly string extendMovieName;
protected int type;
internal virtual int Type
{
get { return this.type; }
set { this.type = value; }
}

字段 Width,Height 用来表示精灵的大小,而字段 halfSize 用来表示精灵尺寸的一半,我们会预先计算这个值,以避免重复计算。

internal readonly int Width;
internal readonly int Height;
protected readonly Vector2 halfSize;

字段 Location 表示精灵的位置,属性 Angle 表示精灵的角度,如果字段 isRotable 为 false,则 Angle 是不能被修改的,字段 isMovieRotable 则表示电影的角度是否同时被修改。每当 Angle 被修改后,我们会调用 updateSpeed 方法来更新速度。

字段 speed 表示精灵的速度,字段 xSpeed,ySpeed 分别表示精灵在 x,y 轴上的速度。如果修改 Speed 属性,xSpeed 和 ySpeed 并不会被修改,但是你可以在派生类中修改 updateSpeed 方法来完成。

字段 protoSpeed,protoXSpeed,protoYSpeed 用来记录速度的原始值。字段 spiritBatch 用来绘制电影。

字段 isMovable 表示精灵是否可以移动,字段 isMoving 表示精灵是否正在移动中。

internal Vector2 Location;
internal readonly HitArea HitArea; protected int angle;
internal virtual int Angle
{
get { return this.angle; }
set
{ if ( !this.isRotable )
return; value = Calculator.Degree ( value ); this.angle = value; if ( this.isMovieRotable )
{
this.movie.Rotation = value; if ( null != this.extendMovie )
this.extendMovie.Rotation = value; } this.updateSpeed ( );
}
} private float protoSpeed;
private float protoXSpeed;
private float protoYSpeed;
protected float speed;
protected float xSpeed;
protected float ySpeed;
public virtual float Speed
{
get { return this.speed; }
set
{
this.speed = value;
this.protoSpeed = this.speed;
this.updateSpeed ( );
}
} private SpriteBatch spiritBatch; protected bool isMoving = false;
protected bool isMovable = true;
protected bool isRotable = true;
private readonly bool isMovieRotable;

字段 destroyFrameCount 用来表示销毁精灵的帧数,字段 isAreaLimited 表示精灵是否可以超出 World 的 BattleArea,字段 isAreaEntered 表示精灵是否已经进入 BattleArea 中,字段 areaFrameCount 表示精灵进入 BattleArea 的限制时间。(但这里没有展示关于 isAreaLimited,isAreaEntered,areaFrameCount 的代码。)

字段 IsVisible 表示精灵是否可视。

private long destroyFrameCount;

private readonly bool isAreaLimited;
protected bool isAreaEntered; private long areaFrameCount; internal bool IsVisible = true;

在 Spirit 的构造函数中,我们将初始化这些字段。

protected Spirit ( IScene scene, int type, Vector2 location, string movieName, string extendMovieName, float speed, int angle, HitArea hitArea, int width, int height, double destroySecond, bool isMovieRotable, bool isAreaLimited, bool isAreaEntered, double areaSecond )
{ if ( null == scene || string.IsNullOrEmpty ( movieName ) )
throw new ArgumentNullException ( "scene, movieName", "scene, movieName can't be null" ); this.destroyFrameCount = World.ToFrameCount ( destroySecond ); this.scene = scene;
this.world = scene.World;
this.audioManager = scene.AudioManager; this.isMovieRotable = isMovieRotable;
this.isAreaLimited = isAreaLimited;
this.isAreaEntered = isAreaEntered; this.areaFrameCount = World.ToFrameCount ( areaSecond ); this.Location = location; this.movie = Movie.Clone ( this.scene.Makings[movieName] as Movie );
this.movie.Ended += this.movieEnded; this.movieName = movieName; if ( !string.IsNullOrEmpty ( extendMovieName ) )
{
this.extendMovie = Movie.Clone ( this.scene.Makings[extendMovieName] as Movie );
this.extendMovieName = extendMovieName;
} this.Width = width;
this.Height = height;
this.halfSize = new Vector2 ( width / , height / ); this.Type = type; this.Speed = speed;
this.Angle = angle;
this.HitArea = hitArea; if ( null != this.HitArea )
this.HitArea.Locate ( this.getHitAreaLocation ( ) ); }

在方法 LoadContent 中,我们将设置电影的相关内容,并从 World 中获取 SpriteBatch。在 Dispose 方法中我们销毁了一些对象。

internal virtual void LoadContent ( )
{
this.spiritBatch = this.scene.World.Services.GetService ( typeof ( SpriteBatch ) ) as SpriteBatch; this.movie.Texture = ( this.scene.Makings[ this.movieName ] as Movie ).Texture; if ( null != this.extendMovie )
this.extendMovie.Texture = ( this.scene.Makings[ this.extendMovieName ] as Movie ).Texture; } public void Dispose ( )
{ this.Dispose ( true ); } protected virtual void Dispose ( bool disposing )
{ if ( disposing )
{
this.movie.Ended -= this.movieEnded;
this.movie.Dispose ( ); if ( null != this.extendMovie )
this.extendMovie.Dispose ( ); if ( null != this.HitArea )
this.HitArea.Dispose ( ); } }

在 Update 方法中,我们将播放动画,并根据可用性调用 updating 方法。而在 updating 方法中,我们将判断是否需要销毁精灵,以及是否需要移动精灵,使碰撞区的位置和精灵的位置保持一致。

我们允许碰撞区和精灵的位置存在偏移,你可以通过 getHitAreaLocation 方法修改他。

internal void Update ( GameTime time )
{
Movie.NextFrame ( this.movie ); if ( null != this.extendMovie )
Movie.NextFrame ( this.extendMovie ); if ( this.scene.IsEnabled && this.world.IsEnabled )
this.updating ( time ); } protected virtual void updating ( GameTime time )
{ if ( this.destroyFrameCount > && --this.destroyFrameCount <= )
{
this.Destroy ( );
return;
} if ( this.isMoving && this.isMovable )
this.move ( ); if ( null != this.HitArea )
this.HitArea.Locate ( this.getHitAreaLocation ( ) ); } protected virtual Point getHitAreaLocation ( )
{ return new Point ( ( int ) this.Location.X, ( int ) this.Location.Y ); }

在 Draw 方法中,我们将根据一些条件来决定是否调用方法 drawing。而在 drawing 方法中,我们将调整电影的位置,并绘制他们。

同样,电影的位置可以不同于精灵的位置,你可以通过方法 getMovieLocation 来调整他。

internal void Draw ( GameTime time )
{ if ( !this.scene.IsClosed && this.IsVisible )
{
this.spiritBatch.Begin ( );
this.drawing ( time, this.spiritBatch );
this.spiritBatch.End ( );
} } protected virtual void drawing ( GameTime time, SpriteBatch batch )
{
this.movie.Location = this.getMovieLocation ( ); Movie.Draw ( this.movie, time, batch ); if ( null != this.extendMovie )
{
this.extendMovie.Location = this.movie.Location;
Movie.Draw ( this.extendMovie, time, batch );
} } protected virtual Vector2 getMovieLocation ( )
{ return this.Location; }

方法 movieEnded 将在电影播放完毕之后调用,方法 Execute 用来执行一些命令,在本示例中不会用到。

方法 move 和 updateSpeed 是需要派生类修改的,以设置精灵的速度和移动。

protected virtual void movieEnded ( object sender, MovieEventArgs e )
{ } internal virtual void Execute ( int action )
{ } protected virtual void move ( )
{ } protected virtual void updateSpeed ( )
{
this.protoXSpeed = this.xSpeed;
this.protoYSpeed = this.ySpeed;
}

精灵还有一些播放电影的方法,这里不再累述。

修改 World

我们需要为 World 增加一个管理精灵的类,代码如下:

internal sealed class SpiritCollection
{
private readonly List<Spirit> spirits = new List<Spirit> ( ); private bool isInitialized = false; internal SpiritCollection ( )
{ } internal void Initialize ( )
{ if ( this.isInitialized )
return; foreach ( Spirit spirit in this.spirits.ToArray ( ) )
spirit.LoadContent ( ); this.isInitialized = true;
} internal void Update ( GameTime time )
{ foreach ( Spirit spirit in this.spirits.ToArray ( ) )
spirit.Update ( time ); } internal void Draw ( GameTime time )
{ foreach ( Spirit spirit in this.spirits.ToArray ( ) )
spirit.Draw ( time ); } internal void Add ( Spirit spirit )
{ if ( spirit == null || this.spirits.Contains ( spirit ) )
return; if ( isInitialized )
spirit.LoadContent ( ); spirit.DrawOrderChanged += this.drawOrderChanged;
this.spirits.Add ( spirit );
this.spirits.Sort ( DrawableSort );
} internal bool Remove ( Spirit spirit )
{ if ( spirit == null )
return false; spirit.DrawOrderChanged -= this.drawOrderChanged; return this.spirits.Remove ( spirit );
} private void drawOrderChanged ( object sender, SpiritEventArgs e )
{ this.spirits.Sort ( DrawableSort ); } private static int DrawableSort ( Spirit a, Spirit b )
{
return a.DrawOrder.CompareTo ( b.DrawOrder );
} }

然后我们为 World 增加一个名称为 Components 的字段,用来管理精灵。

internal readonly SpiritCollection Components = new SpiritCollection ( );

示例

在 SceneT14 场景中,我们创建了一个精灵 bird,另外,我们有两个按钮,点击 Play,小鸟将移动,点击 Stop,小鸟停止移动。

下面是小鸟的代码,在代码中,我们通过修改 updateSpeed,move 方法实现了小鸟的移动。并通过 Go 和 Stop 方法控制了小鸟的移动。

internal class Bird
: Spirit
{ internal Bird ( IScene scene, Vector2 location )
: base ( scene, , location,
"bird", null,
, ,
new SingleRectangleHitArea ( new Rectangle ( -, -, , ) ),
,
,
,
true,
false,
false, )
{ } protected override void updateSpeed ( )
{
this.xSpeed = this.speed;
this.ySpeed = this.speed; base.updateSpeed ( );
} protected override void move ( )
{
this.Location.X += this.xSpeed;
this.Location.Y += this.ySpeed;
} internal void Go ( )
{
this.isMoving = true;
this.PlayMovie ( "go" );
} internal void Stop ( )
{
this.isMoving = false;
this.PlayMovie ( "stop" );
} }

在两个按钮的 Selected 事件中,我们分别让小鸟移动和停止,当然,我们忘记了注销事件。

internal sealed class SceneT14
: CommandScene
{
// ... private Bird bird;
private readonly Button goButton;
private readonly Button stopButton; internal SceneT14 ( )
: base ( Vector2.Zero, GestureType.None, "background1",
new Resource[] {
new Resource ( "bird2.image", ResourceType.Image, @"image\bird2" ),
new Resource ( "go.image", ResourceType.Image, @"image\button1" ),
new Resource ( "stop.image", ResourceType.Image, @"image\button2" ),
},
new Making[] {
new Movie ( "bird", "bird2.image", , , , "stop",
new MovieSequence ( "go", true, new Point ( , ), new Point ( , ) ),
new MovieSequence ( "stop", true, new Point ( , ) )
),
new Button ( "b.go", "go.image", "GO", new Vector2 ( , ), , , new Point ( , ) ),
new Button ( "b.play", "stop.image", "STOP", new Vector2 ( , ), , , new Point ( , ) )
}
)
{
this.goButton = this.makings[ "b.go" ] as Button;
this.stopButton = this.makings[ "b.play" ] as Button; this.goButton.Selected += this.goButtonSelected;
this.stopButton.Selected += this.stopButtonSelected;
} private void goButtonSelected ( object sender, ButtonEventArgs e )
{ this.bird.Go ( ); } private void stopButtonSelected ( object sender, ButtonEventArgs e )
{ this.bird.Stop ( ); } public override void LoadContent ( )
{
base.LoadContent ( ); this.bird = new Bird ( this, new Vector2 ( , ) );
this.bird.LoadContent ( ); this.world.Components.Add ( this.bird );
} public override void UnloadContent ( )
{
this.world.Components.Remove ( this.bird );
this.bird.Dispose ( ); base.UnloadContent ( );
} }

本期视频 http://v.youku.com/v_show/id_XNTgwOTE3NTky.html

项目地址 http://wp-xna.googlecode.com/
更多内容 WPXNA

平方开发的游戏 http://zoyobar.lofter.com/

QQ 群 213685539

欢迎访问我在其他位置发布的同一文章:http://www.wpgame.info/post/decc4_74291e

使用 Spirit 类在 XNA 中创建游戏中的基本单位精灵(十三)的更多相关文章

  1. 地图四叉树一般用在GIS中,在游戏寻路中2D游戏中一般用2维数组就够了

    地图四叉树一般用在GIS中,在游戏寻路中2D游戏中一般用2维数组就够了 四叉树对于区域查询,效率比较高. 原理图

  2. 3D游戏开发之在UE4中创建非玩家角色(NPC)

    接着上节我们继续学习,现在我们来创建一些NPC(non-playable characters,非玩家角色).在这个游戏中,当我们靠近NPC时,它们会做出相应的反应. 一 创建C++类 1) 在UE编 ...

  3. 使用 SpiritManager 类管理在 XNA 游戏中的精灵(十四)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  4. 使用 Button 类在 XNA 中创建图形按钮(九)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  5. 使用 Scene 类在 XNA 中创建不同的场景(八)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  6. 编写Java程序,创建Dota游戏中的防御塔类,通过两个坐属性显示防御塔所在的位置

    返回本章节 返回作业目录 需求说明: 创建Dota游戏中的防御塔类 通过两个坐属性显示防御塔所在的位置 实现思路: 创建防御塔(TowerDefense)类 在该类中定义了两个属性,分别是int类型横 ...

  7. 编写Java程序,创建Dota游戏中的兵营类,兵营类有一个类成员变量count、一个实例变量name和另一个实例变量selfCount。

    返回本章节 返回作业目录 需求说明: 创建Dota游戏中的兵营类 兵营类有一个类成员变量count.一个实例变量name和另一个实例变量selfCount. count表示的是兵营已经创建士兵的总数: ...

  8. 使用 CommandScene 类在 XNA 中创建命令场景(十二)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

  9. 使用 Anime 类在 XNA 中创建小动画(十一)

    平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...

随机推荐

  1. CentOS下内核TCP参数优化配置详解

    主动关闭的一方在发送最后一个ACK后就会进入TIME_WAIT状态,并停留2MSL(Max Segment LifeTime)时间,这个是TCP/IP必不可少的. TCP/IP的设计者如此设计,主要原 ...

  2. HDU 1009 FatMouse' Trade肥老鼠的交易(AC代码) 贪心法

    题意: 一只老鼠用猫粮来换豆子,每个房间的兑换率不同,所以得尽量从兑换率高的房间先兑换.肥老鼠准备M磅猫粮去跟猫交易,让猫在warehouse中帮他指路,以找到好吃的.warehouse有N个房间,第 ...

  3. IOS 自定义Layer(图层)

    方式1: @interface NJViewController () @end @implementation NJViewController - (void)viewDidLoad { [sup ...

  4. hdu-1198 Farm Irrigation---并查集+模拟(附测试数据)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1198 题目大意: 有如上图11种土地块,块中的绿色线条为土地块中修好的水渠,现在一片土地由上述的各种 ...

  5. python_50_函数与函数式编程

    import time def logger(): """追加写""" time_format='%Y-%m-%d %X'#年-月-日 小时 ...

  6. python_34_文件操作3

    f=open('yesterday',encoding='utf-8') print(f.tell())#文件句柄所在指针指向的位置,即光标在哪里(按字符计数) f.readline()#读一行 pr ...

  7. python_19_编码解码

    msg="我爱北京天安门" #字符串转成Byte类型 print(msg.encode())#encode 编码 print(msg.encode(encoding="u ...

  8. IPython安装过程 @win7 64bit

    http://www.360doc.com/content/14/0902/11/16740871_406476389.shtml 为了测验测验一下IPython的应用,今天折腾了好久的从安装包msi ...

  9. ethtool查看网卡以及修改网卡配置

    ethtool 命令详解 命令描述: ethtool 是用于查询及设置网卡参数的命令. 使用概要:ethtool ethx       //查询ethx网口基本设置,其中 x 是对应网卡的编号,如et ...

  10. JZOJ 5344. 摘果子

    Description Input Output Sample Input 7 9 39 6 13 2 22 6 7 4 -19 5 28 6 -17 1 2 1 3 2 4 1 5 4 6 2 7 ...