Java Roguelike notes (2/13): Creating a procedurally generated dungeon

The algorithm that creates a procedural map is based on my previous post and algorithm in C# , most of work was trying to adapt it to Java and had to take different approaches until I could finally translate it. The process goes as follows:

  1. We generate a basic Tile Board, where each point is a coordinate. Here comes the first difference, while in C# I was using Vector2, in Java I used Point2D.
  2. After the Tile map base is up and running, we set the Floors. Why Floors first? I’ll be using a Walkers algorithm: A bunch of Walkers will be initiated, and on each iteration they’ll go a different direction, creating the floors and opening up the map. This also assures that all rooms are reachable.
  3. Now we generate the Walls, which are the remaining Tiles after the Floors had been set.

The GameBoard:

       System.out.println("[Board - General] WIDTH: " + SCREEN_WIDTH/UNIT_SIZE + "x HEIGHT: " + SCREEN_HEIGHT/UNIT_SIZE);
        // 1. Global board:
        for (int x=0; x<SCREEN_WIDTH; x += 25){
            for (int y=0; y<SCREEN_HEIGHT; y += 25){
                Point2D _tilePos = new Point2D.Double(x,y);
                allPositions.add(_tilePos);
            }
        }
        System.out.println("[Board - All positions] " + allPositions.size() + " tile positions");

The Floors:

    public void setFloors(){

        System.out.println("[Board - Floor] Setting up floors...");
        // 1 - Create Walkers:
        for (int i=0; i < _initialNumberOfWalkers; i++){
            Walker _walker = new Walker();
            _walker.walkerPosition = new Point2D.Double(12.0,12.0); 
            listOfWalkers.add(_walker);

        }
        System.out.println("[Board - Floor] Walkers: " + listOfWalkers.size());

        // 2 - Set Walkers to walk randomly, and create the floor map
        for (int i=0; i < _initialNumberOfWalkerIterations; i++){
            walkerBehaviour(listOfWalkers);
        }
        System.out.println("[Board - Floor] Floor tile positions: " + listOfFloorTilePositions.size());
        System.out.println("[Board - Floor] Floor tiles: " + listOfFloorTiles.size());
        System.out.println("[Board - Floor] Floor Complete");
    }

The Walls:

    void setWalls(){
        List<Point2D> _normalizedFloorPos = new ArrayList<>();
        for (int i = 0; i < listOfFloorTilePositions.size(); i++){
            _normalizedFloorPos.add(helperUnitMultiplier(listOfFloorTilePositions.get(i).getX(), listOfFloorTilePositions.get(i).getY()));
        }
        // 1. Now we find the differences: Walls = All - Floors:
        List<Point2D> _wallPos = new ArrayList<>(allPositions);
        _wallPos.removeAll(_normalizedFloorPos);

        // 2. We generate a new list of Tiles for each valid position:
        for (int i = 0; i < _wallPos.size(); i++){

            Double _wallX = _wallPos.get(i).getX();
            Double _wallY = _wallPos.get(i).getY();

            Tile _wallTile = new Tile();
            _wallTile.tilePosition = new Point2D.Double(_wallX,_wallY);
            listOfWallTiles.add(_wallTile); // Tile to Tile list
            listOfOccupiedTilePositions.add(_wallTile.tilePosition); //Position to Position (All) list
        }

        System.out.println("[Board - Walls] Drawing walls...");
        System.out.println("[Board - Walls] Wall tiles: " + listOfWallTiles.size());
        System.out.println("[Board - Walls] Floor Complete");


    }

The Walkers:

    public void walkerBehaviour(List<Walker> _inputListOfWalkers){

        for (int i=0; i<_inputListOfWalkers.size();i++){
            // Walkers new position will be a random direction from current position:
            Point2D _walkerPos = _inputListOfWalkers.get(i).walkerPosition;
            _inputListOfWalkers.get(i).walkerPosition = randomDirection(_walkerPos);

        }
    }
public Point2D randomDirection(Point2D _currentWalkerPosition){

        // 1. Get current walker position
        Double _walkerX = _currentWalkerPosition.getX();
        Double _walkerY = _currentWalkerPosition.getY();

        // 2. draw() will be responsible of drawing the List, we jump into Walker's Direction now:
        Random _randomRange = new Random();
        int _r = _randomRange.nextInt(4);

        switch (_r){
            case 0:
                _walkerY += 1.0; // up
                break;
            case 1:
                _walkerY -= 1.0; // down
                break;
            case 2:
                _walkerX += 1.0; // left
                break;
            case 3:
                _walkerX -= 1.0; // right
                break;
        }

        // 3. Boundaries, if walker hits these tiles, turn back:
        if (_walkerX < 0.0 ) { //Checks X-- and turns the walker around
            _walkerX = _walkerX * -1;
        }
        if (_walkerY < 0.0){ // Checks Y-- and turns the walker around
            _walkerY = _walkerY * -1;
        }

        // 4. Set new walker position:
        _currentWalkerPosition.setLocation(_walkerX, _walkerY);
        // 4.5 Check for duplicates and skip if needed.
        if (listOfOccupiedTilePositions.contains(_currentWalkerPosition)){
            assert true; // If this position already exist in our list, do nothing,
        } else {
            // 5. Place a new Tile as the walkers move into each new position, and save coordinates:
            Tile _tile = new Tile();
            _tile.tilePosition = new Point2D.Double(_walkerX,_walkerY);
            listOfFloorTiles.add(_tile); // Tile to Tile list
            listOfFloorTilePositions.add(_tile.tilePosition); // Position to Position (Floor) list
            listOfOccupiedTilePositions.add(_tile.tilePosition); // Position to Position (All) list
        }


        return _currentWalkerPosition;
    }

Caveats:

  • Was important to set different lists for coordinates, and for the object itself. This was pretty different from C# where I could operate with the same one.
  • Important to double check your UNIT_SIZE, as if any of the Tiles have the wrong one it will mess with List operations when generating the map, like .removeAll(), .commons(), and others.
  • I seem unable to make work some built-in methods, this is most likely my lack of understanding of how Java works and trying to assign variables to Null, for example here I wasn’t expecting I had to instantiate a new Point2D but just assign the previous assignments of X and Y to the object via .setLocation():
// Will not work:
                Tile _wallTile = new Tile();
                int _wallXPos = (int)allPositions.get(i).getX();
                int _wallYPos = (int)allPositions.get(i).getY();
                _wallTile.tilePosition.setLocation(_wallXPos,_wallYPos);
// Will work:
                Tile _wallTile = new Tile();
                int _wallXPos = (int)allPositions.get(i).getX();
                int _wallYPos = (int)allPositions.get(i).getY();
                _wallTile.tilePosition = new Point2D.Double(_wallXPos,_wallYPos);

One thought on “Java Roguelike notes (2/13): Creating a procedurally generated dungeon

Leave a Reply