表驱动

在做简单2D RPG游戏时,可能会遇到NPC与玩家对话的情况。

如何判定NPC能否和玩家对话,一般有两个条件:
1. NPC在玩家附近(地图中相邻的格子中)
2. NPC和玩家面对面

通常会写如下代码:

 1 enum SpriteDirection
 2 {
 3     UP,
 4     DOWN,
 5     LEFT,
 6     RIGHT,
 7 }
 8 
 9 public bool CanTalk(NPC npc)
10 {
11     return Nearest(npc) && FaceToFace(npc);
12 }
13 
14 public bool Nearest(npc)
15 {
16     //省略, 通常直接使用地图的二维数组判断
17 }
18 
19 public bool FaceToFace(NPC npc)
20 {
21     if(this.Direction == SpriteDirection.UP && npc.Direction == SpriteDirection.DOWN)
22     {
23         return true;
24     }
25     if(this.Direction == SpriteDirection.DOWN && npc.Direction == SpriteDirection.UP)
26     {
27         return true;
28     }
29     if(this.Direction == SpriteDirection.LEFT && npc.Direction == SpriteDirection.RIGHT)
30     {
31         return true;
32     }
33     if(this.Direction == SpriteDirection.RIGHT && npc.Direction == SpriteDirection.LEFT)
34     {
35         return true;
36     }
37     return false;
38 }

我们看看FaceToFace函数虽然明了,但是写得实在是长了点,其实我们可以利用一点手段进行简化:

public bool FaceToFace(NPC npc)
{
    int []direction = new int[4] = {-1,1,-2,2};
    return direction[(int)this.Direction] + direction[(int)npc.Direction] == 0;
}

其实当时没知道这叫表驱动的时候就想到这种写法了(当时很自豪0m0),但是在可读性却比原先函数低,就看各位如何取舍了。

含有就是FaceToFace和Nearest函数可以写在一齐,一步到位:

首先我们要知道下地图提供的API

class Map
{
    public int GetRow();                        // 返回整个地图的行数
    public int GetColumn();                        // 返回整个地图的列数
    public int GetIndex(int row, int col);                 // 根据二维数组(坐标)返回一维数组的索引
    public void GetRowColumn(int index, ref int row, ref int col);    // 根据一维数组索引返回二维数组坐标
    public int GetSpriteIndex(Sprite sprite);             // Sprite 派生出 玩家类 和NPC类
}
// 玩家类
public bool CanTalk(NPC npc)
{
    int []direction = new int[4]{-map.GetColumn(), map.GetColumn(), -1, 1};
    int myIndex = map.GetSpriteIndex(this);
    int npcIndex = map.GetSpriteIndex(npc);
    return myIndex + direction[(int)this.Direction] == npcIndex &&
        npcIndex + direction[(int)npc.Direction] == myIndex;
    // 当然还有越界问题需要处理, 例如npc在上一行最右,你在本行最左等问题,只要他们的行列相减的绝对值再相加等于1就行了.
}

 再举一个有关数学的例子,如图:

AB是一线段,长度不定(大于0),从AB中截取长度为10的点AC,如果AB长度<10,则是延长AB到AC。
其中AB与粉线交点为X(也有 没有交点的情况),点P到AB的垂点为P'(也有 没有垂点的情况),求AC、AX、AP'三条线段中最短的一条。(P点会移动,粉红色线也会移动)

很多时候会求出AX的距离,然后判断是否小于10,是则替换点,再是AP'与最短点比较。。。(讲的比较模糊,直接上代码)

vec3f D; // 与A距离最近点
vec3f A;
vec3f B;

vec3f C = B-A;
C.SetLength(10); // 先设置长度为10
C += A; //还原成图中C点

D = C; //先默认设置为C为比较主体

vec3f X;
if(粉线与AB有交点(ref X)) // 如果检测到有交点,则修改X为交点坐标
{
    if(Distance(A,D) > Distance(A,X)) //Distance是求两点距离函数
    {
        D = X;
    }
}

vec3f PP;
if(P点与AB有垂点(ref PP))
{
    if(Distance(A,D) > Distance(A,PP))
    {
        D = PP;
    }
}

return D;

上面代码中直接默认AC 10 为相比较主体。其实可以发现AC、AP'、AX 三个点是同等关系,是不分上下的,用表驱动可以写成是如下形式(没有相比较主体):

vec3f A;
vec3f B;

bool []hit = new bool[3]; //是否有焦点
vec3f []pos = new vec3f[3]; 

hit[0] = true;
pos[0] = B-A;
pos[0].SetLength(10);
pos[0] += A;

hit[1] = 粉线与AB有交点(ref pos[1]);
hit[2] = P点与AB有垂点(ref pos[2]);

vec3f D; // 与A距离最近点
bool h = false;
for(int i=0;i<3;++i)
{
    if(!h && hit[i])
    {
        h = true;
        D = pos[i];
    }
    
    if(hit[i])
    {
        if(Distance(A,D) > Distance(A,pos[i]))
        {
            D = pos[i];
        }
    }
}
Assert(h);
return D;

虽然下面的代码比较长,但是我还是比较喜欢下面这种方法~


以前的空间日志:

表驱动:

人类阅读复杂数据结构远比复杂的控制流程容易,或者说数据驱动开发是非常有价值的。《代码大全2》声称这个是表驱动法。[参考:http://dennis-zane.javaeye.com/blog/183886]

[参考:http://www.cnblogs.com/shinn/archive/2008/04/16/1157141.html]

//如果if else语句嵌套到都不知道最里面的if代表什么的时候,switch长到很牛*的时候就可以用表查询了。

//表驱动法的本质是时间与空间的转换,即利用空间来换取时间,博主完全可以再深入的分析什么时候用表驱动,什么时候不应该用,怎么用才最好等等.

//当你发现,大量重复的代码写到你想要问候别人长辈的时候,就可以考虑使用表驱动法了。

简单例子:

 String _1 = "星期一";
 String _2 = "星期二";
 String _3 = "星期三";
 String _4 = "星期四";
 String _5 = "星期五";
 String _6 = "星期六";
 String _7 = "星期日";
 
 String GetDay(int day){

  if(day == 0){
   return _1;
  }
  if(day == 1){
   return _2;
  }
  //....
  throw new RuntimeException();
 }

/////////////////////////////////////////////////////////////////

//改进后:

 char[] days = "一二三四五六日".toCharArray();
 String GetDay(int day){
  return "星期"+days[day-1];
 }

 其他参考:“表驱动”那点事儿。。。 http://www.cnblogs.com/lijian2010/archive/2010/12/16/1908374.html

原文地址:https://www.cnblogs.com/godzza/p/3001165.html