AOE这个词的意思,我相信玩过WOW的人都不陌生,包括玩过LoL的也不会陌生,说穿了就是一个区域内发生效果(Area of effect)。这里我们要讨论的就是关于一个适合于几乎所有游戏的AOE机制,当然之所以能适合于所有游戏,因为它也只是一个机制,一个机制从策划语法来就是一种思路,而不是一套代码。

<ignore_js_op>

  在这里我可能会用到一些类似DSL的东西,至于DSL是什么,别去百度,那里你得不到答案,这里有一个云风的帖你可以看一下,至于DSL的好坏,我觉得如果你的程序员有一定经验和水平,是不难看出的,怎么说呢,这就好比HTML和SQL的关系,SQL很好用,但是如何让一个HTML程序员用……传送门http://blog.codingnow.com/2012/01/dev_note_8.html

1,AOE和AOEObj

  首先要说清的是AOE和AOEObj的区别,这其实本质上是一个Struct和一个Instance的区别,AOE更多的是来自表项的数据,当然拜读了DSL概念之后,我觉得这个表项也可以写成DSL;而AOEObj则是工作的实体,有程序在运行过程中临时创建删除。

2,AOE通用表项

  这里我用的DSL是一个混合了Pascal Lua和Haxe的写法。

  1. Type AOE{
  2. id:Int=0; //id,不解释。
  3. area:Area; //之所以这里特别定义一个叫做Area的Struct,因为你在不同的项目中的区域概念会不同,简单举例,如果你是个LoL类型的游戏,他可能会需要扇形、圆形等等任何形状;而如果是一个战棋SLG,他可能只需要一个单元个坐标的数组Array<Point>就可以了。
  4. sightEffect:String=""; //在区域的中心播放的视觉特效,如果文件不存在则不需要播放。
  5. tick:Float; //多久发作一次效果,Float用于计算秒作为单位,而如果是一个回合制游戏,如我叫MT,完全可以用Int,因为它是回合作为单位,如果这个数字小于等于0,那么他将不会发作。
  6. onGuyEnter:*AOEObj->*Character->[Void]; //这里是一个函数(haxe),用于在有角色进入AOE区域时候的回调,第一个参数是AOEObj指针(我定义*XXX代表XXX的指针),第二个参数是进入区域的角色指针,返回值(中括号内)为空。可以为空,空时不执行效果。
  7. onGuyLeave:*AOEObj->*Character->[Void]; //角色离开AOE区域的回调函数。
  8. onTick:*AOEObj->Array<*Character>->[Void]; //区域自身每一跳的回调,和上面两个函数一样可以是空的,如果为空不论tick写了多少都没意义,因为都不执行。
  9. }

复制代码

我们可以看到,以上这个数据结构是任何类型游戏的AOE系统所必备的,可以说是基础的东西,当然这不代表AOE只能有这些信息,这还是根据游戏的具体需要进行具体的设计。

3,AOEObj及其工作原理

  1. Type AOEObj{
  2. aoe:*AOE;  //这是来自于那个AOE模板的,为了范围,我们总得给于一个AOE模板,这里有一个争议就是是否需要通过继承手段来解决相近两个相似区域的问题,我认为这个区域应当是策划认真设计的,所以没有必要,当然另一方面来说,上面我们提到的Area这个东西,其实本质上并没有说明他不可以是通过一个函数return的,所以……
  3. caster:*Character; //制造这个AOE的负责人,这当然可以是空的,比如通过GM命令创建了一个AOE,或者某个地形机关创建的AOE,你并不在意他的负责人是谁。
  4. casterInfoRecord:ChaProperty;//注意者并不是一个指针,这是一个clone体,我们需要知道这个AOE的创造者再创造AOE时候的属性,即使没有创造者,我们也应该new一个假的数据出来。因为一些AOE的效果的确需要用到这类属性,比如说我们需要暴风雨魔法,对区域内的人每3秒造成一次相当于Magic属性30%的伤害。
  5. lastTime:Float; //剩余多少时间后自然消亡,当然用在回合制游戏可以理解为还有多少个回合后消失,看到这里如果你足够有灵性会想到,我们是否有些AOE要在消失的时候干些什么特别的,好主意!那就再Type AOE中加上onRemove之类的东西就好了,你的项目你做主。
  6. }

复制代码

一个最最基础的AOEObj诞生了,在整个游戏中,我们需要在程序中加入一些时间点去调用AOE的回调函数,通常会有:

1)AOEObj产生时,调用AOEObj.aoe.onCreate(AOEObj),如果策划有设计这个的话,其实我觉得大多时候是不需要的。
2)当角色进入AOE时,调用AOEObj.aoe.onGuyEnter(AOEObj, guy),这里涉及到一个遍历,优化关键之一。
3)当角色离开AOE时,调用AOEObj.aoe.onGuyLeave(AOEObj, guy),同上。
4)当aoe的tick>0同时onTick并不是空的时候,我们要定时触发onTick(AOEObj, guysInAOE),当然这里有人提出了聪明的做法,就是角色进入AOE给他添加一个Buff,离开时移除这个Buff,这个可以视情况决定,诸如AOE的ontick是当偶数tick冒一下红色火焰,奇数tick冒一下绿色火焰的时候,这个想法根本行不通。而且是用自身的tick精确度会更高,比如一块地刺钉板,每5秒伸出地刺伤害区域内的人,这时候你用onTick的触发就会更精确的反应,而不会因为进入钉板区域的人进入时间不统一而产生麻烦。

4,策划对于这个机制的运用

  看到这里,有人会觉得这么设计AOE既浪费又不好理解,这是当然,我还是那句话——当你用战斗机只是杀1、2人的时候,他是不如砍刀实惠的,如果你的策划心中的AOE只有那些战斗效果,什么暴风雨魔法,剑刃风暴,那这简直就是浪费这套机制——“剑本凡铁,因执拿而通灵”,这关键还是看策划有没有灵性。

  最简单的例子,我们在游戏中有个buff叫冒火,每3秒对角色造成一次火焰伤害,可以堆叠20层,默认上10层,这时候我们地图上有2块区域,一个油坑和一片河流,其实我们地图上有3个AOE区域:

1)油坑:玩家进入后,如果身上有冒火,则会每秒中叠加1层冒火。
2)浅水区:如果玩家进入后身上有冒火和翻滚2个buff,那么就删除所有冒火的debuff。
3)深水区:玩家进入后直接消除身上所有的冒火debuff。

  以上是这3个区域的逻辑效果,但事实上你还需要利用好buff机制和AOE机制,去把细节做到位,比如冒火的buff在buffOccur时判断层数,不同的层数会导致视觉特效不同,或会随层数增加越来越旺;而水的AOE在消除火时会根据冒火的层数来播放不同程度的烟。

  有人又要说了“啊呀,谁在乎这些细节啊,我把项目做出来就好了就能赚钱了”,我想起一个故事——落潮的时候,沙滩上都是鱼,一个孩子一条一条地捡起沙滩上的濒死小鱼,把它们重新放进大海。有人对孩子说:“沙滩上那么多的小鱼,你捡得过来吗?一条小鱼而已,有谁会在乎呢?”孩子一边不停地往海里扔鱼,一边说:“你看,这一条在乎,这一条也在乎。”,谁在乎呢?其实每一条鱼都在乎,只是他们没有能力自己回到水里罢了。

【转】AOE机制的DSL及其实际运用的更多相关文章

  1. Java GC回收机制

    优秀Java程序员必须了解的GC工作原理 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只 ...

  2. Atitit. 提升软件开发效率and 开发质量---java 实现dsl 4gl 的本质and 精髓 O725

    Atitit. 提升软件开发效率and 开发质量---java 实现dsl 4gl 的本质and 精髓  O725 1. DSL主要分为三类:外部DSL.内部DSL,以及语言工作台. 1 2. DSL ...

  3. ios 消息传递机制

    引用文章 一.KVO 1.当对象中的某个属性值发生了改变,可以对这些值的观察者做出通知. 2.接受者(会接收到值发生改变的消息) 必须知道发送者(值将发生改变的那个对象). 3.接收者同样还需要知道发 ...

  4. 简述 Ruby 与 DSL 在 iOS 开发中的运用

    阅读本文不需要预先掌握 Ruby 与 DSL 相关的知识 何为 DSL DSL(Domain Specific Language) 翻译成中文就是:"领域特定语言".首先,从定义就 ...

  5. [2017-08-25]100行CSharp代码利用dynamic写个DSL(特定领域语言)

    最近看<CLR via C#(第4版)> 读到第五章末尾dynamic基元类型时,看了下作者的一个利用dynamic动态调用string类型的Contains方法(静态方法)的实现,突然发 ...

  6. ElasticSearch(二) 关于DSL

    关于Lucene里面的查询评分,其实是基于一个公式:TF/ IDF(Term-Frequency/ Inverse Document Frequency),词频率/ 倒排文档频率,这个公式讲了一个故事 ...

  7. 大数据框架对比:Hadoop、Storm、Samza、Spark和Flink--容错机制(ACK,RDD,基于log和状态快照),消息处理at least once,exactly once两个是关键

    分布式流处理是对无边界数据集进行连续不断的处理.聚合和分析.它跟MapReduce一样是一种通用计算,但我们期望延迟在毫秒或者秒级别.这类系统一般采用有向无环图(DAG). DAG是任务链的图形化表示 ...

  8. 【转】DSL

    DSL DSL 时不时地会成为一个话题,所以今天想专门说一下. DSL 也就是 Domain Specific Language 的简称,是指为某些特定领域(domain)设计的专用语言.举个例子,L ...

  9. 白话 Ruby 与 DSL 以及在 iOS 开发中的运用

    每日一篇优秀博文 2017年10月7日 周六 白话 Ruby 与 DSL 以及在 iOS 开发中的运用 阅读本文不需要预先掌握 Ruby 与 DSL 相关的知识 何为 DSL DSL(Domain S ...

随机推荐

  1. .net core 实践笔记(二)--EF连接Azure Sql

    ** 温馨提示:如需转载本文,请注明内容出处.** 本文链接:https://www.cnblogs.com/grom/p/9902098.html 笔者使用了常见的三层架构,Api展示层注入了Swa ...

  2. (Oracle)DBMS_SYSTEM工具-01[20180510]

    分析描述:     使用DBMS_SYSTEM分析SQL语句执行,并且获取SQL会话中的绑定变量.等待事件.消耗资源和执行计划等等. 环境介绍:     Oracle 11.2.0.4.0     L ...

  3. python2与python3的input函数的区别

    Python3.x 中 input() 函数接受一个标准输入数据,返回为 string 类型. Python2.x 中 input() 相等于 eval(raw_input(prompt)) ,用来获 ...

  4. K8S学习心得 == 创建容器influxdb的RC和SVC

    附上:YAML在线检查:http://nodeca.github.io/js-yaml Part 1: 部署Influxdb的RC文件,步骤如下: step 1: influxdb-rc.yaml文件 ...

  5. 在js内生成PDF文件并下载的功能实现(不调用后端),以及生成pdf时换行的格式不被渲染,word-break:break-all

    在js内生成PDF文件并下载的功能实现(不调用后端),以及生成pdf时换行的格式不被渲染,word-break:break-all 前天来了个新需求, 有一个授权书的文件要点击下载, 需要在前端生成, ...

  6. MongoDB 数据类型查询 — $type使用

    MongoDB 使用过程中经常需要根据字段的类型来查询数据, 而MongoDB中查询字段类型是通过$type操作符来实现. $type使用法语: db.集合名.find({$type:类型值}); / ...

  7. Delphi RAD Server 应用服务基础平台

    RAD Server是一个应用服务框架平台,可快速构建和部署应用服务.RAD Server提供自动化的Delphi和C++ REST/ JSON API的 发布与管理.企业数据库集成中间件.智能物联网 ...

  8. 调试日志——基于stm32的智能声光报警器(二)

    今天调试了音频部分的播放功能,这里我采用的是输出pwm来播放声音的方式. 声音的源文件是一个wav文件,然后用一个软件将wav文件转化为一个数组的文件. 要播放的文件就是这个数组文件里面的数据部分,前 ...

  9. Python学习:13.Python正则表达式

    一.正则表达式简介 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式. 就其本质 ...

  10. PHP 审计

    1.get  和post  上传数组,数组的sha1值相等 通过阅读代码,我们发现要想得到flag就要达到下面三个条件: 使 uname的sha1值 与 passwd的sha1的值 相等    但是同 ...