首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实现游戏“升级”功能

实现游戏“升级”功能
EN

Code Review用户
提问于 2014-07-30 16:31:01
回答 2查看 1.2K关注 0票数 18

我正在构建一个简单的冒险游戏,有“升级”功能。

在此游戏中,用户控制一个玩家,该玩家可以升级以增加如下内容:

  • 赚得的黄金数额
  • 最大健康
  • 攻击力量
  • (稍后会添加更多内容,例如:国防升级、经验等。)

一个“升级”有一个最大的级别上限,对每个升级级别的影响,以及每次升级的成本。

这是我目前的方法。

这是我的Upgrades类,包含所有可能的升级:

代码语言:javascript
复制
public class Upgrades {

    public static final int GOLD_LEVEL_CAP = 10;
    public static final float GOLD_EFFECT_PER_LEVEL = 0.01f;
    public static final int[] GOLD_UPGRADE_COSTS = {
        100, 200, 300, 400, 500,
        600, 700, 800, 900, 1000
    };

    public static final int HEALTH_LEVEL_CAP = 10;
    public static final int HEALTH_EFFECTS_PER_LEVEL = 100;
    public static final int[] HEALTH_UPGRADE_COSTS = {
        100, 200, 300, 400, 500,
        600, 700, 800, 900, 1000
    };

    public static final int POWER_LEVEL_CAP = 5;
    public static final float POWER_EFFECT_PER_LEVEL = 0.05f;
    public static final int[] POWER_UPGRADE_COSTS = {
        1000, 2000, 3000, 4000, 5000
    };

}

这是存储当前升级的类:(属于Player类)

代码语言:javascript
复制
public class UpgradeProgress {

    private int goldUpgradeLevel = 0;
    private int healthUpgradeLevel = 0;
    private int powerUpgradeLevel = 0;

    public void upgradeGold() {
        if (goldUpgradeLevel < Upgrades.GOLD_LEVEL_CAP) {
            goldUpgradeLevel++;
        }
    }

    // Getter
    public int getGoldUpgradeLevel() {
        return goldUpgradeLevel;
    }

    // and so on..
    // same for Health and Power
}

但是,如果我想添加一个新的升级,例如,我想添加一个“国防升级”。我应该同时修改UpgradesUpgradeProgress类。

这是否有效和可接受的?

还是有更好的方法?

EN

回答 2

Code Review用户

发布于 2014-07-30 23:13:01

由于巴佐拉和我在处理这一问题的最佳途径上意见不一,我在这里的回答是:

从他的答案中拿出bazolas升级课程,然后使用下面的实现来实现升级。

代码语言:javascript
复制
public class DamageUpgrade extends Upgrade {
    public DamageUpgrade(int levelCap, float effectPerLevel, int[] upgradeCosts) {
        super(levelCap, effectsPerLevel, upgradeCosts);
    }
}

public class Player {
    private float damage = 10; //base value
    private DamageUpgrade dmgUp = new DamageUpgrade(2, 2, new int[]{10,20});

    public float getDamage(){
        //returns 10 with no Upgrade, 12 on first and 14 on second Upgrade
        return damage + dmgUp.getTotalEffect();
    }
}

在我看来,为每一种升级类型创建类的优点是,它很容易实现,而且您可以相对容易地看到这些可获取的升级。这个解决方案可以通过使用一个映射来保存不同的Upgradetype来改进,这样就可以轻松地在Runtime动态添加一个新的Upgradetype,但是在这种类型的游戏中没有必要,因为所有的升级都已经在Compiletime中知道了(至少应该是这样)。

缺点当然是工作空间变得杂乱无章。你会有很多文件,几乎没有新的内容。这意味着你必须组织自己,例如,通过使用包(无论如何,我推荐)。

对于那些不喜欢多类解决方案的人:

使用Enum。忘记以前的一切,我现在要回收名字。levelCap被缩减了,就像Arraysize已经暗示的那样。

代码语言:javascript
复制
public enum Upgrade{
    DAMAGE(2, new int[]{10,20}),
    RANGE(1, new int[]{5,10,15,20});

    private float effectsPerLevel;
    private int[] upgradeCosts;

    private Upgrade(float effectsPerLevel, int[] upgradeCosts){
        this.effectsPerLevel = effectsPerLevel;
        this.upgradeCosts = upgradeCosts;
    }

    //special getter for nonexistent field, because we still need the levelcap
    public int getLevelCap(){
        return upgradeCosts.length;
    }

    //add getters for all fields, in the following I assume you did this
}

真正的魔法来了。我将使用EnumMap来跟踪每一次升级的当前级别。这需要一些准备。我必须用0初始化每个字段,最后的结果要复杂一些。由于它有点复杂,升级也包括在内。如果你对这个答案有疑问,请发表意见,我会澄清的。

代码语言:javascript
复制
public class Player{
    private EnumMap<Upgrade, Integer> upg;
    private float damage = 10;
    private int balance;    //included to show upgrading

    //Note: You can extend this constructor as much as you want, this is the minimum
    public Player() {
        upg = new EnumMap<>(upg.class);
        for (Upgrade u : Upgrades.values()){
            upg.put(u, 0);
        }
    }

    public float getDamage(){
        return damage + upg.get(Upgrade.DAMAGE) * Upgrade.DAMAGE.getEffectPerLevel();
    }

    public void levelUp(Upgrade u){
        if(upg.get(u) >= u.getLevelCap()) return; //early escape, we can't upgrade
        if(balance < u.getUpgradeCost()[upg.get(u)]) return; //early escape, not enough money.

        balance = balance - u.getUpgradeCost()[upg.get(u)];
        upg.put(u, upg.get(u) + 1); //fetch the value, add one, store it back
    }
}

优点:一旦安装,添加一个新的升级就像添加另一个enum条目一样简单(如范围所示)。每当你添加一个新的升级类型,它将自动有0级,所以你不必担心升级没有初始化。一旦你进入它,使用枚举和地图是很棒的。您也可以在许多其他地方使用枚举。想为每次升级生成一个按钮,但不想每次都重写整个GUI?使用Upgrade.values()获取所有升级的数组,这将自动提供升级的数量。

缺点:一旦安装成功,它就能工作。要做到这一点很复杂,需要对Enum和Maps有一个扎实的理解(前者比后者更多)。

结论:第一个版本更容易,从长远来看,第二个版本更好(因为更好地重用)。这至少是我经过两个小时的思考后的看法。

票数 8
EN

Code Review用户

发布于 2014-07-30 22:18:40

当前的方法不是一个直观的面向对象的设计。Upgrades类基本上就像C中的struct,它只是集合在一起的“东西”。复数名称“升级”本身已经表明,这是一个事物集合,使用" things“集合的自然方法是创建一个Thing类并将您的东西放入Collection<Thing>中。

也许有一个Feature类来表示特性是有意义的,如下所示:

代码语言:javascript
复制
class Feature {
    private final String name;
    private final float effectPerLevel;
    private final int[] upgradeCosts;

    Feature(String name, float effectPerLevel, int[] upgradeCosts) {
        this.name = name;
        this.effectPerLevel = effectPerLevel;
        this.upgradeCosts = upgradeCosts;
    }

    public int getLevelCap() {
        return upgradeCosts.length;
    }
}

基于Feature,您可以创建方便类GoldHealthPower

代码语言:javascript
复制
class Gold extends Feature {
    public Gold() {
        super("Gold", .01f, new int[]{
                100, 200, 300, 400, 500,
                600, 700, 800, 900, 1000
        });
    }
}

class Health extends Feature {
    public Health() {
        super("Health", 100, new int[]{
                100, 200, 300, 400, 500,
                600, 700, 800, 900, 1000
        });
    }
}

这些是方便的类,因为您可以通过直接调用Feature的构造函数来创建这些特性。它将取决于您的整个程序,哪种方法将更符合人体工程学。

我删除了级别上限的常量,因为(至少现在),您似乎可以从upgradeCosts数组字段中派生出这个值。

我在这里并没有故意包括升级逻辑。其中一个原因是职责分离:理想情况下,类应该有一个单独的责任。Feature的职责是通过保持其特性来描述其特性。如果我想跟踪当前的功能和升级级别,这将是第二个责任。

将升级逻辑放在其他地方的另一个原因是,当您有多个player对象时,可以避免重复。每个玩家可能都有一个或多个特征。它们可能在不同的层次上具有这些特性,但是特性的定义特征是相同的,只是级别不同。换句话说,Feature对象可以由多个参与者共享,并且您可以只允许玩家在级别上有所变化。也许这是捕捉这一概念的一种方式:

代码语言:javascript
复制
class PlayerFeature {
    private final Feature feature;
    private int level;

    PlayerFeature(Feature feature) {
        this.feature = feature;
        this.level = 1;
    }

    void upgrade() {
        if (level < feature.getLevelCap()) {
            ++level;
        }
    }
}

你可以定义Players,它可以有一个PlayerFeatureS的集合:

代码语言:javascript
复制
class Player {
    Set<PlayerFeature> features = new HashSet<PlayerFeature>();

    public void addFeature(PlayerFeature feature) {
        features.add(feature);
    }
}

然后用这样的东西来设置一个游戏:

代码语言:javascript
复制
class Game {
    public void initPlayers() {
        Gold gold = new Gold();
        Health health = new Health();

        Player player1 = new Player();
        player1.addFeature(new PlayerFeature(gold));
        player1.addFeature(new PlayerFeature(health));

        Player player2 = new Player();
        player2.addFeature(new PlayerFeature(gold));
        player2.addFeature(new PlayerFeature(health));
    }
}

通过这种方式拆分原始的Upgrades类,您可以获得灵活性。如果您想要对其中一个特性进行一些更改,例如Gold,您可以使用现代IDE中的键盘快捷方式很容易地跳到该类,而必须在不断增长的Upgrades类中进行文本搜索。

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

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

复制
相关文章

相似问题

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