C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(二十) 第一部分拓展小结篇
写了20节,一路向追着鬼子打一样都没停过,索性也想暂时休息一下整理整理思绪好完成后面的第二部分更为精彩的内容:诸如跟随式地图移动模式、NPC & 怪物 与主角的互动、对象AI、攻击与魔法、各种类型伤害计算、完美的RPG游戏界面……等等等等,激动吗?讲实话:我很激动!
读者声音:还没写就开始激动了,典型的傻子。
^_^||言归正传,本节就先来个承上启下的的小结吧,我打算分4个部分对前20节内容进行补充拓展:
一、完美的改进型A*寻路移动模式
在C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(十九) 完美精灵之八面玲珑(WPF Only)③中,我们虽然实现了精灵的全方向与动作,但是细心的朋友就会发现,精灵在走路的时候一直使用着A*;这将导致两个问题:1、性能上的损失,每次移动不管中间是否有障碍物都启动寻路算法,造成资源的白白浪费;2、在C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(十二)神奇的副本地图 的结尾我曾轻描淡写的叙述了如何实现改进型A*,虽然通过副本地图简单实现了,但是暂时并不完美。那么,下面我将向大家讲解通过地地道道的方法实现改进型完美A*移动模式。
何谓改进型完美A*移动模式?即主角每次移动时,首先并不启动A*寻路而是直接建立两点间的直线移动;接下来即进行时时的障碍物判断,如果没有碰撞到任何障碍物或对象则将该直线移动保持到终点;但是中途一旦碰到障碍物,则以目的地为终点即时启动A*寻路。
原理很简单,关键技术就是如何对碰撞进行检测?
传统的方法有两种:
第一种我且称之为坐标还原法:即时时记录精灵未碰撞障碍物时的坐标(Old_X,Old_Y),在精灵移动时一旦检测到精灵此时站到了障碍物上,则将精灵此时的坐标进行还原(X=Old_X,Y= Old_Y),然后启动A*寻路。此方法的优点是使用简单,不需要复杂的判断逻辑;缺点是效果不好,在画面上将造成精灵一瞬间被弹开的情况,虽然那一刻非常的短暂且距离微小,但是对于精灵移动动画平滑性的影响是严重的,因此我们最好不要采用此方法。
第二种为启发式预测法:该方法的原理为时时对精灵前方的区域进行预测,一旦发现前方有障碍物,则即时启动A*寻路直到目的地。该方法可谓绝对皇室血统,一个字“正”,集所有优点之大成者;优点多相对的实现起来难度就大些。在WPF/Silverlight中如何实现之?先来看下图:
上图中已经给了很详细的说明,即在直线移动过程中,精灵时时判断此时朝向前方的单元格是否为障碍物,如果是则启动A*寻路饶过它。充分理解了原理后,我们可以通过如下方法来返回精灵是否将要遇到障碍物了:
//判断是否将要碰撞到障碍物(障碍物预测法)
private bool WillCollide() {
switch ((int)Spirit.Direction) {
case 0:
return Matrix[(int)(Spirit.X / GridSize), (int)(Spirit.Y / GridSize) - 1] == 0 ? true : false;
case 1:
return Matrix[(int)(Spirit.X / GridSize) + 1, (int)(Spirit.Y / GridSize) - 1] == 0 ? true : false;
case 2:
return Matrix[(int)(Spirit.X / GridSize) + 1, (int)(Spirit.Y / GridSize)] == 0 ? true : false;
case 3:
return Matrix[(int)(Spirit.X / GridSize) + 1, (int)(Spirit.Y / GridSize) + 1] == 0 ? true : false;
case 4:
return Matrix[(int)(Spirit.X / GridSize), (int)(Spirit.Y / GridSize) + 1] == 0 ? true : false;
case 5:
return Matrix[(int)(Spirit.X / GridSize) - 1, (int)(Spirit.Y / GridSize) + 1] == 0 ? true : false;
case 6:
return Matrix[(int)(Spirit.X / GridSize) - 1, (int)(Spirit.Y / GridSize)] == 0 ? true : false;
case 7:
return Matrix[(int)(Spirit.X / GridSize) - 1, (int)(Spirit.Y / GridSize) - 1] == 0 ? true : false;
default:
return true;
}
}