首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java中基于文本的RPG

Java中基于文本的RPG
EN

Code Review用户
提问于 2012-03-13 21:56:48
回答 3查看 41.6K关注 0票数 15

我正在用Java编写一个简单的基于文本的RPG。我认为这是一个很好的练习,可以练习OO,并思考对象应该如何最好地交互。我很想听听你的想法!

Game类包含一个游戏:

代码语言:javascript
复制
public final class Game {

    private final Player player = Player.newInstance();

    public void play() throws IOException {
        System.out.println("You are " + player + " " + player.getDescription());
        Dungeon.newInstance().startQuest(player);
    }

    public static void main(String[] args) throws IOException {
        Game game = new Game();
        game.play();
    }

}

下面是一个简单的Dungeon类,Rooms在地图上列出的一个集合。玩家从一个房间移动到另一个房间,遇到怪物并与之搏斗。

代码语言:javascript
复制
public final class Dungeon {

    private final Map<Integer, Map<Integer, Room>> map = new HashMap<Integer, Map<Integer, Room>>();
    private Room currentRoom;
    private int currentX = 0;
    private int currentY = 0;

    private Dungeon() {
    }

    private void putRoom(int x, int y, Room room) {
        if (!map.containsKey(x)) {
            map.put(x, new HashMap<Integer, Room>());
        }
        map.get(x).put(y, room);
    }

    private Room getRoom(int x, int y) {
        return map.get(x).get(y);
    }

    private boolean roomExists(int x, int y) {
        if (!map.containsKey(x)) {
            return false;
        }
        return map.get(x).containsKey(y);
    }

    private boolean isComplete() {
        return currentRoom.isBossRoom() && currentRoom.isComplete();
    }

    public void movePlayer(Player player) throws IOException {
        boolean northPossible = roomExists(currentX, currentY + 1);
        boolean southPossible = roomExists(currentX, currentY - 1);
        boolean eastPossible = roomExists(currentX + 1, currentY);
        boolean westPossible = roomExists(currentX - 1, currentY);
        System.out.print("Where would you like to go :");
        if (northPossible) {
            System.out.print(" North (n)");
        }
        if (eastPossible) {
            System.out.print(" East (e)");
        }
        if (southPossible) {
            System.out.print(" South (s)");
        }
        if (westPossible) {
            System.out.print(" West (w)");
        }
        System.out.print(" ? ");
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String direction = in.readLine();
        if (direction.equals("n") && northPossible) {
            currentY++;
        } else if (direction.equals("s") && southPossible) {
            currentY--;
        } else if (direction.equals("e") && eastPossible) {
            currentX++;
        } else if (direction.equals("w") && westPossible) {
            currentX--;
        }
        currentRoom = getRoom(currentX, currentY);
        currentRoom.enter(player);
    }

    public void startQuest(Player player) throws IOException {
        while (player.isAlive() && !isComplete()) {
            movePlayer(player);
        }
        if (player.isAlive()) {
            System.out.println(Art.CROWN);
        } else {
            System.out.println(Art.REAPER);
        }
    }

    public static Dungeon newInstance() {
        Dungeon dungeon = new Dungeon();
        dungeon.putRoom(0, 0, Room.newRegularInstance());
        dungeon.putRoom(-1, 1, Room.newRegularInstance());
        dungeon.putRoom(0, 1, Room.newRegularInstance());
        dungeon.putRoom(1, 1, Room.newRegularInstance());
        dungeon.putRoom(-1, 2, Room.newRegularInstance());
        dungeon.putRoom(1, 2, Room.newRegularInstance());
        dungeon.putRoom(-1, 3, Room.newRegularInstance());
        dungeon.putRoom(0, 3, Room.newRegularInstance());
        dungeon.putRoom(1, 3, Room.newRegularInstance());
        dungeon.putRoom(0, 4, Room.newBossInstance());
        dungeon.currentRoom = dungeon.getRoom(0, 0);
        return dungeon;
    }

}

下面是Monster类:

代码语言:javascript
复制
public final class Monster {

    private final String name;
    private final String description;
    private int hitPoints;
    private final int minDamage;
    private final int maxDamage;
    private final static Random random = new Random();
    private final static Set<Integer> monstersSeen = new HashSet<Integer>();
    private final static int NUM_MONSTERS = 3;

    public static Monster newRandomInstance() {
        if (monstersSeen.size() == NUM_MONSTERS) {
            monstersSeen.clear();
        }
        int i;
        do {
            i = random.nextInt(NUM_MONSTERS);
        } while (monstersSeen.contains(i));
        monstersSeen.add(i);

        if (i == 0) {
            return new Monster("Harpy", Art.HARPY, 40, 8, 12);
        } else if (i == 1) {
            return new Monster("Gargoyle", Art.GARGOYLE, 26, 4, 6);
        } else {
            return new Monster("Hobgoblin", Art.HOBGOBLIN, 18, 1, 2);
        }
    }

    public static Monster newBossInstance() {
        return new Monster("Dragon", Art.DRAGON, 60, 10, 20);
    }

    private Monster(String name, String description, int hitPoints, int minDamage, int maxDamage) {
        this.name = name;
        this.description = description;
        this.minDamage = minDamage;
        this.maxDamage = maxDamage;
        this.hitPoints = hitPoints;
    }

    @Override
    public String toString() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public String getStatus() {
        return "Monster HP: " + hitPoints;
    }

    public int attack() {
        return random.nextInt(maxDamage - minDamage + 1) + minDamage;
    }

    public void defend(Player player) {
        int attackStrength = player.attack();
        hitPoints = (hitPoints > attackStrength) ? hitPoints - attackStrength : 0;
        System.out.printf("  %s hits %s for %d HP of damage (%s)\n", player, name, attackStrength,
                getStatus());
        if (hitPoints == 0) {
            System.out.println("  " + player + " transforms the skull of " + name
                    + " into a red pancake with his stone hammer");
        }
    }

    public boolean isAlive() {
        return hitPoints > 0;
    }

}

Battle类:

代码语言:javascript
复制
public final class Battle {

    public Battle(Player player, Monster monster) throws IOException {
        System.out.println("You encounter " + monster + ": " + monster.getDescription() + "\n");
        System.out.println("Battle with " + monster + " starts (" + player.getStatus() + " / "
                + monster.getStatus() + ")");
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        while (player.isAlive() && monster.isAlive()) {
            System.out.print("Attack (a) or heal (h)? ");
            String action = in.readLine();
            if (action.equals("h")) {
                player.heal();
            } else {
                monster.defend(player);
            }
            if (monster.isAlive()) {
                player.defend(monster);
            }
        }
    }

}

Room类:

代码语言:javascript
复制
public final class Room {

    private final String description;
    private final Monster monster;
    private final Boolean isBossRoom;
    private final static Random random = new Random();
    private final static Set<Integer> roomsSeen = new HashSet<Integer>();
    private final static int NUM_ROOMS = 7;

    private Room(String description, Monster monster, Boolean isBossRoom) {
        this.description = description;
        this.monster = monster;
        this.isBossRoom = isBossRoom;
    }

    public static Room newRegularInstance() {
        if (roomsSeen.size() == NUM_ROOMS) {
            roomsSeen.clear();
        }
        int i;
        do {
            i = random.nextInt(NUM_ROOMS);
        } while (roomsSeen.contains(i));
        roomsSeen.add(i);

        String roomDescription = null;
        if (i == 0) {
            roomDescription = "a fetid, dank room teeming with foul beasts";
        } else if (i == 1) {
            roomDescription = "an endless mountain range where eagles soar looking for prey";
        } else if (i == 2) {
            roomDescription = "a murky swamp with a foul smelling odour";
        } else if (i == 3) {
            roomDescription = "a volcano with rivers of lava at all sides";
        } else if (i == 4) {
            roomDescription =
                    "a thick forest where strange voices call out from the trees high above";
        } else if (i == 5) {
            roomDescription =
                    "an old abandoned sailing ship, littered with the remains of some unlucky sailors";
        } else if (i == 6) {
            roomDescription = "a cafe filled with hipster baristas who refuse to use encapsulation";
        } else {
        }
        return new Room(roomDescription, Monster.newRandomInstance(), false);
    }

    public static Room newBossInstance() {
        return new Room("a huge cavern thick with the smell of sulfur", Monster.newBossInstance(),
                true);
    }

    public boolean isBossRoom() {
        return isBossRoom;
    }

    public boolean isComplete() {
        return !monster.isAlive();
    }

    @Override
    public String toString() {
        return description;
    }

    public void enter(Player player) throws IOException {
        System.out.println("You are in " + description);
        if (monster.isAlive()) {
            new Battle(player, monster);
        }
    }

}

Player类:

代码语言:javascript
复制
public final class Player {

    private final String name;
    private final String description;
    private final int maxHitPoints;
    private int hitPoints;
    private int numPotions;
    private final int minDamage;
    private final int maxDamage;
    private final Random random = new Random();

    private Player(String name, String description, int maxHitPoints, int minDamage, int maxDamage,
            int numPotions) {
        this.name = name;
        this.description = description;
        this.maxHitPoints = maxHitPoints;
        this.minDamage = minDamage;
        this.maxDamage = maxDamage;
        this.numPotions = numPotions;
        this.hitPoints = maxHitPoints;
    }

    public int attack() {
        return random.nextInt(maxDamage - minDamage + 1) + minDamage;
    }

    public void defend(Monster monster) {
        int attackStrength = monster.attack();
        hitPoints = (hitPoints > attackStrength) ? hitPoints - attackStrength : 0;
        System.out.printf("  " + name + " is hit for %d HP of damage (%s)\n", attackStrength,
                getStatus());
        if (hitPoints == 0) {
            System.out.println("  " + name + " has been defeated");
        }
    }

    public void heal() {
        if (numPotions > 0) {
            hitPoints = Math.min(maxHitPoints, hitPoints + 20);
            System.out.printf("  %s drinks healing potion (%s, %d potions left)\n", name,
                    getStatus(), --numPotions);
        } else {
            System.out.println("  You've exhausted your potion supply!");
        }
    }

    public boolean isAlive() {
        return hitPoints > 0;
    }

    public String getStatus() {
        return "Player HP: " + hitPoints;
    }

    @Override
    public String toString() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public static Player newInstance() {
        return new Player("Mighty Thor",
                "a musclebound hulk intent on crushing all evil in his way", 40, 6, 20, 10);
    }
}
EN

回答 3

Code Review用户

回答已采纳

发布于 2012-03-13 22:55:32

代码语言:javascript
复制
    private final Player player = Player.newInstance();

newInstance这样的函数是可疑的。我不明白你为什么不使用new Player。在某些情况下,您使用newRandomInstance,我更喜欢它,因为它告诉我您到底在做什么。

代码语言:javascript
复制
public static void main(String[] args) throws IOException {

让您的主要函数抛出一个IOException可能不是最好的主意。实际上,尽管与IO没有真正的关系,但是您已经拥有了抛出IOExceptions的各种函数。由于IOException实际上无能为力,我建议您在发生这种情况时捕获它们,然后重新抛出它们:

代码语言:javascript
复制
throw new RuntimeException(io_exception);

您不会用任何方式处理的异常信息来扰乱代码。

代码语言:javascript
复制
private final Map<Integer, Map<Integer, Room>> map = new HashMap<Integer, Map<Integer, Room>>();

在我看来,你最好用二维数组来保存地图,而不是这个。它将在许多地方简化您的代码。

代码语言:javascript
复制
    System.out.print("Where would you like to go :");
    if (northPossible) {
        System.out.print(" North (n)");
    }

正如Landei所说,最好将输入/输出与实际的游戏逻辑分开。

代码语言:javascript
复制
private Room currentRoom;
private int currentX = 0;
private int currentY = 0;

在我看来,这些是玩家的一部分,而不是地牢。

代码语言:javascript
复制
public void startQuest(Player player) throws IOException {
    while (player.isAlive() && !isComplete()) {
        movePlayer(player);
    }

一个名为startQuest的函数继续运行,直到玩家死亡或获胜,这有点奇怪。

代码语言:javascript
复制
private final static Random random = new Random();
private final static Set<Integer> monstersSeen = new HashSet<Integer>();

我建议避免静态变量。(常数很好)。当你使用静力学时,你会失去一些灵活性。在你的例子中,我认为你真的应该把这个逻辑放在一个工厂类中。另外,你真的不应该有一个特定于类的随机实例。您希望在所有对象中共享一个随机对象。

代码语言:javascript
复制
   if (roomsSeen.size() == NUM_ROOMS) {
        roomsSeen.clear();
    }
    int i;
    do {
        i = random.nextInt(NUM_ROOMS);
    } while (roomsSeen.contains(i));
    roomsSeen.add(i);

您多次做这个基本的事情,这意味着您应该考虑找到一种方法来编写一个类,您可以在这两种情况下使用。

代码语言:javascript
复制
    if (monster.isAlive()) {
        new Battle(player, monster);
    }

把行动作为创造对象的一面并不是个好主意。至少作为调用方法的结果发生了操作。

这里最重要的是将用户界面(读写控制台)与游戏逻辑本身分开。我指出的其他方面可以改进,但这才是最大的问题所在。

票数 14
EN

Code Review用户

发布于 2012-03-13 22:19:17

IO:您可以使用everywhere System.out.printlnSystem.in (BTW,ScannerReader方便得多)。即使您想要从控制台输出切换到一个简单的Swing应用程序,只需要一个文本区域,您也必须更改所有内容。同样的故事,如果你想为你的游戏提供翻译。

因此,遵循单一责任原则:像PlayerMonster这样的模型类应该关心游戏的状态及其转换,而不是IO。即使这不是一个完美的分离,发送String到一个IO类并请求它输入仍然比在本地完成所有事情要好。然后由IO类负责如何显示数据。稍后,您可能希望只向IO发送像PlayerDied()HealthDownTo(42)这样的消息,IO从文本文件中获得真正的输出。

房间:if (i == 0) {...级联用switch写得更好。在您的例子中,一个包含所有字符串的数组会更好,您只需要使用roomDescriptions[i]就可以得到正确的数组。

票数 7
EN

Code Review用户

发布于 2012-03-20 08:46:16

Java没有什么,只有纯粹的OO原则。

为什么玩家和怪物是完全不同的阶级?

应该有类"Objet“,然后它的后代”生物“(或其他什么),然后,从它,玩家和怪物。

代码语言:javascript
复制
     Object
       |
       |
       |
    Creature
       |
       /\
      /  \
     /    \
    /      \
   |        |
Player   Monster

生物可以移动,携带物品,拥有玩家和怪物共享的大量属性(攻击、防御、生命等等)。

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

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

复制
相关文章

相似问题

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