jjzjj

java - 面向对象设计 - 法术

coder 2024-03-16 原文

我的第一个 Java 项目是一个基本的角色扮演游戏。现在我在研究法术,我需要一些 OOD 指导。

我有Character,它是一个抽象类Character 有一些子类(比如magefighterrogue牧师).

法师牧师(至于现在,牧师没有法力,但它可能会改变) 都是施法者。

我还有一个 Spell 类,其中包含一些信息(例如 spell namemana cost 等)。 MageSpellsListClericSpellsList 是另外两个类,它们都有 Spell 类列表。我也有 Effects 类(施法应该使用它)。

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

也许使用“SpellCaster”接口(interface)和一些方法,如 castSpell 和 showSpellbook,这样 Mage 和 Cleric 将实现该接口(interface)? .

也许 MageSpellsList 和 ClericSpellsList 应该是 Spell 的子类?我的目

法师.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");
        }
    }
}

拼写.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:

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;
    }
}

效果.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;
        }
    }
}

最佳答案

序言

我尽量概括类,这样我就不会得到很多只代表不同数据而不是不同结构的特定类。此外,我尝试将数据结构与游戏机制分开。特别是,我尝试将战斗机制全部放在一个地方,而不是将它们分成不同的类别,并且我尝试不对任何数据进行硬编码。在这个答案中,我们将介绍角色、他们的能力/法术、能力的效果,以及战斗机制.

字符

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

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
}

能力

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

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;
}

效果

最后,效果说明了一个能力的作用。伤害有多大,持续多长时间,对角色有何影响。同样,这都是数据,没有游戏逻辑。

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

机制只是一个枚举。

enum Mechanic {
    DAMAGE, BURN;
}

力学

现在是时候让事情正常进行了。这是您的游戏循环将与之交互的类,您必须向它提供游戏状态(例如哪些角色正在战斗)。

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

如何实现每个机制取决于您。它的范围可以是每个 Mechanic 的 hell 开关或 if/else,或者您可以将代码移动到 Mechanic 枚举,或者移动到私有(private)嵌套类并使用 EnumMap 以检索每个处理程序。

示例机制

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);
        }
    }
}

关于java - 面向对象设计 - 法术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33450665/

有关java - 面向对象设计 - 法术的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  4. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  5. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  8. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  9. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  10. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

随机推荐