A闪的 BLOG 技术与人文
上篇中,我们讲工作模型中的题库环节进行详细设计并拆分,本节,我们继续后面的内容
在整个工作模型中,存在一个所谓“阅题”的环节。此环节设计如下: 阅题器会根据所输入的题目数据生成我们需要的游戏界面。但界面是什么样子的,针对不同数据格式的题目如何解析,需要我们通过“界面生成器”来完成。在抽象接口中,我们定义了ITopicReader(抽象阅读器)和ITopicRender(抽象渲染器)来完成这部分内容。与此同时,做了第一版实现。
抽象阅读器
/*
author: mebius
date: 2016.2.6
des:topic阅读器,用来渲染提示界面
*/
interface ITopicReader
{
setRender(render:ITopicRender):void; //设置渲染器
setRootLayer(layer:egret.DisplayObjectContainer):void; //设置根渲染器
getFrames():egret.DisplayObject; //获取当前帧
renderFrames(data:ITopic,auto:boolean):egret.DisplayObject; //渲染帧
dispose();
}
阅读器实现
/*
author: mebius
date: 2016.2.8
des:默认topic阅读器
*/
class BaseTopicReader implements ITopicReader {
private _render:ITopicRender;
private _rootLayer:egret.DisplayObjectContainer;
private _frames:egret.DisplayObject;
public setRender(render:ITopicRender):void
{
this._render = render;
}
public setRootLayer(layer:egret.DisplayObjectContainer):void
{
this._rootLayer = layer;
}
public getFrames():egret.DisplayObject
{
return this._frames;
}
public renderFrames(data:ITopic,auto:boolean):egret.DisplayObject
{
this._frames = this._render.render(data);
if(auto)
{
this._rootLayer.addChild( this._frames );
}
return this._frames;
}
public dispose()
{
this._frames = null;
this._render = null;
this._rootLayer = null;
}
}
抽象渲染器接口
/*
author: mebius
date: 2016.2.8
des:topic渲染器接口
*/
interface ITopicRender
{
render(data:ITopic):egret.DisplayObject; //渲染帧
}
渲染器实现
/*
author: mebius
date: 2016.2.8
des:topic渲染器
*/
class TopicRender implements ITopicRender
{
public render(data:ITopic):egret.DisplayObject
{
var obj:egret.DisplayObject;
//...
return obj;
}
}
最后的渲染器实现我并没有编写实际代码,在项目中,这部分与设计部分较差内容非常大。
当题目阅读步骤完成后,我们来看一下后续的步骤。
作答部分本身难以独立成为一个系统,因为会和我们的业务逻辑关联非常密切。所以用了一个松耦合的设计,采用消息机制方便其他模块通知我们的新手引导进行动作响应。 在Egret中,此部分可使用事件完成。
具体的实现方式,我们可以设定一个“消息中心”,通过消息中心去转发我们需要的信息。实现如下:
/*
author: mebius
date: 2016.2.8
des:topic 事件
*/
class TopicEvent extends egret.Event
{
public static TOPIC_COMPLETE:string = "topic_complete";
public id:number = 0;
public constructor(type:string, bubbles:boolean=false, cancelable:boolean=false, data?:any )
{
super(type, bubbles, cancelable, data );
}
}
/*
author: mebius
date: 2016.2.8
des:消息中心
*/
class MessageCeneter extends egret.EventDispatcher
{
private static _messageCenter:MessageCeneter;
private static _init:boolean = false;
public constructor()
{
super();
if( MessageCeneter._init == false )
{
throw( new Error("单例模式") );
}
}
public static getInstance():MessageCeneter
{
if( MessageCeneter._messageCenter == null )
{
MessageCeneter._init = true;
MessageCeneter._messageCenter = new MessageCeneter();
MessageCeneter._init = false;
}
return MessageCeneter._messageCenter;
}
public pushMessage(id:number):void
{
var evt:TopicEvent = new TopicEvent(TopicEvent.TOPIC_COMPLETE);
evt.id = id;
this.dispatchEvent(evt);
}
}
有了以上内容,我们可以设定新手引导的管理器,所一个统一管理,将一些必要功能封装起来。
/*
author: mebius
date: 2016.2.5
des:topic模块管理器,负责整体新手引导模型流程
*/
class TopicManage
{
private _topicParse:ITopicParse;
private _topicLib:ITopicLib;
public constructor(parse:ITopicParse)
{
this._topicLib = new BaseTopicLib();
this._topicParse = parse;
MessageCeneter.getInstance().addEventListener(TopicEvent.TOPIC_COMPLETE, this.topicComplete,this);
}
public setData(data:any):void
{
this._topicParse.parse(data);
this._topicLib.addTopics( this._topicParse.getTopics() );
this._topicParse.dispose();
}
//--
private _reader:ITopicReader;
public setRender(render:ITopicRender,rootLayer:egret.DisplayObjectContainer)
{
this._reader = new BaseTopicReader();
this._reader.setRender(render);
this._reader.setRootLayer(rootLayer);
}
//--
private _id:number = 0;
public getCurrentID():number
{
return this._id;
}
private topicComplete(evt:TopicEvent)
{
this._id++;
this._reader.renderFrames( this._topicLib.getTopicByID(this._id), true );
}
}
使用如下:
class Main extends egret.DisplayObjectContainer {
public constructor() {
super();
RES.getResByUrl("resource/topics.json",this.topicLoadComplete,this);
}
private topicLoadComplete(data:any)
{
var a:TopicManage = new TopicManage(new GameTopicParse());
a.setData(data);
}
}
到此为止,你的新手引导系统,已经完成绝大部分,但仍然需要不同的进行扩展重构,以应对变化的需求。
最后两个步骤我并没有进行代码编写,而是像前面一样,设计其基本业务模型,因为此部分随着业务发展,变化非常大,很难做一个“有用”的设计。
评分阶段 理论上,我们应该将此部分放置与服务器端实现。如果在本地实现此部分逻辑,那么需要注意你的分数数据需要进行封装。同时,在数据部分,也要响应增加此新手引导环节分数评判标准。生成的结果作为输出进入到下一个工作模型环节中。
记录阶段 对于网络游戏而言,此过程我们要进行I/O同步操作。但也可能该环节由服务器完成,将计算后结果传回客户端中。这取决于项目的具体功能需求。
总结:新手引导模块在游戏开发中属于最为简单的模块系统,但它也是最为繁琐的模块系统。当策划修改,甚至大量功能性改动的时候,会涉及到非常多的业务逻辑修改。我们能保证的是,尽量将业务抽象化,保留可变环节,以适应游戏开发过程中的不确定因素。