首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >数据驱动(能力)系统

数据驱动(能力)系统
EN

Code Review用户
提问于 2016-03-23 14:49:41
回答 1查看 247关注 0票数 4

我正在为生物、能力、物品等制作一个数据驱动系统。考虑到我的能力对象,我需要从这些对象中创建“模板”,因为实时解析它们太慢了。我试图找到一种方法,只是引用这些在我的生物,而不是一个副本的参考每一种能力。

问题是,我在引用的能力对象中有可变的数据。这是因为XML数据可以包含数据,因此不存在字段。

有正常的攻击能力。

代码语言:javascript
复制
<ability name="Attack">
    <!-- Owners weapon range-->
    <range>Weapon</range>
    <!-- for ai FSM to find a proper thing to do.
    <type>damage</type>

    <!-- bitflags a attack needs a target so before this ability starts the player needs to select one trough GUI -->
    <behaviors>
        <behavior>CREATURE_TARGET</behavior>
        <behavior>ATTACK</behavior>
    </behaviors>

    <!-- bitflags a attack obviously needs a melee weapon
    If it would also contain AXE and TWO_HANDED  it would require a two handed axe to activate-->
    <requirements>
        <requirement>MELEE_WEAPON</requirement>
    </requirements>

    <!-- I have a turn/tickbased game -->
    <tickCost>WeaponSpeed</tickCost>
    <!-- basic stats, could also go into special parameters -->
    <cooldown>0</cooldown>
    <manaCost>0</manaCost>
    <energyCost>0</energyCost>
    <lifeCost>0</lifeCost>

    <!-- Actions associated with this ability -->
    <abilityActions>
        <!-- the event on which the containing actions are triggered -->
        <event type="OnAbilityActivated">
            <actions>
                <!-- the action itself, this creates a `new DamagaAction(..)`-->
                <action>                        
                    <class>Damage</class>
                    <damageType>PHYSICAL</damageType>
                    <target>TARGET</target>
                    <damage>Weapon</damage>
                </action>
            </actions>
        </event>
    </abilityActions>
</ability>

一种能力可以容纳你想要的多少行动。它可以抛出100个钉,其中每个穗将有一个不同的视觉和状态的效果。

现在,让我们想象一下,有人创造了一个有耐力的生物,没有什么可以保持耐力,所以我们需要额外的参数。想象一下,你想要创造一种能伤害这种新生物耐力的能力。

代码语言:javascript
复制
<parameter name="stamina_damage" value"10"></parameter>
                <action>                        
                    <class>Damage</class>
                    <damageType>PHYSICAL</damageType>
                    <target>TARGET</target>
                    <damage>stamina_damage</damage> <!-- parameter reference -->
                </action>

我还可以将显示信息存储到参数中,以显示有关此功能的适当信息。

实际代码

下面的代码是我目前试图做的一个清晰的例子。

代码语言:javascript
复制
public class Ability implements CreatureListener {    
    private final String name;//Not actually final since that would make my constructor too long with parsing everything. Considering on a builder pattern for this.
    //...

    //This gets changed and used by each creature that has this reference
    public HashMap<String, Object> parameters = new HashMap<>();

    public Ability(XmlNode abilityNode)
    {
         parseAbility(abilityNode);
    }

    public void startAbility(Creature owner)
    {
        parameters.put("OWNER", owner);
        //Perform needed actions, if they need the owner they can take it from parameters

        //foreach action that is tagged OnStartAbility -> action.perform();
    }

    @Override
    public void onAttack(Creature owner, Creature target) {
        parameters.put("OWNER", owner);
        parameters.put("TARGET", target);

        //Perform needed actions, if they need the owner or target they can take it from parameters

        //foreach action that is tagged OnAttack -> action.perform();
    }

    @Override
    public void onTakeDamage(Creature owner, Creature attacker, int damageDealt) {
        parameters.put("OWNER", owner);
        parameters.put("ATTACKER", attacker);
        parameters.put("DAMAGE_DEALT", damageDealt);

        //Perform needed actions, if they need the owner or attacker they can take it from parameters

        //foreach action that is tagged OnTakeDamage -> action.perform();
    }
}

每次运行某一技能时,都会重新设置可能的参数。考虑以下情况:

  1. 攻击生物进行AOE行动,并找到两个受害者。
  2. 攻击生物使用一个伤害动作来计算伤害并应用于victim1。
  3. Victem1触发onTakeDamage(..)它把自己设定为它的主人,攻击生物作为目标,它所受的伤害是DAMAGE_DEALT
  4. Victem1使用损害操作将DAMAGE_DEALT应用于攻击者。攻击生物使用伤害动作对victim2施加伤害。
  5. Victem2触发onTakeDamage(..)它把自己设定为它的主人,攻击生物作为目标,它所受的伤害是DAMAGE_DEALT
  6. Victem2使用损害操作将DAMAGE_DEALT应用于攻击者。

这在我看来是安全的,但真的吗?这似乎被认为是非常糟糕的做法。那我有什么选择?

编辑我创造了另一个能力,应该对单个目标造成伤害,并治愈附近的友军单位的成功。它应该看起来像这样,我尽我最大的努力来记录它。

代码语言:javascript
复制
<ability name="DefineSubtraction">
            <range>use_range</range> <!-- corrsponds to parameter -->
            <AI><!-- for AI to decide what to do -->
                <abilityType>
                    <type>SINGLE_DAMAGE</type>
                    <type>MULTIPLE_HEAL</type>
                </abilityType>
            </AI>

            <!-- The behavior of the skill -->
            <behaviors>
                <behavior>CREATURE_TARGET</behavior> <!-- Creature need to select a target on activation -->
                <behavior>MAGIC</behavior> <!-- Cannot target magic imune and does not trigger PhysicalDamageTaken -->
                <!-- More things like NO_TARGET, COORDINATE_TARGET, PASSIVE, SUSTAINED can be added and they act like bits in a bitset -->
            </behaviors>

            <requirements><!-- Requirements to use this ability also creates a bitset from the combinations -->
                <requirement>SPELL_BOOK</requirement> <!-- Need a spell book -->
                <!-- Could have many requirements like AXE + TWO_HANDED would require 2 handed axe and MELEE_WEAPON + SINGLE_HANDED requires single handed melee weapon -->
            </requirements>

            <!-- The parameters, this actually is the thing I'm worried about. As you can see I need this here, especially when modders want to introduce new creatures with different stats and all.
             It will also hold additional information to show information about the ability. I also need this to add additional data at runtime, like when I need current data of the owner. -->
            <parameters>
                <parameter type="int">
                    <name>use_range</name>
                    <value>10</value>
                </parameter>
                <parameter type="int">
                    <name>spell_damage</name>
                    <value>20</value>
                </parameter>
                <parameter type="int">
                    <name>heal_radius</name>
                    <value>200</value>
                </parameter>
            </parameters>

            <!-- Basic stats, not really sure what I exactly need. I probably put all this in parameters eventually -->
            <tickCost>WeaponSpeed</tickCost>
            <cooldown>0</cooldown>
            <manaCost>0</manaCost>
            <energyCost>0</energyCost>
            <lifeCost>0</lifeCost>

            <!-- The actual action blocks for the ability start here.
             For each phase you can add abilities, there are many phases like:
             OnDamageTaken, OnAbilitySuccess, OnDeath, OnEquip, etc, etc. -->
            <abilityActions>
                <event type="OnAbilityActivated"><!-- Actions that trigger when ability is activated -->
                    <actions>
                        <!-- The actions in order of execution. Actions can force everything to stop until finished or just run in one go by default.
                         Actions are essentially classes that get instantiated for this ability. So these are hardcoded and run by the ability, the actions
                         handle everything what happens in the game world. I could even hook actions up to map objects. Some actions are: Damage, Projectile,
                          PlaySound, Move, Effect, AOE and many more. But just with those 4 I can make most abilities of any game. I keep them very basic
                          Damage just does damage and might trigger onDamageTaken. AOE just fills a list with participants for the next action in line to use.-->
                        <action>
                            <!-- regular damage action -->
                            <class>Damage</class>
                            <!-- Damage type -->
                            <damageType>MAGICAL</damageType>
                            <!-- The target, since the behavior is CREATURE_TARGET I know it has been filled when ability is activated -->
                            <target>TARGET</target>
                            <!-- amount of damage from parameters -->
                            <damage>Spell_Damage</damage>
                        </action>
                    </actions>
                </event>

                <event type="OnAbilitySuccess"><!-- Actions that trigger when a initial ability was successfully, for example landing a attack although OnAttackLanded is another Phase that can be triggered  -->
                    <actions>
                        <!-- here I queue two actions up, I can essentially create hundreds of actions here that all behave differently. -->
                        <action>
                            <!-- puts all creatures found in the parameters -->
                            <class>AOE</class>
                            <!-- bitset to determine what creatures to add -->
                            <target_type>
                                <type>FRIENDLY</type>
                                <type>NEUTRAL</type>
                                <type>ALLIED</type>
                            </target_type>
                            <!-- point from where to draw circle of influence -->
                            <origin>OWNER_ORIGIN</origin>
                            <!-- radius of circle of influence -->
                            <radius>heal_radius</radius>
                        </action>

                        <action>
                            <!-- action that heals -->
                            <class>Heal</class>
                            <!-- Target, since AOE object always stores crteature in map as "TARGETS", creatureSet I can just put TARGETS here-->
                            <target>TARGETS</target>
                            <!-- Amount to be healed, from parameters -->
                            <damage>HealAmount</damage>
                        </action>
                    </actions>
                </event>
            </abilityActions>
        </ability>

正如你所看到的,这个系统是非常通用的,我想我可以让它和那个HashMap<String, Object> parameters一起工作,我的原型至少工作得很好,但它也很基本。

EN

回答 1

Code Review用户

发布于 2016-03-23 16:46:58

代码语言:javascript
复制
public interface OneOnOneAction<S extends Creature, T extends Creature, P> {
  public void perform(S sourceCreature, T targetCreature, P parameter);
}


public interface Creature {
  // ...
}

public interface StaminaCreature extends Creature {
  public int getStamina();
  public void setStamina(int stamina);
}


public class TransferStaminaAction implements OneOnOneAction<StaminaCreature, StaminaCreature, Integer> {

  public void perform(StaminaCreature sourceCreature, StaminaCreature targetCreature, Integer parameter) {
    sourceCreature.setStamina( sourceCreature.setStamina() - parameter);
    targetCreature.setStamina( targetCreature.getStamina() + parameter );
  }

}

您可以使用反射来构建生物、操作&c。

代码语言:javascript
复制
<action id="transferStaminaAction" class="my.game.TransferStaminaAction"/>
<creature id="staminaMan" class="my.game.StaminaManCreature">
  <actions>
    <actionref id="transferStaminaAction"/>
  </actions>
</creature>

这只是一个例子,可以说明如何将行为参数化,而不必发明一种完整的配置语言。

然而,我认为我们没有足够的信息来提供更多有用的建议。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/123660

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档