我打算做的战棋是 tilebased,也就是以格子为单位的游戏形式。核心概念就在于一格一格,比较容易算清楚,和策略性挺搭。逻辑上,可以理解成一个 tile 是一个点,用二维数组的下标对应内容的话,索引会比较快,用起来方便。(斜45度角那种「2.5d」,技术上是2d不过tile是菱形的,渲染的时候做一个坐标轴转换再贴图就行了)
我第一件要思考的事情,就是,对于打算做的战棋游戏,一个tile应该做哪些事情(一个东西要做哪些事情这个问题也会在后续聊component抽象的时候聊到)有哪些数据?这些数据又应该以什么形式存放?
1.
应该有哪些数据:
这些数据都是根据游戏设计来的,看的是游戏中地图单元格的玩法内容,通常主要包括:
tile 自己的名字;
移动力消耗(不同职业在这一个tile都可能会会有不同的消耗,因此会用数组来记录对应的消耗);
对 tile 上的 character 属性的修改(对不同职业,也会有不同的生效情况);
地图渲染相关的图片信息,可能还包含了动画信息。
2.
应该以什么形式存放:
tile 信息本身可以用 struct——
struct 可以把相关的数据打包成一个值,这样在设计发生变化的时候,只需要变化struct的内容,就能对全局有效。
比如,对于移动力消耗,不同的兵种(步兵,骑兵,飞行等)会有不同的移动力消耗,把兵种+消耗作为一个 struct 打包起来很方便!
如果不采用 struct,原本的写法(现在是……代码时间!):
/// <summary>
/// 一个单元格,这里只有移动相关的局部信息
/// </summary>
public struct Tile
{
public int flyCost; //飞行经过这个单元格的消耗
public int footCost; //走路经过这个单元格的消耗
}
现在,用 struct 可以写成:
/// <summary>
/// 每个移动方式消耗的移动力
/// </summary>
public struct MovementInfo
{
public MoveType moveType; //移动方式
public int moveCost; //移动力
}
/// <summary>
/// 单元格数据
/// </summary>
public struct Tile
{
public List<MovementInfo> costs; //各种移动方式的消耗
}
这里为什么要用 struct:
尽管这看起来与 Dictionary 相似,但是 Dictionary 是 KeyValue 形式,并不适合扩展出更多的东西;即使有偏方扩展,也并非是好的手段——
例如,现在需要每个移动方式除了消耗移动力之外,还有一个hp的消耗,经过单元格会消耗 hp:
那么在使用 struct 时,可以扩展这个 MovementInfo:
/// <summary>
/// 每个移动方式消耗的移动力
/// </summary>
public struct MovementInfo
{
public MoveType moveType; //移动方式
public int moveCost; //移动力
public int hpCost; //hp消耗
}
当然,尽管(使用打补丁的行为)可以把 MoveType 作为 key,其余的作为 Value建立一个 Dictionary(从实际含义上想想这种抽象就有点怪……我也说不清楚但是怪怪的),
/// <summary>
/// 每个移动方式消耗的移动力
/// </summary>
public struct Tile
{
public Dictionary<MoveType, MovementInfo> costs; //移动力消耗
}
但这也不意味着可以拆掉 MovementInfo,从中移除掉 MoveType,因为MovementInfo 这个结构还会被用于别处,比如描述一个单位的移动力(大概也说明这种抽象是更合理的……挖个坑下一篇或者下下篇讨论一下「抽象」的话题):
/// <summary>
/// 一个单位的信息
/// </summary>
public struct BattleUnit
{
public MovementInfo movement; //单位的移动力,比如 步行5点、飞行7点 等,它是由移动方式、移动力构成的
}
把兵种+消耗作为一个 struct 打包之后,对于每个地形,根据设计给出各个兵种的移动力消耗,存成一个 list 就好啦。

另外,对地形上方角色属性的修改,存成一个 struct 也合适:
/// <summary>
/// 角色属性结构
/// </summary>
public struct CharacterStatus
{
public int attack; //攻击力属性
public int defense; //防御力属性
public int dodge; //闪避能力属性
}
用它作为角色属性的数据结构,那么一个角色的属性、装备提供的属性、技能影响的属性等,都是用这个struct,一旦设计发生变化,比如要增加一个属性criticalRate,我们就只需要改变这个结构,而不必到各个模块改动。
那,今天先写到这里……?关于tile,还差「地图渲染相关的图片信息」,但是这部分似乎要和地图在一起说比较好喔。
争取明天更新!!!!!

本文原载于P10的公众号