使用 NPC,NPCManager 在 XNA 中创建 NPC(十九)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛。在这里分享一下经验,仅为了和各位朋友交流经验。平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXNA 吧,最后请高手绕道而行吧,以免浪费时间。(为了突出重点和减少篇幅,有些示例代码可能不够严谨。)
NPC
NPC 是游戏中重要的内容,也就是非玩家控制单位。所以,平方创建了 NPC 类和其他相关的类。下面是 NPC 类的一些字段。
静态字段 InjuredSoundName 和 DeadSoundName 表示 NPC 受伤的声音和死亡的声音。由于使用静态字段,所以所有的 NPC 都将使用此声音。
事件 Injured 表示 NPC 受伤之后,你可以在这个事件中让 NPC 显示受伤的效果。
字段 frameCount 用来计算时间,将根据 frameCount 来执行动作。
internal static string InjuredSoundName;
internal static string DeadSoundName; internal event EventHandler<NPCEventArgs> Injured; private long frameCount = ; protected readonly List<NPCAction> actions = new List<NPCAction> ( );
protected readonly List<NPCAction> loopActions = new List<NPCAction> ( );
private int currentActionIndex;
protected int life;
protected int protoLife;
internal bool IsDied = false; internal NPCManager Manager; protected NPC ( IPlayScene scene, int type, Vector2 location, string movieName, string extendMovieName, float speed, int angle, HitArea hitArea, int width, int height, int life, IList<NPCAction> actions, double destroySecond, bool isMovieRotable, bool isAreaLimited, bool isAreaEntered, double areaSecond )
: base ( scene, type, location, movieName, extendMovieName, speed, angle, hitArea, width, height, destroySecond, isMovieRotable, isAreaLimited, isAreaEntered, areaSecond )
{ this.life = life <= ? : life;
this.protoLife = this.life; if ( null != actions )
this.actions.AddRange ( actions ); this.currentActionIndex = ;
}字段 actions,loopActions,currentActionIndex 都和动作相关。actions 和 loopActions 用来存放动作,currentActionIndex 表示当前动作的索引。
字段 life 表示 NPC 当前的生命值,字段 protoLife 表示 NPC 生命的原始值。
字段 IsDied 表示 NPC 是否已经死亡。字段 Manager 表示 NPC 管理器。
继承自 NPC 的类可以修改方法 execute,这样就可以完成他们的自定义动作,比如:敌人的战斗机在 1 秒后发射子弹。
在 updating 方法中,我们将会判断动作的执行时间,如果可以则执行这些动作,并判断这些动作是否可以重复执行,如果可以则将动作添加到 loopActions 字段中。
protected virtual void execute ( NPCAction action )
{ } protected override void updating ( GameTime time )
{
this.frameCount++; foreach ( NPCAction action in this.loopActions.ToArray() )
if ( action.FrameIndex <= this.frameCount )
{
this.execute ( action ); if ( !action.Next ( ) )
this.loopActions.Remove ( action ); } for ( int index = this.currentActionIndex; index < this.actions.Count; index++ )
{
NPCAction action = this.actions[ index ]; if ( action.FrameIndex > this.frameCount )
break;
else
{
this.execute ( action ); if ( action.IsLoop )
{
this.loopActions.Add ( action );
action.Next ( );
} this.currentActionIndex++;
} } base.updating ( time );
}方法 Injure 将扣除 NPC 的生命值,如果生命值小于等于 0,那么我们将播放 NPC 死亡的电影,但是并不会调用 Destroy 方法,而是在 movieEnded 方法中判断死亡电影是否播放完毕,在播放完毕后我们才会调用 Destroy 方法。
派生类可以修改 dying 方法来添加一些代码,这些代码将在 NPC 死亡前执行。
protected override void movieEnded ( object sender, MovieEventArgs e )
{ if ( e.SequenceName == "dead" )
this.Destroy ( ); } protected virtual void dying ( )
{ } public virtual bool Injure ( int life, int type )
{ if ( this.IsDied )
return true; this.scene.AudioManager.PlaySound ( InjuredSoundName ); int injuredLife; if ( this.life < life )
injuredLife = this.life;
else
injuredLife = life; this.life -= injuredLife; if ( null != this.Injured )
this.Injured ( this, new NPCEventArgs ( this, injuredLife, type ) ); if ( this.life <= )
{
this.scene.AudioManager.PlaySound ( DeadSoundName ); this.IsDied = true;
this.PlayMovie ( "dead" ); this.dying ( );
}
else
this.PlayExtendMovie ( "flash" ); return this.life <= ;
}
NPC 管理器
NPCManager 类派生自类 SpiritManager<T>。
internal class NPCManager
: SpiritManager<NPC>
{
protected long frameCount = ; internal event EventHandler<SpiritEventArgs> Destroyed; internal event EventHandler<NPCEventArgs> Injured; internal NPCManager ( )
: base ( )
{ } protected override void spiritDestroyed ( object sender, SpiritEventArgs e )
{ if ( null != this.Destroyed )
this.Destroyed ( sender, e ); NPC npc = sender as NPC;
npc.Injured -= this.Injured;
npc.Manager = null;
base.spiritDestroyed ( sender, e );
} internal override void Append ( NPC spirit, int order )
{ spirit.Manager = this;
spirit.Injured += this.Injured;
base.Append ( spirit, order );
} internal override void Update ( GameTime time )
{
this.frameCount++;
} internal List<NPC> HitTest ( HitArea area )
{
List<NPC> npcs = new List<NPC> ( ); foreach ( NPC npc in this.Spirits )
if ( !npc.IsDied && area.HitTest ( npc.HitArea ) )
npcs.Add ( npc ); return npcs;
} internal List<NPC[]> HitTest ( )
{
List<NPC[]> npcs = new List<NPC[]> ( ); for ( int index1 = ; index1 < this.Spirits.Count; index1++ )
{
NPC npc1 = this.Spirits[ index1 ]; if ( npc1.IsDied )
continue; for ( int index2 = index1 + ; index2 < this.Spirits.Count; index2++ )
{
NPC npc2 = this.Spirits[ index2 ]; if ( !npc2.IsDied && npc1.HitArea.HitTest ( npc2.HitArea ) )
npcs.Add ( new NPC[] { npc1, npc2 } ); } } return npcs;
} internal void SetReelSpeed ( DirectionType direction, float speed )
{ foreach ( NPC npc in this.Spirits )
npc.SetReelSpeed ( direction, speed ); } }事件 Destroyed 和 Injured 用来通知外界 NPC 已经被摧毁或者受伤,你可以在这些事件中为玩家加分。
方法 spiritDestroyed 会在 NPC 被摧毁后执行,在这个方法中,我们移除了 NPC 的 Injured 事件,而在基类中会移除 Destroyed 事件。
第一个 HitTest 方法接收了一个 HitArea 类型的参数 area,我们将判断哪些 NPC 和 area 发生了碰撞,然后将这些 NPC 作为列表返回。而第二个 HitTest 方法,我们测试互相碰撞的 NPC 并返回,比如:在竞速类游戏中,我们需要检测车辆之间的碰撞。
方法 SetReelSpeed 用来设置所有 NPC 的卷轴速度。
NPC 动作
每一个 NPC 在被创建之后,都可能会改变自己当前的状态。因此,平方创建了类 NPCAction,他包含了一些信息,这些信息说明了如何改变 NPC 的状态。
有时候要完成一个动作还需要其他的类,所以使用字段 Type 来说明动作的类型。字段 frameIndex,IsLoop,loopedCount,intervalFrameCount 用来说明动作是否可以被重复执行,第一次执行的时间,以及频率,执行次数。
当动作执行一次之后,将调用 Next 方法来获取下一次的执行时间,或者判断是否可以继续执行。
internal abstract class NPCAction
{ internal readonly int Type; private long frameIndex;
internal long FrameIndex
{
get { return this.frameIndex; }
} internal readonly bool IsLoop; private readonly int loopCount; private int loopedCount = ;
internal int LoopedCount
{
get { return this.loopedCount; }
} private readonly long intervalFrameCount; protected NPCAction ( NPCAction action )
{
this.Type = action.Type; this.frameIndex = action.frameIndex; this.IsLoop = action.IsLoop;
this.loopCount = action.loopCount;
this.intervalFrameCount = action.intervalFrameCount;
}
protected NPCAction ( int type, float second, float intervalSecond, int loopCount )
{
this.Type = type; this.frameIndex = World.ToFrameCount ( second ); this.IsLoop = intervalSecond > ;
this.loopCount = loopCount;
this.intervalFrameCount = World.ToFrameCount ( intervalSecond );
} internal virtual bool Next ( )
{ if ( !this.IsLoop || ( this.loopCount > && this.loopedCount >= this.loopCount ) )
return false; this.frameIndex += this.intervalFrameCount;
this.loopedCount++;
return true;
} internal abstract NPCAction Clone ( ); }
示例
场景 SceneT20 是 SceneT19 的扩展,除了有 SceneT19 的功能,还将创建一些 NPC,这些 NPC 将自动旋转。
类 MyNPC 继承自 NPC,方法 execute 可以用来执行自定义动作,也就是 MyNPCAction。类 MyNPCAction 的 OffsetAngle 字段表示旋转角度。
internal class MyNPC
: NPC
{ internal MyNPC ( IPlayScene scene, Vector2 location, int angle, IList<NPCAction> actions )
: base ( scene, , location,
"mynpc", null,
3f, angle,
new SingleRectangleHitArea ( new Rectangle ( -, -, , ) ),
, ,
,
actions,
, true, true, true, )
{ this.isMoving = true; } protected override void execute ( NPCAction action )
{
MyNPCAction myAction = action as MyNPCAction; this.Angle += myAction.OffsetAngle;
} protected override void updateSpeed ( )
{
this.xSpeed = Calculator.Cos ( this.angle ) * this.speed;
this.ySpeed = Calculator.Sin ( this.angle ) * this.speed; base.updateSpeed ( );
} protected override void move ( )
{
this.Location.X += this.xSpeed;
this.Location.Y += this.ySpeed;
} } internal class MyNPCAction
: NPCAction
{
internal readonly int OffsetAngle; internal MyNPCAction ( MyNPCAction action )
: base ( action )
{
this.OffsetAngle = action.OffsetAngle;
}
internal MyNPCAction ( int offsetAngle )
: base ( , 0f, 0.5f, )
{ this.OffsetAngle = offsetAngle; } internal override NPCAction Clone ( )
{ return new MyNPCAction ( this ); } }接下来,我们为 SceneT20 增加了一个 NPCManager。
private NPCManager npcManager;最后,我们在 goButtonSelected 方法增加了代码,创建一个新的 MyNPC。
private void goButtonSelected ( object sender, ButtonEventArgs e )
{
this.bulletManager.Append ( new MyBullet ( this, new Vector2 ( , ), ) );
this.itemManager.Append ( new MyItem ( this, new Vector2 ( , ), ) ); this.npcManager.Append ( new MyNPC ( this, new Vector2 ( , ), ,
new NPCAction[] {
new MyNPCAction ( )
}
) );
}
本期视频 http://v.youku.com/v_show/id_XNjAxNDczMTIw.html
项目地址 http://wp-xna.googlecode.com/
更多内容 WPXNA
平方开发的游戏 http://zoyobar.lofter.com/
QQ 群 213685539
欢迎访问我在其他位置发布的同一文章:http://www.wpgame.info/post/decc4_858ce9
使用 NPC,NPCManager 在 XNA 中创建 NPC(十九)的更多相关文章
- 使用 NPC,NPCManager 在 XNA 中创建 NPC
使用 NPC,NPCManager 在 XNA 中创建 NPC 平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐 ...
- 使用 Region,RegionManager 在 XNA 中创建特殊区域(十八)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
- 使用 Pinup,PinupManager 在 XNA 中创建贴图(十七)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
- 使用 Item,ItemManager 在 XNA 中创建物品和道具(十六)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
- 使用 Bullet,BulletManager 在 XNA 中创建子弹攻击目标(十五)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
- 使用 Spirit 类在 XNA 中创建游戏中的基本单位精灵(十三)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
- 使用 Button 类在 XNA 中创建图形按钮(九)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
- 使用 Scene 类在 XNA 中创建不同的场景(八)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
- 使用 CommandScene 类在 XNA 中创建命令场景(十二)
平方已经开发了一些 Windows Phone 上的一些游戏,算不上什么技术大牛.在这里分享一下经验,仅为了和各位朋友交流经验.平方会逐步将自己编写的类上传到托管项目中,没有什么好名字,就叫 WPXN ...
随机推荐
- 编译安装PHP-7.1.8
安装依赖包: 1.安装yasm cd /usr/local/src tar zxvf yasm-1.3.0.tar.gz cd yasm-1.3.0 ./configure make make ins ...
- Array负载均衡控制器(vAPV)
平台: freebsd 类型: 虚拟机镜像 软件包: apache python basic software load balance network infrastructure slb ssl ...
- POJ-1149 PIGS---最大流+建图
题目链接: https://vjudge.net/problem/POJ-1149 题目大意: M个猪圈,N个顾客,每个顾客有一些的猪圈的钥匙,只能购买这些有钥匙的猪圈里的猪,而且要买一定数量的猪,每 ...
- Android(java)学习笔记99:Java虚拟机和Dalvik虚拟机的区别
Google于2007年底正式发布了Android SDK, 作为 Android系统的重要特性,Dalvik虚拟机也第一次进入了人们的视野.它对内存的高效使用,和在低速CPU上表现出的高性能,确实令 ...
- linux slab学习
https://blog.csdn.net/bullbat/article/details/7194794 https://blog.csdn.net/qq_26626709/article/deta ...
- 学习 KMP 算法
KMP 算法是用来处理字符串匹配问题的.也就是给你两个字符串,你需要回答:B 串是否是 A 串的子串(或 B 串在 A 串中出现的位置).比如,字符串 A = “ i am student ”, 字符 ...
- SAP 日志管理
现在项目上自开发的dialog程序越来越多,有很多敏感数据需要像SAP标准的业务一样,能看到所有的修改日志,要想实现日志的功能,有以下几个办法: 办法一.建一个日志表,在原有表的基础上,加上日期和时间 ...
- Vue入门之HelloWorld
对于新学习一门技术,一门语言比如JAVA.Python等都是从编写Hello World开始的,这样一来有益于初学者的人门,并给予初学者一定的信心,所以我也从HelloWorld开始讲起. 干货: 对 ...
- Java - 类加载的时候,是有缺省同步锁的
类加载的时候,是有缺省同步锁的
- struts2、hibernate的知识点
以下内容是我在复习struts2.hibernate和spring的时候记下得到,部分书上找不到的内容来自网络 以下是网络部分的原文网站: http://blog.csdn.net/frankaqi/ ...