我一直在寻找有关如何改善此代码的一般流程以及最小化if / switch条件的建议。但是,欢迎提出任何建议,以及一般游戏和游戏玩法的建议(玩家缩放,敌人缩放,攻击和敌人攻击的概率,这些问题在dice类中进行处理)。

import java.util.*;
public class driver {
    static Scanner scan = new Scanner(System.in);
    static Random rand = new Random();
    static dice die = new dice();
    public static String playerName;
    public static int playerhp;
    public static int maxhp;
    public static int maxmana;
    public static int mana;
    public static int playermeleedmg;
    public static int xp;
    public static int enemyhp;
    public static int enemymeleedmg;
    public static int Level;
    public static String charclass;
    public static boolean fighting = false; //globals for player stats & enemy stats

    private static void printStats() {
        if(charclass.equals("mage")){
            System.out.println(playerName + "\nhp: " + playerhp + "\nmana: " + mana + "\ndamage: " + playermeleedmg + "\nxp: " + xp + "\n");
        }else{
            System.out.println(playerName + "\nhp: " + playerhp + "\ndamage: " + playermeleedmg + "\nxp: " + xp + "\n");
        }
        }
    private static void printEnemyStats() {
        System.out.println("Enemy "+"\nhp: " + enemyhp + "\ndmg: " + enemymeleedmg + "\n");
    }

    private static void buildWarrior() {
        charclass = "warrior";
        maxhp = 20;
        playerhp = 20;
        playermeleedmg = 4;
        xp = 0;
        Level = 1; 
    }
    private static void buildArcher() {
        charclass = "archer";
        maxhp = 14;
        playerhp = 14;
        playermeleedmg = 6;
        xp = 0;
        Level = 1;
    }
    private static void buildMage() {
        charclass = "mage";
        maxhp = 10;
        playerhp = 10;
        mana = 20;
        maxmana = 20;
        playermeleedmg = 2;
        xp = 0;
        Level = 1; // initializes globals according to class
    }
    private static void buildEnemy() {
        switch(Level){
        case 1:
            enemyhp = 9;
            enemymeleedmg = 1;
            break;
        case 2:
            enemyhp = 19;
            enemymeleedmg = 4;
            break;
        case 3:
            enemyhp = 24;
            enemymeleedmg = 6;
            break;
        case 4:
            enemyhp = 32;
            enemymeleedmg = 7;
            break;
        case 5:
            enemyhp = 40;
            enemymeleedmg = 9;
            break; //initializes enemy stats based on player level
        }
    }
    private static void fight() {
        String action;
        String spellAction = null;
        System.out.println("An enemy approaches");
        buildEnemy();
        fighting = true;
        while(fighting = true){
            System.out.println("Press 'a' to attack\nPress 'i' for info");
            if(charclass.equals("mage")){
                System.out.print("Press 's' for spells\n");
            }
            action = scan.nextLine();
            if(action.charAt(0) == 'a'){
                fighting = attack();
                if(fighting == false){
                    switch(Level){
                    case 1: 
                        xp = xp + 4;
                        break;
                    case 2:
                        xp = xp + 6;
                        break;
                    case 3:
                        xp = xp + 9;
                        break;
                    case 4:
                        xp = xp + 12;
                        break;
                    }
                    System.out.println("You earned :" + xp + " xp");
                    checkLevelUp();
                    return;
                }
                enemyattack();  
            }

            if(action.charAt(0) == 'i'){
                printStats();
                printEnemyStats();

            }
            if(action.charAt(0) == 's'){
            System.out.println("Press 'f' for fireball\nPress 'h' to heal\n");
            spellAction = scan.nextLine();
            if(spellAction.charAt(0) == 'f'){
            if(die.roll10() > 2){
                mana = mana - 10;
                if(mana <0){
                    System.out.println("You don't have enough mana...");
                    mana = mana + 10;
                }else{
                int k = die.roll10(); //randomly hurts 1-10
                System.out.println("You hit for " + k + " damage!");
                enemyhp = enemyhp - k;
                if(enemyhp <= 0){
                    System.out.println("You Won!"); 
                    switch(Level){
                    case 1: 
                        xp = xp + 4;
                        break;
                    case 2:
                        xp = xp + 6;
                        break;
                    case 3:
                        xp = xp + 9;
                        break;
                    case 4:
                        xp = xp + 12;
                        break;
                    }
                    System.out.println("You earned :" + xp + " xp");
                    checkLevelUp();
                    return;
                }
                enemyattack();  
                }
            }
            else{
                System.out.println("You miss!");
                enemyattack();
            }
            }else
            if(spellAction.charAt(0) == 'h'){
                mana = mana - 8;
                if(mana <0){
                    System.out.println("You don't have enough mana...");
                    mana = mana + 8;
                }else{
                    int x = die.roll10(); //randomly heals 1-8
                System.out.println("You heal your wounds...");
                System.out.println("+ " + x + " hp");
                playerhp = playerhp + x;
                if(playerhp>maxhp){
                    playerhp = maxhp;
                }
                enemyattack();  
            }
            }

    }
    }
    }

    private static void checkLevelUp() {
        if(xp >= 100 && Level == 4){
            System.out.println("Level 5!");
            Level = Level + 1;
            maxhp = maxhp + 25;
            playerhp = maxhp;
            if(charclass.equals("mage")){
                maxmana = maxmana + 7;
                mana = maxmana;
            }
            playermeleedmg = playermeleedmg + 3;
            printStats();
        }else
        if(xp >= 50 && Level == 3){
            System.out.println("Level 4!");
            Level = Level + 1;
            maxhp = maxhp + 20;
            playerhp = maxhp;
            if(charclass.equals("mage")){
                maxmana = maxmana + 7;
                mana = maxmana;
            }
            playermeleedmg = playermeleedmg + 2;
            printStats();
        }else
        if(xp >= 25 && Level == 2){
            System.out.println("Level 3!");
            Level = Level + 1;
            maxhp = maxhp + 10;
            playerhp = maxhp;
            if(charclass.equals("mage")){
                maxmana = maxmana + 7;
                mana = maxmana;
            }
            playermeleedmg = playermeleedmg + 2;
            printStats();
        }else
        if(xp >= 10 && Level == 1){
            System.out.println("Level 2!");
            Level = Level + 1;
            maxhp = maxhp + 5;
            playerhp = maxhp;
            if(charclass.equals("mage")){
                maxmana = maxmana + 7;
                mana = maxmana;
            }
            playermeleedmg = playermeleedmg + 1;
            printStats();
        }//increments player level and adds to stats with xp

    }
    private static void enemyattack() {
        if(die.roll6() > 2){
            System.out.println("Enemy hits!");
            playerhp = playerhp - enemymeleedmg;
            if(playerhp <= 0){
                gameover();
                System.exit(0);//game over if player health < 0
            }
        }else{
            System.out.println("Enemy Misses!");
        }       
    }
    private static boolean attack() {
        if(die.roll6() > 2){
            System.out.println("You hit!");
            enemyhp = enemyhp - playermeleedmg;
            if(enemyhp <= 0){
                System.out.println("You Won!"); //prints if enemy health < 0
                return false; 
            }
        }else{
            System.out.println("You miss!");
        }
        return true;
    }

    private static void gameover() {
        System.out.println(playerName + " Died!") ;
        System.out.println("GAME OVER!");
        System.exit(0); //terminates if lost
        return;
    }
    public static void main(String[] args) {
        String charclass;
        int num = 2;
        while(num > 1){
        System.out.println("Enter your Name: ");
        playerName = scan.nextLine();
        System.out.println("Choose your class: ");
        System.out.println("'w' for warrior");
        System.out.println("'a' for archer");
        System.out.println("'m' for mage");
        charclass = scan.nextLine();
        while(charclass.charAt(0) != 'w' && charclass.charAt(0) != 'a' && charclass.charAt(0) != 'm'){
            System.out.println("'w' for warrior");
            System.out.println("'a' for archer");
            System.out.println("'m' for mage");
            charclass = scan.nextLine();
        }
        if(charclass.charAt(0) == 'w'){
            buildWarrior();
        }
        if(charclass.charAt(0) == 'a'){
            buildArcher();
        }
        if(charclass.charAt(0) == 'm'){
            buildMage();
        }
        printStats();
        while(Level == 1){
        fight();
        }
        System.out.println("This area is clear... time to move on\n");
        while(Level == 2){
            fight();
        }
        System.out.println("This area is clear... time to move on\n");
        while(Level == 3){
            fight();
        }
        System.out.println("This area is clear... time to move on\n");
        while(Level == 4){
            fight();
        }
        System.out.println("This area is clear... time to move on\n");
        while(Level == 5){
            fight();
        }//keeps in area until levelUp
    }

    }
}



import java.util.*;
public class dice {

public int roll6(){
    Random rand = new Random();
    int n = rand.nextInt(7);
    while(n == 0){
        n = rand.nextInt(7);
    }//1-6
    return n;
}
public int roll10(){
    Random rand = new Random();
    int n = rand.nextInt(11);
    while(n == 0){
        n = rand.nextInt(11);
    }
    return n;
}//1-10
public int roll20(){
    Random rand = new Random();
    int n = rand.nextInt(21);
    while(n == 0){
        n = rand.nextInt(21);
    }//1-20
    return n;
}

}


评论

太糟糕了,我在工作,这是一个很好的问题!你用什么来编码?您在使用IDE还是其他工具?

我在这个项目中使用了Eclipse Luna。

while(fighting = true)...那不应该是==吗?

嗯...为什么要讨论?在战斗中

@Mawg这是Java,而不是JavaScript。因此,没有“三等分”符号。

#1 楼

OOP

这个任务实际上是面向对象编程的。

第一步,我建议移动maxhpplayerhpplayermeleedmgxplevel字段指定给玩家专用的类,并重写build*方法。您可以使用PlayerFactory方法创建build*,以创建并返回战士,弓箭手,法师,敌人。
所有这些不同的方法字符可以从通用基址Player类继承。

滚动骰子

要在x的包含范围内获取随机数[1:6],而不是rand.nextInt(7)
跳过while循环中的零,
您应该使用1 + rand.nextInt(6)来获得相同的效果。

roll6roll10roll20方法都使用相同的逻辑。
只需一个roll方法使用参数可以完成此任务:

public class dice {
    private final Random random = new Random();

    public int roll(int max) {
        return 1 + random.nextInt(max);
    }

    public int roll6() {
        return roll(6);
    }

    // and so on ...
}


如本示例所示,
您可能不需要新的Random ins

束缚条件

这些条件是互斥的,因此它们应该与else if链接在一起,而不是使用多个独立的if语句:


if(charclass.charAt(0) == 'w'){
    buildWarrior();
}
if(charclass.charAt(0) == 'a'){
    buildArcher();
}
if(charclass.charAt(0) == 'm'){
    buildMage();
}



在此示例中,switch甚至会更好,
使得charclass.charAt(0)只会被评估一次。

不要重复自己

在多个地方都有重复的行。
例如,enemyattackattack方法实现相同的逻辑,
详细信息之间只有很小的差异。
这些详细信息可以作为参数,
以便避免复制粘贴代码。
以后需要更新复制粘贴的部分时,
/>在多个位置进行并行更改会非常烦人,
也很容易出错。
大约有99%的时间提取通用逻辑和泛化比复制更好-粘贴。

格式化

您正在使用Eclipse Luna,
具有很好地重新格式化整个代码的功能。
最好使用它,直到习惯性地键入好为止。

评论


\ $ \ begingroup \ $
感谢您的反馈!我实现了您建议的一些更改,并且我总是很高兴简化我的代码。
\ $ \ endgroup \ $
–Johnsonite
17年5月30日在19:04

\ $ \ begingroup \ $
是否可以切换(charclass.charAt(0)){...}比其他方式更好? (特别是如果具有默认值:他们尝试按q等的情况)
\ $ \ endgroup \ $
– gmatht
17年5月31日在9:15



\ $ \ begingroup \ $
“每次滚动之前您可能不需要一个新的Random实例”-我说您绝对不应该创建Random的新实例!使用该代码,如果您在同一毫秒内多次调用new Dice()。roll(),那么您将获得相同的结果。请确保重复使用Random实例,或者您永远不要使用多个Dice对象实例。
\ $ \ endgroup \ $
–迈克·巴克斯特(Mike Baxter)
17年6月1日在8:24

\ $ \ begingroup \ $
@gmatht不,不会。基本上,它归结为编译器如何分解switch语句。如果所有值都是顺序值减去一个值,则是的,切换效率更高。但是在这种情况下,w(119),a(97)和m(109)不是固定的。例如,如果它是b(98),a(97)和d(100)(请注意b和d之间的间隔,那么是的,您可能想使用switch语句,这样会更高效。在编译器级别上,lookupswitch(间隙大于1,效率较低,基本上是elseif)与tableswitch(效率最高)相比。
\ $ \ endgroup \ $
–searchengine27
17年6月1日在21:35



\ $ \ begingroup \ $
请访问artima.com/underthehood/flowP.html,了解有关其幕后工作原理的更明确的细节。另外,如果您使用的是Eclipse之类的IDE,则Eclipse将允许您在创建.class文件后查看Java程序集,以便您可以自己汇编一些测试用例,然后在IDE中打开.class文件以查看确切如何对待它。
\ $ \ endgroup \ $
–searchengine27
17年1月1日在21:36

#2 楼

您的某些开关盒不必要地冗长,重复,并包含许多代码。

您可以轻松地将它们替换为索引数组,从而使其更易于阅读,更短,并且以后更容易使用更多的值进行扩展。

例如,代替

switch(Level){
    case 1:
        enemyhp = 9;
        enemymeleedmg = 1;
        break;
    case 2:
        enemyhp = 19;
        enemymeleedmg = 4;
        break;
    case 3:
        enemyhp = 24;
        enemymeleedmg = 6;
        break;
    case 4:
        enemyhp = 32;
        enemymeleedmg = 7;
        break;
    case 5:
        enemyhp = 40;
        enemymeleedmg = 9;
        break; //initializes enemy stats based on player level
    }


您可以这样做

int[] enemyHpArray = {1, 9, 19, 24, 32, 40};
int[] enemyMeleedmgArray = {1, 1, 4, 6, 7, 9};
enemyhp = enemyHpArray[Level];
enemymeleedmg = enemyMeleedmgArray[Level];


请注意,我将值1放在索引0上,只是为了避免放置那里是0。看起来您不会使用数组的0索引,但是如果您偶然使用它,则可以减少由于hp / damage意外设置为0而导致游戏崩溃的风险。

另一种可能性是仅将您的实际值{9, 19, 24, 32, 40}放入数组中,然后在enemyHpArray[Level-1];上进行索引,但我个人认为这将使代码更混乱/可读性更低。

https://stackoverflow.com/问题/ 1938101 /如何在Java中初始化数组

评论


\ $ \ begingroup \ $
很高兴听到一个想法,长时间的开关盒会令人讨厌。谢谢!
\ $ \ endgroup \ $
–Johnsonite
17年5月30日在22:23

\ $ \ begingroup \ $
“看来您不会使用数组的0索引,但是万一您不小心使用了它,可能会降低崩溃游戏的风险”。您希望它能够快速失败,而不是失败-安全。更好的值为-1。
\ $ \ endgroup \ $
–迈克尔
17年5月31日在21:28

#3 楼

Format

如其他人所指出的那样,可以安排格式。
任何体面的IDE都具有代码格式化程序;)

此外,类名称应以大写开头大小写字母;)

在有意义时使用枚举

在用Java进行编码时,应将String charclass替换为一个更有意义的枚举。字符串是用于表示文本的,因此,您应该问自己是否能够使用上一个类是有意义的;)
甚至可以问一个特殊的枚举值是否可以*例如强制拼写*;)

enum CharacterClass {
    GENERIC_RANDOM_WARRIOR(true, true), SINGER(false, false), BIG_SLEEPER(false, true);
    public final boolean canWieldBigAxes;
    public final boolean loveToSleep;

    private CharacterClass(final boolean canWieldBigAxes, final boolean loveToSleep) {
        this.canWieldBigAxes = canWieldBigAxes;
        this.loveToSleep = loveToSleep;
    }
}


对于可以挥动手臂的人来说,情况看起来像这样:

if (charClass.canWieldBigAxes) {
    // things
}
// instead of
if (charClass.charAt(0) = 'w')


更容易阅读,更合逻辑,对吧?

随机数

您的Dice类很整洁,但您应该遵循Janos的建议:


使用nextInt(x)+ 1
使Random一个仅初始化一次的字段(这也使随机数更好)

OOP

这可能不是您的主要目标,但您的代码是遗憾的是不是OO。

如果要使其更面向对象:

,您应该尝试查找代码中“实体”正在共享的东西。

例如,您的敌人和英雄都可以被视为具有最大生命值,当前生命值,MinMeleeDamage和MaxMeleeD的“可以战斗的实体”(来自nom:BattleUnit)等级,最重要的是,可以尝试击中对手!

因此,您可以使用以下界面:

interface BattleUnit {
    int getLevel();
    void setLevel(final int level);
    int increaseLevel();
    boolean isDead();
    int getHP();
    int getMaxHP();
    int setHP(final int newHP);
    void damage(final int valueToSubstract);
    boolean attack(final BattleUnit target);
    int getMinMeleeDamage();
    int getMaxMeleeDamage();
}


您的英雄将被定义为扩展BattleUnit接口(或您所说的任何名称),因为作为强大的英雄,他是“可以战斗的实体”(或者有时他将是“可以战斗并施放法术的实体”)。 ..各种敌人也一样,因为他们都是深深的战队。

class Hero implements BattleUnit {
    private String name;
    private CharacterClass charClass;
    // some fields

    public boolean isDead() {
        return getHP() <= 0;
    }

    // blablabla moar code
    public boolean attack(final BattleUnit target) {
        if (die.roll6() > 2) {
            System.out.println("You hit!");

            target.damage(produceDamage());

            if (target.isDead()) {
                System.out.println("You Won!"); // prints if enemy health < 0
                return false;
            }
        } else {
            System.out.println("You miss!");
        }
        return true;
    }


#4 楼

格式化

看起来有些琐碎,但是,正如janos所指出的,请使用代码格式化程序。
(香草蚀上的Ctrl-Shift-F是您的朋友)。
在整个项目中坚持一种风格。

特别是缩进可能会严重误导人(这使我畏缩;-)):

        }
        }else
        if(..){
            ..
            if(..){
                ..
            }else{
                ..
            ..
            if(..){
                ..
            }
            ..  
        }
        }
}


正确缩进使您能够一目了然地看到类/子类/方法/块/嵌套块的边界。

使“魔术”数字外化

曾经想改变游戏机制。
定义并使用常量(或常量字段或枚举)表示不会改变的值。

public static final int INITIAL_ARCHER_HP = 14;

private static void buildArcher() {
    ..
    playerhp = INITIAL_ARCHER_HP;
    ..
}


这样,所有内容都定义一次,所有定义都集中在一个地方。

代码


请记住,如果需要,String.charAt()将给您带来意想不到的结果使用奇异的字符集(请参阅此讨论)。
考虑一个简单的return而不是System.exit(0)
对于小型独立Java应用程序来说,这是很好的选择,但它会突然终止JVM,这在多线程应用程序中可能会使所有其他线程感到惊讶。.

(btw:return;之后的System.exit(0);从未达到)

#5 楼

关于如何减少重复次数的一些建议。

如您所见,while循环首先执行检查,因此不能保证该循环执行一次或多次。

System.out.println("'w' for warrior");
System.out.println("'a' for archer");
System.out.println("'m' for mage");
charclass = scan.nextLine();
while(charclass.charAt(0) != 'w' && charclass.charAt(0) != 'a' && charclass.charAt(0) != 'm'){
    System.out.println("'w' for warrior");
    System.out.println("'a' for archer");
    System.out.println("'m' for mage");
    charclass = scan.nextLine();
}


但是,do while循环在最后进行检查。这意味着可以保证至少执行一次:

char charclass; // changed to char rather than string

// ...

do {
    System.out.println("'w' for warrior");
    System.out.println("'a' for archer");
    System.out.println("'m' for mage");
    charclass = scan.nextLine().charAt(0);

} while (charclass != 'w' && charclass != 'a' && charclass != 'm')


如您所见,我还更改了charclass变量的定义。


您也可以对此进行重构:

while(Level == 1){
    fight();
}
System.out.println("This area is clear... time to move on\n");
while(Level == 2){
    fight();
}
System.out.println("This area is clear... time to move on\n");
while(Level == 3){
    fight();
}
System.out.println("This area is clear... time to move on\n");
while(Level == 4){
    fight();
}
System.out.println("This area is clear... time to move on\n");
while(Level == 5){
    fight();
}


至:

for (int currentLevel = 1; currentLevel <= 5; currentLevel++) {
    while(Level == currentLevel){
        fight();
    }
    System.out.println("This area is clear... time to move on\n");
}


#6 楼

您可以通过将特定播放器类是否适用于该播放器类代码的逻辑移到该特定类实例中,来进一步概括播放器创建代码。例如,

public MagePlayerClass extends AbstractPlayerClass {
    @Override 
    boolean canCreate(final String arg) {
        return "m".equalsIgnoreCase(arg);
    }
}


这样,您就可以完全删除巨大的if-then-else或switch语句,并且可以具有数百个扩展AbstractPlayerClass的类。
然后,将通过一个抽象工厂方法创建播放器实例,该方法使用AbstractPlayerClass的那些子类来创建和初始化Player实例。

您的播放器创建代码将遍历一个播放器中的所有类。扩展AbstractPlayerClass的特定文件夹,直到其中一个从对canCreate(“ m”)的调用返回true为止。

此外,您现在有了一个超类来移动所有通用播放器类代码。 />

评论


\ $ \ begingroup \ $
这些类名太可怕了:( MagePlayer和Player会更好。
\ $ \ endgroup \ $
–迈克尔
17年6月8日在8:23

\ $ \ begingroup \ $
另外,canCreate可能应该是静态的-否则,您需要创建一个法师以检查是否应该能够创建它。
\ $ \ endgroup \ $
–迈克尔
17年6月8日在8:29

\ $ \ begingroup \ $
如果它是静态的,则不能使用继承。您将必须对类类型使用反射,以查看其是否扩展了AbstractPlayerClass,然后调用其静态方法canCreate(String)。
\ $ \ endgroup \ $
–瑞克·赖克(Rick Ryker)
17年7月6日在16:47

\ $ \ begingroup \ $
您不能继承该方法。而且甚至不建议使用反射来做这件事那么可怕的事情。再看一遍,我认为应该使用工厂类来实现。您传递一个字符,就退出一个玩家。
\ $ \ endgroup \ $
–迈克尔
17年7月6日在18:05

\ $ \ begingroup \ $
如果一个文件夹中有数百个都实现该接口的子类,则更有可能让工厂类读取该文件夹中的所有类文件并加载它们,实例化每个文件中的一个,然后映射该子类的自报告创建id(“ M”)到子类对象(my.package.MageImpl),然后让实例化的子类获取垃圾回收。许多框架都以这种方式工作。一种快捷方式是让它删除映射文本文件。然后在启动时加载它,并且仅实例化文本文件中未找到的文件的类。 M = my.package.MageImpl
\ $ \ endgroup \ $
–瑞克·赖克(Rick Ryker)
18年5月25日在6:55