新手引导—脚本设计

最近在做游戏的新手引导模块。由于是关卡式的引导,所以很适合用读取引导脚本的方式来开发。

脚本和命令配置

程序中通过GuideLib这个类来解析命令配置和脚本。脚本以行为单位运行,每一行就是一条指令。每一条指令有指令的名称和指令的参数。就像下面这样。

goto step:+1
showArrow direction:down position:[200,300] clickRect:[100,30]
delayCall step:-1 time:2

第一个单词表示命令名称,然后后面的参数以空格分开,参数名称和参数值用冒号分开。由若干条命令就组成了一个完整的脚本。但是光有脚本还是不够,从脚本里面我们看不出来参数的类型。比如position应该是一个Number数组,所以我们还需要一个配置文件来说明每一条命令的详细细节。这里配置文件用xml表示。

<?xml version=”1.0” encoding=”UTF-8”?>
<commands>
<command name=”goto”>
<param name=”step” type=”String”/>
</command>
<command name=”delayCall”>
<param name=”step” type=”String”/>
<param name=”time” type=”Number”/>
</command>
<command name=”showArrow”>
<param name=”direction” type=”String”/>
<param name=”position” type=”Array_Number”/>
<param name=”clickRect” type=”Array_Number”/>
</command>
</commands>

可以看到param有一个type的属性用来说明参数类型。查看GuideLib的源代码会发现有一个ITypeAdapter,这个就是用来解析参数类型的,在解析脚本的时候,调用ITypeAdapter的convert方法自动将脚本中param的值由字符串转换为命令配置文件中的type。默认的类型解释器是TypeAdapter。

/**
 * 将命令参数的值转换成正确的类型
 */
public function convert(type:String , value:String):*
{
    var result:String = value;
    if(type == "String")
    {
        return value;
    }
    else if(type == "Boolean")
    {
        return value=="true"?true:false;
    }
    else if(type.indexOf("Array") == 0)
    {
        if(type.split("_",2).length&lt;2)
        {
            return convertStringToArray("String" , value);
        }
        else
        {
            return convertStringToArray(type.split("_")[1] , value);
        }
    }
    else
    {
        return getDefinitionByName(type)(value);
    }
}

执行命令

GuideLib将通过getScript将脚本解析成一个数组。每一个元素就是一条命令,然后交给GuideSystem去处理每一条命令。在GuideSystem中又可以看到一个ICommandAdapter。这个就是用来运行命令的解释器,调用run方法执行命令。默认的命令解释器是CommandAdapter。

/**
 * 默认的函数库
 */
private var guideFunction:GuideFunction = new GuideFunction();
/**
 * 运行命令
 * @param command 命令的名称
 * @param param 命令的参数
 * @param guideSystem 向导管理
 */
public function run(command:String, param:Object, guideSystem:GuideSystem):void
{
    var fun:Function;
    if(guideFunction.hasOwnProperty(command))  //函数库中存在这个函数
        fun = guideFunction[command];
    fun(param);
    if(guideSystem.auto)
        guideSystem.doNext();
}

GuideFunction就是默认的函数库,如果命令配置是调用其他的类的函数,可以自己实现这个run方法。

函数库的自定义

脚本中的每一条命令实际上就是调用的某一个类的某一个函数。默认情况下这个类就是GuideFunction。所以我们要实现功能,为GuideFunction添加方法就行了。方法名保存和命令名一致,参数就是命令的参数组成的Object。例如delayCall这个命令的实现

/**
 * 延时 多少秒执行哪一步
 */
public function delayCall(param:Object):void
{
    var step:String = param["step"];
    var time:Number = param["time"];
    GuideSystem.getInstance().auto = false;
    setTimeout(function():void{
        GuideSystem.getInstance().auto = true;
        GuideSystem.getInstance().goto(step);
    } , time*1000);
}

我们可以根据业务逻辑的不同,自由的定制参数类型解析器,命令函数解析器和函数库。

脚本编辑器

脚本编辑器是一个可视化的工具,能方便快速的创建脚本,你只需要先写好命令配置xml。编辑器会根据xml自动生成命令列表,然后向脚本中自由的加入命令。预览图:

新手引导示例:Guide

脚本编辑器:ScriptEditor