游戏中的新手引导模块开发 中

书接上文,我们要开始对模型进行细节设计,并编写代码。所有代码使用Egret做示例。

注意:所有模块对外都应该为黑盒,数据需要一进一出。

将新手引导环节抽象为一个考试模型,有利于我们定义整个新手引导过程,即出题与奖励的过程。在整个工作模型中,再将不同环节进行拆分,每个环节可以进行扩展自定义,保证我们工作模型不变的情况下,能够适应更多的需求。

1、题库部分

问题:

题目如何定义? 题目从何而来? 题目如何存储?

我们将其流程进行细分,最终得到下面的模型。 7.png 转换为代码,我们可以做如下抽象。

在HTML5中,数据源可以使用JSON数据格式进行外部配置,这也是我们推荐的方式,而JSON将被解析为一个Object对象。根据需求得变更,我们的数据内容也会发生变化。所以要为这部分数据接口保留可扩展性。

题目作为最原子元素需要进行扩展,所以,我们允许进行继承扩展操作。我们创建题目的接口,实际实现,根据需求来进行扩展。

/*
author: mebius
date:   2016.2.5

des:topic数据接口

*/

interface ITopic
{
    id:number;   //当前topic的唯一ID编号
    type:number; //当前topic类型
}

这里我们先设计一个暂定的数据格式,json内容如下:

{
    "topic":[
        {"id":"0","type":"tips","ui":"0","text":"这是新手引导的提示功能1"},
        {"id":"1","type":"tips","ui":"0","text":"这是新手引导的提示功能2"},
        {"id":"2","type":"action","ui":"1","text":"开启功能,领取奖励","award":["2","2","3","4","5","1"]}
    ]
}

假如要对这个数据进行解析,我们需要实现ITopic接口。类如下:

/*
author: mebius
date:   2016.2.8

des:topic数据
    type类型包含如下2种:
    "tips":纯提示
    "action":需要执行多做

*/

enum TopicType
{
    tips,action
}

class GameTopic implements ITopic
{
    private _id:number = 0;
    private _type:number = 0;

    public get id():number
    {
        return this._id;
    }
    public get type():number
    {
        return this._type;
    }

    public constructor(id:number,type:TopicType)
    {
        this._id = id;
        this._type = type;
    }

    private _ui:number = 0;
    public setUI(ui:number)
    {
        this._ui = ui;
    }
    public get ui():number
    {
        return this._ui;
    }

    private _text:string="";
    public setText(str:string)
    {
        this._text = str;
    }
    public get text():string
    {
        return this._text;
    }

    private _award:number[];
    private _awardNum:number[];
    private _awardsNum:number = 0;
    public setAward(award:number[])
    {
        //console.log(award);
        this._award = [];
        this._awardNum = [];
        if( award.length%2 == 1 )
        {
            throw( new Error("奖励数据错误!") );
        }
        else
        {
            this._awardsNum = award.length/2;
            for(var i=0;i<this._awardsNum;i++)
            {
                this._award.push( award[i]*2 );
                this._awardNum.push( award[i]*2+1 );
            }
        }
    }
    public get awardNum():number
    {
        return this._awardsNum;
    }
    public getAwardByIndex(index:number):number
    {
        return this._award[index];
    }
    public getAwardNumByIndex(index:number):number
    {
        return this._awardNum[index];
    }
}

解析器需要留有数据输入与输出的接口,但对于不同数据格式,我们需要进行扩展,所以,解析器需要进行接口设计,与实现进行分离。我们先定义解析器接口ITopicParse。代码如下:

/*
author: mebius
date:   2016.2.5

des:当前接口定义了一个topic数据解析器所必须的动作

*/

interface ITopicParse
{
    parse(data:any):void;  //解析数据
    getTopics():ITopic[];  //获取所有topic
    dispose():void;        //处理内存资源
}

针对我们上面的例子,解析器实现如下:

/*
author: mebius
date:   2016.2.8

des:topic数据解析器

*/

class GameTopicParse implements ITopicParse
{

    private _topics:ITopic[];

    public parse(data:any):void
    {
        this._topics = [];
        var len:number = data.topic.length;
        for(var i:number=0;i<len;i++)
        {
            var t:any = data.topic[i];
            var topic:GameTopic;
            switch( t.type )
            {
                case "tips":
                    topic = new GameTopic( t.id, TopicType.tips );
                    break;
                case "action":
                    topic = new GameTopic( t.id, TopicType.action );
                    topic.setAward(t.award);
                    break;
            }
            topic.setUI(t.ui);
            topic.setText(t.text);
            this._topics.push(topic);
        }
    }

    public getTopics():ITopic[]
    {
        return this._topics;
    }

    public dispose():void
    {
        this._topics = null;
    }
}

最终的题目会直接进入道题库,但题库的变化较小,如需要扩展,我们允许继承扩展操作。目前我们还没有遇到一些特殊需求,所以,定义了题目录接口ITopicLib后,我们编写它的实现BaseTopicLib。

/*
author: mebius
date:   2016.2.5

des:topic库接口

*/

interface ITopicLib
{
    addTopics(topics:ITopic[]):void;  //添加topic
    getTopicByID(id:number):ITopic;   //通过ID获取topic
    dispose():void;                   //清除内存资源
}

实现类

/*
author: mebius
date:   2016.2.5

des:基本topic库

*/

class BaseTopicLib implements ITopicLib
{
    private _topics:ITopic[];

    public constructor()
    {
        this._topics = [];
    }

    public addTopics(topics:ITopic[]):void
    {
        if( this._topics )
        {
            this._topics = topics;
        }
        else
        {
            throw(new Error("BaseTopicLib中_topics为null") );
        }
    }

    /*
     * 通过ID获取topic
     * 如果ID无匹配项,则返回null
    */
    public getTopicByID(id:number):ITopic
    {
        if( this._topics )
        {
            var len:number = this._topics.length;
            for(var i:number=0;i<len;i++)
            {
                if(this._topics[i].id == id)
                {
                    return this._topics[i];
                }
            }
            return null;
        }
        else
        {
            throw(new Error("BaseTopicLib中_topics为null") );
        }
    }

    public dispose():void
    {
        this._topics = null;
    }
}

上面所讲的内容,是根据设计的工作模型所编写代码。后面,我们讲解后续的模型拆解方法。