【问题标题】:Procedural Generated Rooms (Like Binding of Isaac)程序生成的房间(如 Isaac 的绑定)
【发布时间】:2025-11-26 09:55:02
【问题描述】:

我正在制作一个简单的程序生成地牢,您的角色在房间中生成,房间的门在随机方向上随机打开或关闭。这已经完成了。

当角色离开房间时,它会为新房间生成新的随机数。存储旧房间的号码并在角色向后移动时生成带有这些号码的房间。这部分还没有完成,但是很容易做,但我还不想这样做,因为它会导致下面的问题。

我怎样才能创建一些东西,无论玩家走到哪里都可以存储它并且知道何时使用变量?我怎样才能自动创建这样的变量?

当前代码:

public class Executable extends JFrame implements KeyListener {
Container contentPane=this.getContentPane();
Graphics bufferGraphics;

int xAxis; //Universal Variables:
int yAxis;
int characterX=463;
int characterY=486;
int oldCharacterX=463;
int oldCharacterY=486;
Image characterNorth = CustomImages.createImageIcon("Images/characterNorth.jpg").getImage();
Image characterEast = CustomImages.createImageIcon("Images/characterEast.jpg").getImage();
Image characterSouth = CustomImages.createImageIcon("Images/characterSouth.jpg").getImage();
Image characterWest = CustomImages.createImageIcon("Images/characterWest.jpg").getImage();
Image brickWall = CustomImages.createImageIcon("Images/brickWall.jpg").getImage();
Image brickFloor = CustomImages.createImageIcon("Images/brickFloor.jpg").getImage();
Image character=characterNorth;
boolean pressed=false;
static boolean northDoor;
static boolean eastDoor;
static boolean southDoor;
static boolean westDoor;
static boolean Randomizer=true;
static int north;
static int east;
static int south;
static int west;
static Random r=new Random();
boolean doorOpen=false;

public static void main(String[] args) { //Main
    north=r.nextInt(3)+1;
    east=r.nextInt(3)+1;
    south=r.nextInt(3)+1;
    west=r.nextInt(3)+1;
    if(north==1) {
        northDoor=true;
    }else {
        northDoor=false;
    }
    if(east==1) {
        eastDoor=true;
    }else {
        eastDoor=false;
    }
    if(south==1) {
        southDoor=true;
    }else {
        southDoor=false;
    }
    if(west==1) {
        westDoor=true;
    }else {
        westDoor=false;
    }
    Executable e=new Executable();
}

public Executable() { //Canvas
    this.setBounds(0, 0, 1680, 1050);
    this.setVisible(true);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    addKeyListener(this);
    setFocusable(true);
    requestFocusInWindow();
}

public void paint(Graphics g) { //Paint 

    for(xAxis=58;xAxis<=858;xAxis=xAxis+50) {
        for(yAxis=81;yAxis<=881;yAxis=yAxis+50) {
            g.drawImage(brickFloor,xAxis,yAxis,null);
        }
        yAxis=31;
    }
    for(xAxis=8;xAxis<958;xAxis=xAxis+50) {
        g.drawImage(brickWall,xAxis,yAxis,null);
    }
    yAxis=931;
    for(xAxis=8;xAxis<=908;xAxis=xAxis+50) {
        g.drawImage(brickWall,xAxis,yAxis,null);
    }
    xAxis=8;
    for(yAxis=81;yAxis<=881;yAxis=yAxis+50) {
        g.drawImage(brickWall,xAxis,yAxis,null);
    }
    xAxis=908;
    for(yAxis=81;yAxis<=881;yAxis=yAxis+50) {
        g.drawImage(brickWall,xAxis,yAxis,null);
    }

    while(!doorOpen) {
        if(northDoor==false && eastDoor==false && southDoor==false && westDoor==false) {
            north=r.nextInt(3)+1;
            east=r.nextInt(3)+1;
            south=r.nextInt(3)+1;
            west=r.nextInt(3)+1;
            if(north==1) {
                northDoor=true;
            }else {
                northDoor=false;
            }
            if(east==1) {
                eastDoor=true;
            }else {
                eastDoor=false;
            }
            if(south==1) {
                southDoor=true;
            }else {
                southDoor=false;
            }
            if(west==1) {
                westDoor=true;
            }else {
                westDoor=false;
            }
        }else {
            doorOpen=true;
        }
    }

    if(northDoor) {
        g.drawImage(brickFloor,458,31,null);
    }
    if(eastDoor) {
        g.drawImage(brickFloor,908,481,null);
    }
    if(southDoor) {
        g.drawImage(brickFloor,458,931,null);
    }
    if(westDoor) {
        g.drawImage(brickFloor,8,481,null);
    }

    g.drawImage(character,characterX,characterY,null);
}

@Override
public void keyPressed(KeyEvent arg0) { //Character rotation and movement.
    if(pressed==false) {
        pressed=true;
        if(arg0.getKeyCode() == KeyEvent.VK_W){
            if(character==characterNorth) {
                if(characterY>86 && characterX>13 && characterX<913) {
                    characterY=characterY-50;
                }else if(northDoor && characterX==463) {
                    oldCharacterY=characterY;
                    characterY=characterY-50;
                }
            }else {
                character=characterNorth;
            }
        }
        if(arg0.getKeyCode() == KeyEvent.VK_A){
            if(character==characterWest && characterY>36 && characterY<926) {
                if(characterX>63) {  //and y is greater than and less than
                    oldCharacterX=characterX;
                    characterX=characterX-50;
                }else if(westDoor && characterY==486) {
                    oldCharacterX=characterX;
                    characterX=characterX-50;
                }
            }else {
                character=characterWest;
            }
        }
        if(arg0.getKeyCode() == KeyEvent.VK_S){
            if(character==characterSouth) {
                if(characterY<871 && characterX>13 && characterX<913) {
                    oldCharacterY=characterY;
                    characterY=characterY+50;
                }else if(southDoor && characterX==463) {
                    oldCharacterY=characterY;
                    characterY=characterY+50;
                }
            }else {
                character=characterSouth;
            }
        }
        if(arg0.getKeyCode() == KeyEvent.VK_D){
            if(character==characterEast && characterY>36 && characterY<926) {
                if(characterX<848) {  //and y is greater than and less than
                    oldCharacterX=characterX;
                    characterX=characterX+50;
                }else if(eastDoor && characterY==486) {
                    oldCharacterX=characterX;
                    characterX=characterX+50;
                }
            }else {
                 character=characterEast;
            }
        }
        repaint(oldCharacterX,oldCharacterY,40,40);
        repaint(characterX,characterY,40,40);
        }
    }

@Override
public void keyReleased(KeyEvent arg0) { //Prevents keys from being held down.
    if(arg0.getKeyCode() == KeyEvent.VK_W){
        pressed=false;
    }
    if(arg0.getKeyCode() == KeyEvent.VK_A){
        pressed=false;
    }
    if(arg0.getKeyCode() == KeyEvent.VK_S){
        pressed=false;
    }
    if(arg0.getKeyCode() == KeyEvent.VK_D){
        pressed=false;
    }
}

@Override
public void keyTyped(KeyEvent arg0) {
    // TODO Auto-generated method stub

  }
}

【问题讨论】:

    标签: java eclipse procedural


    【解决方案1】:

    希望这会有所帮助。我建议你有一个游戏状态,它处理绘图、移动、关键事件,比如你当前的Executable 类。

    以下内容的基本概述。 GameState 引用了 currentRoomState,这是您应该认为角色所在的位置。移动到门后,您需要检查这一点,使用适当的参数调用 currentRoomState.MoveToNextRoom()

    这将触发当前房间状态生成一个新房间,将新房间的引用设置为绑定到正确的门,将角色放置在新房间的正确门上,将引用绑定到旧房间在新房间中,并将当前房间设置为新房间。

    当您移动到与以前的房间绑定的门时,变量将被保留。

    祝你好运

    -克里斯

    例如:

    class GameState{
                RoomState startingRoomState = new RoomState();
                RoomState currentRoomState = startingRoomState;
    
                public void move(int x, int y)
                {
                    currentRoomState.iCharacterPosX += x;
                    currentRoomState.iCharacterPosY += y;
    
                    // Check if need to move to a new room (eg move north)
                    currentRoomState = currentRoomState.MoveToNextRoom(true, false, false, false);
                }
    
                public void draw()
                {
                    //Based on the character pos and draw variables in currentRoomState
                }
    }
    

    然后你有一个房间状态,它描述了一个房间

    class RoomState{
                    //Where the doors are
                    boolean northDoor = false;
                    boolean eastDoor = false;
                    boolean southDoor = false;
                    boolean westDoor = false;
    
                    // Reference to the room adjacent rooms
                    RoomState rs_NorthDoor = null;
                    RoomState rs_EastDoor = null;
                    RoomState rs_SouthDoor = null;
                    RoomState rs_WestDoor = null;
    
                    int iCharacterPosX = 0;
                    int iCharacterPosY = 0;
    
                    // Initial
                    RoomState(){
                        northDoor = true; eastDoor = true; southDoor = true; westDoor = true;
                    }
    
                    RoomState(RoomState prevState, boolean north, boolean east, boolean south, boolean west){
                        // Randomise door states
    
                        // Put character in the position he should be
                        iCharacterPosX = 0;//right most x Coord - oldX
                        iCharacterPosX = 0;//bottom most y Coord - oldy
    
                        if(north)
                        {
                            rs_NorthDoor = prevState;
                        } //elseif other doors
                    }
    
                    public RoomState MoveToNextRoom(boolean north, boolean east, boolean south, boolean west){
                        if(north){
                            if(rs_NorthDoor == null)
                            {
                                rs_NorthDoor = new RoomState(this, north, east, south, west);
                            }
                            return rs_NorthDoor;
                        } //else if the others
    
                        return null;
                    }
                }
    

    【讨论】:

      【解决方案2】:

      如果您要存储与每个房间关联的随机数,然后按顺序向后遍历随机数,堆栈就是您要查找的数据结构。要获得对堆栈的概念性理解,请参阅Stack (abstract data type)。简而言之,堆栈就像一堆文件,其中放在堆顶部的最后一项是遍历文件时从堆中删除的第一个元素,称为后进先出 (LIFO) 排序。

      在 Java 中,Deque(双端队列,读作“deck”)可以用作堆栈。要将一个元素放在栈顶,你使用push方法,从栈顶移除一个元素,你使用pop方法(注意pop都获得了栈顶的值堆栈并将其从堆栈中删除)。例如:

      Deque<Integer> stack = new ArrayDeque<>();
      stack.push(10);
      stack.push(20);
      System.out.println(stack.pop());
      stack.push(30);
      System.out.println(stack.pop());
      System.out.println(stack.pop());
      

      此代码将产生以下输出:

      20
      30
      10
      

      要封装此功能,您可以创建一个名为 RoomTracker 的新类(或类似名称以表示跟踪先前房间的操作)并按如下方式实现它:

      public class RoomTracker {
      
          private final Deque<Integer> rooms = new ArrayDeque<>();
      
          public void addRoomNumber(Integer roomNumber) {
              rooms.push(roomNumber);
          }
      
          public Integer getPreviousRoomNumber() {
              return rooms.pop();
          }
      }
      

      以与以前类似的方式,您可以按如下方式使用此代码:

      RoomTracker tracker = new RoomTracker();
      tracker.addRoomNumber(10);
      tracker.addRoomNumber(20);
      System.out.println(tracker.getPreviousRoomNumber());
      tracker.addRoomNumber(30);
      System.out.println(tracker.getPreviousRoomNumber());
      System.out.println(tracker.getPreviousRoomNumber());
      

      这将产生以下输出:

      20
      30
      10
      

      如果您需要跟踪您进入房间的方向(以便您找到玩家是否要去上一个房间),您可以创建一个名为 Room 的类并存储随机生成的数字和方向玩家进入的地方:

      public enum Direction {
          TOP, RIGHT, BOTTOM, LEFT;
      }
      
      public class Room {
      
          private final int number;
          private final Direction enteredFrom;
      
          public Room(int number, Direction enteredFrom) {
              this.number = number;
              this.enteredFrom = enteredFrom;
          }
      
          public int getNumber() {
              return number;
          }
      
          public Direction getEnteredFrom() {
              return enteredFrom;
          }
      }
      

      然后您可以根据Room 重新定义RoomTracker

      public class RoomTracker {
      
          private final Deque<Room> rooms = new ArrayDeque<>();
      
          public void addRoom(Room room) {
              rooms.push(room);
          }
      
          public Room getPreviousRoom() {
              return rooms.pop();
          }
      }
      

      然后,当玩家从一个房间移动到另一个房间时,您可以将 Room 对象推入堆栈。例如:

      RoomTracker tracker = new RoomTracker();
      tracker.push(new Room(10, Direction.LEFT);   // Travel right into room 10
      tracker.push(new Room(20, Direction.TOP);    // Travel down into room 20
      

      【讨论】: