施用XWorker编写战斗模拟器
还是XWorker的介绍贴,XWorker是免费开源项目,下面是使用XWorker的数据对象技术开发的我们游戏项目的战斗模拟器,希望让大家了解XWorker中开发的方式。另外不建议目前用XWorker开发商业应用,后果自负,因为XWorker还不成熟,所以才希望有人能够加入XWorker的开发。
数据对象的使用说明:
主要是用于编写界面和数据统计,数据对象技术类似于信息化系统那种格式化的CUDI操作,只不过在XWorker数据对象主要用于一般编辑界面,即不限于数据库的操作,主要是对一般的表单、表格的等处理,如示例中的战斗数据的展示和统计等。
战斗模拟器的界面:
战斗模拟器的详细说明
1. 玩家和怪物界面
XWorker中的代码是:
<Group name="playerGroup" text="玩家"> <GridData style="FILL_HORIZONTAL" name="playerGroupGridData"/> <FillLayout name="FillLayout"/> <DataObjectForm formType="edit" name="playerForm" dataObject="tools:dataobjects.PlayerThingDataObject"/> </Group> <Group name="monsterGroup" text="怪物"> <FillLayout name="monsterGroup"/> <GridData style="FILL_HORIZONTAL" name="monsterGroupGridData"/> <DataObjectForm formType="edit" name="monsterForm" dataObject="tools:dataobjects.Monster"/> </Group>
以上代码生成了如下界面:
生成以上表单的依据是tools:dataobjects.PlayerThingDataObject和tools:dataobjects.Monster,如Monster的定义是:
<ListDataObject label="Monster" listData="npcList" editCols="4" storeSortDir="ASC" name="Monster" dataClassName="Npc"> <attribute type="int" propertyPath="id" label="标识" queryEditor="false" viewEditor="false" gridEditor="false" name="id" editEditor="false" createEditor="false"/> <attribute propertyPath="name" label="名称" inputtype="label" name="name"/> <attribute type="int" propertyPath="level" label="等级" name="level"/> <attribute type="int" propertyPath="hpMax" label="气血上限" name="hpMax"/> <attribute type="int" propertyPath="mpMax" label="内息上限" name="mpMax"/> <attribute type="short" propertyPath="attackMin" label="最小攻击" name="minAttack"/> <attribute type="short" propertyPath="attackMax" label="最大攻击" name="maxAttack"/> <attribute type="float" propertyPath="hitRateAdd" label="命中率" name="hitRate"/> <attribute type="float" propertyPath="hitRateBase" label="基础命中率" name="hitRateBase"/> <attribute type="float" propertyPath="hitRateAdd" label="额外命中率" name="hitRateAdd"/> <attribute type="float" propertyPath="avoRate" label="躲闪率" name="avoRate"/> <attribute type="float" propertyPath="criRate" label="暴击率" name="criRate"/> <attribute type="float" propertyPath="destRate" label="破击率" name="destroyRate"/> <attribute type="float" propertyPath="harmReduceRate" label="伤害减免率" name="harmReduce"/> <attribute type="float" propertyPath="criHarmRate" label="暴击伤害率" name="criHarmRate"/> <attribute type="float" propertyPath="desReduceRate" label="破击减免率" name="desReduceRate"/> <attribute type="float" propertyPath="hpAbsorbRate" label="气血吸收率" name="hpAbsorbRate"/> <attribute type="float" propertyPath="damageReboundRate" label="伤害反弹率" name="damageReboundRate"/> <attribute type="int" propertyPath="speed" label="速度" name="speed"/> <attribute type="int" propertyPath="skill.id" label="技能" inputattrs="tools:battle.SkillSelector:/@shell" inputtype="openWindow" name="skill"/> <attribute type="short" propertyPath="defence" label="防御" name="defence"/> </ListDataObject>
2. 设置和读取表单数据
表单生成后,填充表单数据和获取表单数据的方法。
a. 设置表单的数据
以下是按钮的代码:
<Composite name="buttonComposite"> <GridData horizontalAlignment="END" style="FILL_HORIZONTAL" name="buttonCompositeGridData"/> <RowLayout marginTop="0" name="buttonCompositeRowLayout"/> <Button name="selectPlayerBtn" text="选择角色"> <RowData name="RowData" width="80"/> <Listeners> <Listener name="selectPlayerBtnSelection"> <ShellDialog name="selectPlayer" shellPath="tools:battle.PlayerThingSelector:/@shell"> <Result condition="_return != null" name="ifOk"> <FormSetValues formName="playerForm" name="setPlayer" valueVariable="_return"/> </Result> </ShellDialog> </Listener> </Listeners> </Button> <Button name="selectMonsterBtn" text="选择怪物"> <RowData name="RowData" width="80"/> <Listeners> <Listener name="selectMonsterBtnSelection"> <ShellDialog name="selectMonster" shellPath="tools:battle.MonsterSelector:/@shell"> <Result condition="_return != null" name="ifOk"> <FormSetValues formName="monsterForm" name="setMonster" valueVariable="_return"/> <PropertySetter propertyName="monster" dataSource="actionContext.getScope(0)" name="setActionContext" value="_return"/> </Result> </ShellDialog> </Listener> </Listeners> </Button> <Button name="startBattleBtn" text="开始战斗"> <RowData name="RowData" width="80"/> <Listeners> <Listener name="startBattleBtnSelection"> <GroovyAction code="" name="startBattle"/> </Listener> </Listeners> </Button> <Button name="updatePlayerBtn" text="保存角色"> <RowData name="RowData" width="80"/> <Listeners> <Listener name="updatePlayerBtnSelection"> <EventExecutor exceptionMessage="更新角色失败。" name="executeEpdatePlayer" successMessage="更新角色成功。"> <actions name="actions"> <FormUpdateDataObject name="updatePlayer" formName="playerForm"/> </actions> </EventExecutor> </Listener> </Listeners> </Button> <Button name="createNewPlayerBtn" text="保存为新角色"> <RowData name="RowData" width="80"/> <Listeners> <Listener name="createNewPlayerBtnSelection"> <EventExecutor exceptionMessage="创建角色失败。" name="eventExecutor" successMessage="创建角色成功。"> <actions name="actions"> <FormCreateDataObject name="createPlayer" formName="playerForm"/> </actions> </EventExecutor> </Listener> </Listeners> </Button> </Composite>
对应的界面是:
我们使用选择怪物按钮来解释如何选择怪物并填充怪物表单,选择怪物的按钮的事件处理代码是:
<Listeners> <Listener name="selectMonsterBtnSelection"> <ShellDialog name="selectMonster" shellPath="tools:battle.MonsterSelector:/@shell"> <Result condition="_return != null" name="ifOk"> <FormSetValues formName="monsterForm" name="setMonster" valueVariable="_return"/> <PropertySetter propertyName="monster" dataSource="actionContext.getScope(0)" name="setActionContext" value="_return"/> </Result> </ShellDialog> </Listener> </Listeners>
分解讲解:
<ShellDialog name="selectMonster" shellPath="tools:battle.MonsterSelector:/@shell">
打开选择怪物的窗口tools:battle.MonsterSelector:/@shell。
<Result condition="_return != null" name="ifOk">
如果选择的怪物。
<FormSetValues formName="monsterForm" name="setMonster" valueVariable="_return"/>
把怪物数据填充到怪物表单。
<PropertySetter propertyName="monster" dataSource="actionContext.getScope(0)" name="setActionContext" value="_return"/>
把怪物数据设置到全局变量中,以备后用。
b. 获取表单数据的代码
def mvalues = monsterForm.doAction("getValues", actionContext);
以上是groovy的脚本代码,获取怪物表单中的编辑的数据。
3. 统计数据处理
以上是模拟战斗后的统计结果,实际上战斗过程是由游戏的代码完成的,XWorker负责把每次的战斗结果汇总并显示出来,在这里介绍如何使用数据对象的统计功能。
统计的代码如下:
//计算玩家的统计信息 def attackStaticDObj = world.getThing("tools:battle.AttackInfoStatic"); //玩家统计 def playerAttackStatic = attackStaticDObj.doAction("query", actionContext, ["attackDetails": playerAttackDetails]).get(0); //怪物统计 def monsterAttackStatic = attackStaticDObj.doAction("query", actionContext, ["attackDetails": monsterAttackDetails]).get(0);
playerAttackDetails和monsterAttackDetails是一次战斗的详细信息,是由游戏代码算出来的,attackStaticDObj是统计模块,做统计,
attackStaticDObj.doAction("query", actionContext, ["attackDetails": playerAttackDetails]).get(0);是执行统计并算出结果。
其中统计的定义是tools:battle.AttackInfoStatic,它的代码如下:
<StatisticsQueryDataObject dataObject="ResultDataObject" editCols="4" storeSortDir="ASC" name="ResultStat"> <attribute type="int" label="战斗次数" size="10" gridWidth="60" inputtype="label" aggregateExpression="*" name="totalCount" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="胜利次数" size="10" gridWidth="60" inputtype="label" aggregateExpression="result" name="winCount" queryFieldType="aggregate"/> <attribute type="float" aggregateFunction="avg" colspan="2" label="胜率" size="10" inputtype="label" aggregateExpression="result" name="winRate" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="总时间" size="10" inputtype="label" aggregateExpression="time" name="time" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="avg" label="平均战斗时间" size="10" inputtype="label" aggregateExpression="time" name="avgTime" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="avg" label="玩家平均攻击次数" size="10" inputtype="label" aggregateExpression="playerAttackCount" name="avgAttackCount" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="avg" label="怪物平均攻击次数" size="10" inputtype="label" aggregateExpression="monsterAttackCount" name="avgMonsterAttackCount" queryFieldType="aggregate"/> <attribute colspan="4" label="玩家统计数据" inputtype="label" name="playerData" queryFieldType="finalExpression" finalExpression=""""/> <attribute type="int" aggregateFunction="sum" label="攻击次数" size="10" inputtype="label" aggregateExpression="playerAttackCount" name="playerAttackCount" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="总伤害" size="10" inputtype="label" aggregateExpression="playerAttack" name="playerAttack" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="暴击次数" inputtype="label" aggregateExpression="playerCrickedCount" name="playerCrickedCount" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="破击次数" inputtype="label" aggregateExpression="playerDestroyedCount" name="playerDestroyedCount" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="命中次数" inputtype="label" aggregateExpression="playerHitCount" name="playerHitCount" queryFieldType="aggregate"/> <attribute type="int" label="平均战斗伤害" size="10" inputtype="label" name="avgPlayerBattleAttack" queryFieldType="finalExpression" finalExpression="playerAttack/totalCount"/> <attribute type="float" label="暴击率" inputtype="label" name="playerAvgCrickedCount" queryFieldType="finalExpression" finalExpression="playerCrickedCount/playerAttackCount"/> <attribute type="int" label="破击率" inputtype="label" name="playerAvgDestroyedCount" queryFieldType="finalExpression" finalExpression="playerDestroyedCount/playerAttackCount"/> <attribute type="float" label="命中率" inputtype="label" name="playerAvgHitRate" queryFieldType="finalExpression" finalExpression="playerHitCount/playerAttackCount"/> <attribute type="int" label="平均每次伤害" size="10" inputtype="label" name="playerAvgAttackDamage" queryFieldType="finalExpression" finalExpression="playerAttack/playerAttackCount"/> <attribute type="int" aggregateFunction="sum" label="内息消耗" size="10" inputtype="label" aggregateExpression="playerAttackerMpUsed" name="playerAttackerMpUsed" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="avg" label="平均战斗内息消耗" size="10" inputtype="label" aggregateExpression="playerAttackerMpUsed" name="avgPlayerAttackerMpUsed" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="气血消耗" size="10" inputtype="label" aggregateExpression="playerAttackerHpUsed" name="playerAttackerHpUsed" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="气血吸收" size="10" inputtype="label" aggregateExpression="playerHpAbsorb" name="playerHpAbsorb" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="内息吸收" size="10" inputtype="label" aggregateExpression="playerMpAbsorb" name="playerMpAbsorb" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="伤害反弹" size="10" inputtype="label" aggregateExpression="playerDamageRebound" name="playerDamageRebound" queryFieldType="aggregate"/> <attribute colspan="4" label="怪物统计数据" inputtype="label" name="monsterData" queryFieldType="finalExpression" finalExpression=""""/> <attribute type="int" aggregateFunction="sum" label="攻击次数" size="10" inputtype="label" aggregateExpression="monsterAttackCount" name="monsterAttackCount" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="总伤害" size="10" inputtype="label" aggregateExpression="monsterAttack" name="monsterAttack" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="暴击次数" inputtype="label" aggregateExpression="monsterCrickedCount" name="monsterCrickedCount" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="破击次数" inputtype="label" aggregateExpression="monsterDestroyedCount" name="monsterDestroyedCount" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="命中次数" inputtype="label" aggregateExpression="monsterHitCount" name="monsterHitCount" queryFieldType="aggregate"/> <attribute type="int" label="平均战斗伤害" size="10" inputtype="label" name="avgmonsterBattleAttack" queryFieldType="finalExpression" finalExpression="monsterAttack/totalCount"/> <attribute type="float" label="暴击率" inputtype="label" name="monsterAvgCrickedCount" queryFieldType="finalExpression" finalExpression="monsterCrickedCount/monsterAttackCount"/> <attribute type="int" label="破击率" inputtype="label" name="monsterAvgDestroyedCount" queryFieldType="finalExpression" finalExpression="monsterDestroyedCount/monsterAttackCount"/> <attribute type="float" label="命中率" inputtype="label" name="monsterAvgHitRate" queryFieldType="finalExpression" finalExpression="monsterHitCount/monsterAttackCount"/> <attribute type="int" label="平均每次伤害" size="10" inputtype="label" name="monsterAvgAttackDamage" queryFieldType="finalExpression" finalExpression="monsterAttack/monsterAttackCount"/> <attribute type="int" aggregateFunction="sum" label="内息消耗" size="10" inputtype="label" aggregateExpression="monsterAttackerMpUsed" name="monsterAttackerMpUsed" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="avg" label="平均战斗内息消耗" size="10" inputtype="label" aggregateExpression="monsterAttackerMpUsed" name="avgmonsterAttackerMpUsed" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="气血消耗" size="10" inputtype="label" aggregateExpression="monsterAttackerHpUsed" name="monsterAttackerHpUsed" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="气血吸收" size="10" inputtype="label" aggregateExpression="monsterHpAbsorb" name="monsterHpAbsorb" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="内息吸收" size="10" inputtype="label" aggregateExpression="monsterMpAbsorb" name="monsterMpAbsorb" queryFieldType="aggregate"/> <attribute type="int" aggregateFunction="sum" label="伤害反弹" size="10" inputtype="label" aggregateExpression="monsterDamageRebound" name="monsterDamageRebound" queryFieldType="aggregate"/> </StatisticsQueryDataObject>
StatisticsQueryDataObject是一个分组和汇总模块,可以对List数据或数组等分组和汇总。