面向对象设计 - 法术 [英] Object-Oriented design - Spells

查看:87
本文介绍了面向对象设计 - 法术的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的第一个Java项目上工作,这是一个基本的角色扮演游戏。现在我处理法术,我需要一些OOD指导。

I work on my first Java project, which is a basic roleplaying game. Now I work on spells, and I need some OOD guidance.

我有字符,这是抽象类字符有一些子类(例如 mage 战斗机 rogue cleric )。

I have Character, which is an abstract class. Character has some subclasses (like mage, fighter, rogue, cleric).

Mage cleric (和<$ c一样$ c>现在,牧师没有法术力,但它可能会改变)都是施法者。

Mage and cleric(as for now, cleric doesn't have mana, but it might change) are both spell-casters.

我还有一个拼写类,有一些信息(比如拼写名称法术力费用等)。 MageSpellsList ClericSpellsList 是另一个类,都有Spell类列表。我还有效果类(施放法术应该使用它)。

I also have a Spell class, with some info (like spell name, mana cost etc). MageSpellsList and ClericSpellsList are another classes and both have lists of class Spell. and I also have Effects class(casting a spell should use it).

处理法术的面向对象设计是什么(解决方案不应包括效果类,我可以稍后处理)?

What would be a good object oriented design for dealing with spells (the solution shouldn't include Effects class, I can deal with that later) ?

也许使用SpellCaster界面和一些方法,比如castSpell和showSpellbook,那么Mage和Cleric会实现这个界面吗? 。

Maybe using a "SpellCaster" interface with some methods like castSpell and showSpellbook, so Mage and Cleric will implement the interface? .

也许MageSpellsList和ClericSpellsList应该是Spell的子类?我的目标是使用castSpell(spell name here)并让castSpell通过使用一个好的OOD来完成这项工作,而不是为每个法术编写一个特定的方法(并且在mage和Cleric之间没有重复的代码)

Maybe MageSpellsList and ClericSpellsList should be a subclass of Spell ? My goal is to use castSpell("spell name here") and let castSpell do the job, by using a good OOD, rather than writing a specific method for each spell (and without duplicate code between mage and Cleric)

Mage.java:

Mage.java:

public class Mage extends Character {

    private List<Spell> spellBook;
    private int mana;
    private int CurrentMana;

    public Mage(String name) {

        super(name);

        setName(name);
        setCharacterClass("Mage");
        setLevel(1);
        setHitDice(4);

        setStrength(10);
        setConstitution(10);
        setDexterity(14);
        setIntelligence(16);
        setWisdom(14);
        setCharisma(10);

        setHp((int) (4 + getModifier(getConstitution())));
        setCurrentHp(getHp());
        setArmorClass(10 + getModifier(getDexterity()));
        setBaseAttackBonus(0);

        setMana(20 + 2 * getModifier(getIntelligence()));
        setCurrentMana(getMana());
        spellBook = new ArrayList<Spell>();

    }

    public int getMana() {
        return mana;
    }

    public int getCurrentMana() {
        return CurrentMana;
    }

    protected void setMana(int mna) {
        mana = mna;
    }

    protected void setCurrentMana(int CurrMana) {
        CurrentMana = CurrMana;
    }

    public void showSpellBook() {

        for (Iterator<Spell> iter = spellBook.iterator(); iter.hasNext(); ) {
            Spell spell = iter.next();
            System.out.println("Spell name: " + spell.getSpellName());
            System.out.println("Spell effect: " + spell.getEffect());
        }
    }

    public void addToSpellBook(String spellName) {

        Spell newSpell;
        newSpell = MageSpellsList.getSpell(spellName);
        spellBook.add(newSpell);
        System.out.println(newSpell.getSpellName() + " has been added to the spellbook");

    }


    public void chooseSpells() {
        System.out.println();
    }

    void castSpell(String spellName, Character hero, Character target) {
        try {
            Spell spell = MageSpellsList.getSpell(spellName);
            System.out.println("You casted: " + spellName);
            System.out.println("Spell effect: " + spell.getEffect());
        } catch (Exception e) {
            System.out.println("No such spell");
        }
    }
}

Spell.java:

Spell.java:

public class Spell {
    private String name;
    private int spellLevel;
    private String effect;
    private int manaCost;
    private int duration;

    Spell(String name, int spellLevel, String effect, int manaCost, int duration) {
        this.name = name;
        this.spellLevel = spellLevel;
        this.effect = effect;
        this.manaCost = manaCost;
        this.duration= duration;
    }

    String getSpellName() { return name; }

    int getSpellLevel() { return spellLevel; }

    String getEffect() { return effect; }

    int getManaCost() {
        return manaCost;
    }

    int getDuration() { return  duration; }
}

MageSpellsList.java:

MageSpellsList.java:

public class MageSpellsList {
    static List<Spell> MageSpellsList = new ArrayList<Spell>();

    static {
        MageSpellsList.add(new Spell("Magic Missiles", 1, "damage", 2, 0));
        MageSpellsList.add(new Spell("Magic Armor", 1, "changeStat", 2, 0));
        MageSpellsList.add(new Spell("Scorching Ray ", 2, "damage", 4, 0));
        MageSpellsList.add(new Spell("Fireball", 3, "damage", 5,0 ));
        MageSpellsList.add(new Spell("Ice Storm", 4, "damage", 8, 0));
    }

    static void  showSpellsOfLevel(int spellLevel) {
        try {
            for (Iterator<Spell> iter = MageSpellsList.iterator(); iter.hasNext(); ) {
                Spell spell = iter.next();
                if (spellLevel == spell.getSpellLevel()) {
                    System.out.println("Spell name: " + spell.getSpellName());
                    System.out.println("Spell effect: " + spell.getEffect());
                }
            }
        } catch (Exception e){
            System.out.println("Epells of level " + spellLevel + " haven't been found in spells-list");
        }
    }

    static Spell getSpell(String spellName) {
        try {
            for (Iterator<Spell> iter = MageSpellsList.iterator(); iter.hasNext(); ) {
                Spell spell = iter.next();
                if (spellName.equals(spell.getSpellName())) {
                    return spell;
                }
            }
        } catch (Exception e){
            System.out.println(spellName + " haven't been found in spells-list");
            return null;
        }
        return null;
    }
}

Effects.java:

Effects.java:

public class Effects {

    public  void  damage(int dice, Character attacker, Character target){

        int damage = DiceRoller.roll(dice);
        System.out.println(attacker.getName() + " dealt " + damage + " damage to " + target.getName());
        target.setCurrentHp(target.getCurrentHp() - damage);
    }

    public static void damage(int n, int dice, int bonus, Character target) {

        int damage = DiceRoller.roll(n,dice,bonus);
        System.out.println("You dealt" + damage + "damage to " + target.getName());
        target.setCurrentHp(target.getCurrentHp() - damage);
    }

    public static void heal(int n, int dice, int bonus, Character target) {

        int heal = DiceRoller.roll(n,dice,bonus);
        if (heal + target.getCurrentHp() >= target.getHp()) {
            target.setCurrentHp(target.getHp());
        } else {
            target.setCurrentHp(target.getCurrentHp() + heal);
        }

        System.out.println("You healed" + heal + " hit points!");
    }

    public static void changeStat(String stat, int mod, Character target){

        System.out.println(stat + " + " + mod);

        switch (stat) {
            case "strength":
                target.setStrength(target.getStrength() + mod);
                break;
            case "constitution":
                target.setConstitution(target.getConstitution() + mod);
                break;
            case "dexterity":
                target.setDexterity(target.getDexterity() + mod);
                break;
            case "intelligence":
                target.setIntelligence(target.getIntelligence() + mod);
                break;
            case "wisdom":
                target.setWisdom(target.getWisdom() + mod);
                break;
            case "charisma":
                target.setCharisma(target.getCharisma() + mod);
                break;
            case "armorClass":
                target.setArmorClass(target.getArmorClass() + mod);
                break;
        }
    }
}


推荐答案

Preamble



我尝试尽可能地推广类,所以我最终没有很多特定的类只代表不同的数据,而不是不同的结构。此外,我尝试将数据结构与游戏机制分开。特别是,我尝试将战斗机制保存在一个地方,而不是将它们分成不同的类,我尽量不对任何数据进行硬编码。在这个答案中,我们将涵盖角色,他们的能力/法术,能力的效果以及战斗机制

Preamble

I try to generalise the classes as much as possible, so I do not end up with lots of specific classes that just represent different data, instead of a different structure. Also, I try to separate data structures from game mechanics. In particular, I try to keep the combat mechanics all in one place, instead of splitting them across different classes, and I try not to hard-code any data. In this answer, we will cover the characters, their abilities/spells, the effects of the abilities, and the combat mechanics.

例如,考虑 PlayableCharacter ,代表你的角色。这是一个标准数据类。它提供增加或减少生命值和法力值的方法,以及可用能力的集合。

Consider, for instance, a PlayableCharacter, that represents your characters. This is a standard data class. It provides methods for increasing or decreasing health and mana, and a collection of available abilities.

class PlayableCharacter {
    private final int maxHealth;
    private int health;
    private final int maxResource;    // mana, energy and so on
    private int resource;
    private final Collection<Ability> abilities;

    // getters and setters
}



能力



能力是同等的数据类。它们代表法术力费用,触发效果等。我经常将其表示为普通类,然后从外部数据文件中读取各个功能。在这里我们可以跳过它并用枚举声明它们。

Abilities

Abilities are equally data classes. They represent mana costs, triggered effects, and so on. I often represent this as a normal class, and then read the individual abilities from external data files. Here we can skip that and declare them with enumerations.

enum Ability {
    FIREBALL("Fireball", 3, 5, new Effect[] {
        new Effect(Mechanic.DAMAGE, 10, 0),
        new Effect(Mechanic.BURN, 2, 3)
    });

    private final String name;
    private final int level;
    private final int cost;
    private final List<Effect> effects;
}



效果



最后,效果告诉我们能力的作用。多少伤害,持续多长时间,它如何影响角色。同样,这是所有数据,没有游戏逻辑。

Effects

Finally the effects tell what an ability does. How much damage, how long it lasts, how it affects a character. Again, this is all data, no game logic.

class Effect {
    private final Mechanic effect;
    private final int value;
    private final int duration;
}

机制只是一个枚举。

enum Mechanic {
    DAMAGE, BURN;
}



力学



现在是时候让事情正常运转了。这是你的游戏循环将要与之交互的类,你必须将它提供给游戏状态(例如,角色正在争夺)。

Mechanics

Now it is time to make things work properly. This is the class that your game loop will be interacting with, and you must feed it the game state (which characters are battling, for instance).

class BattleEngine {
    void useAbility(PlayableCharacter source, PlayableCharacter target, Ability ability) {
        // ...
    }
}

如何实施每种机制由您决定。它可以是一个地狱开关,也可以是每个 Mechanic 的if / else,或者你可以将代码移动到 Mechanic 枚举,或私有嵌套类,并使用 EnumMap 来检索每个处理程序。

How you implement each mechanic is up to you. It can range from an infernal switch or if/else for each Mechanic, or you can move the code to the Mechanic enum, or to private nested classes and use an EnumMap to retrieve each handler.

interface MechanicHandler {
    void apply(PlayableCharacter source, PlayableCharacter target, Effect effect);
}





class BattleEngine {
    private final Map<Mechanic, MechanicHandler> mechanics;

    void useAbility(PlayableCharacter source, PlayableCharacter target, Ability ability) {
        source.decreaseResource(ability.getCost());
        for (Effect effect: ability.getEffects()) {
            MechanicHandler mh = mechanics.get(e.getMechanic());
            mh.apply(source, target, effect);
        }
    }

    private static final class DicePerLevel implements MechanicHandler {
        @Override
        public void apply(PlayableCharacter source, PlayableCharacter target, Effect effect) {
            int levels = Math.min(effect.getValue(), source.getLevel());
            int damage = 0;
            for (int i = 0; i < levels; ++i) {
                int roll; // roll a d6 die
                damage += roll;
            }
            target.decreaseHealth(damage);
        }
    }
}

这篇关于面向对象设计 - 法术的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆