Генерация 2D-тайлов над головой

У меня есть этот код прямо сейчас:

private void generateLevel() {
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            tiles[x + y * width] = random.nextInt(4);
        }
    }
}

Что позволяет этому методу работать:

public Tile getTile(int x, int y) {
    if (x < 0 || y < 0 || x >= width || y >= height) return Tile.voidTile;
    if (tiles[x + y * width] == 0) return Tile.grass;
    if (tiles[x + y * width] == 1) return Tile.stone;
    else return Tile.water; 
    //return Tile.voidTile;
}

Что возвращает этот результат: 2D Tile Generation

Чего я хочу, так это гладких, округлых островов с случайными отложениями камней здесь и там. Будет ли шум перлина излишним для этого? Я подумал, что могу просто сгенерировать плитку, проверить идентификатор плитки рядом с ней, а затем поместить ее вниз, если соседняя плитка имеет тот же тип. Но это создало бы бесконечные просторы одной и той же плитки. Любая помощь?


person Community    schedule 16.12.2012    source источник
comment
Почему бы немного не абстрагировать свой код? Сначала разместите свои острова (которые могут быть новым классом) и определите их радиус/границу. Затем вы можете случайным образом размещать свои месторождения внутри границы острова и добавлять воду везде. Не говоря уже о том, что это упростит размещение граничных плиток.   -  person sdasdadas    schedule 16.12.2012
comment
Извините, я очень новичок во всей этой плитке, как мне разместить граничные плитки? Я, наверное, могу понять остальное.   -  person    schedule 16.12.2012
comment
Я немного расширим ответ.   -  person sdasdadas    schedule 16.12.2012
comment
Я ответил, но ни в коем случае не являюсь экспертом. Вы также можете попробовать это для справки: questions/4703033/2d-tile-map-generation?rq=1   -  person sdasdadas    schedule 16.12.2012
comment
Похожий пример есть здесь.   -  person trashgod    schedule 16.12.2012


Ответы (1)


Первый шаг, который я бы сделал, — создать объекты из чего угодно на уровне вашего домена:

public class Island {
    private Point2D center = null;
    private int radius = 0;

    private List<Deposit> deposits = new ArrayList<Deposit>();

    public Island(Point2D center, int radius) {
        this.center = center;
        this.radius = radius;
    }

    public void generateDeposits(int numDeposits) {
        for (int i = 0; i < numDeposits; i++) {
            // TODO: I leave it to you to find an x and y inside the island's
            // boundary.
            int x = getIntInsideCircle(center, radius);
            int y = getIntInsideCircle(center, radius);
            if (!depositInLocation(x, y)) {
                deposits.add(new StoneDeposit(x, y));
            } else {
                i--; // TODO: This code could potentially go on forever,
                     // if we keep generating locations that have been used, 
                     // but I'll leave this for you to handle.
            }
        }
    }
}

public abstract class Deposit {
    private Point2D location = null;

    public Deposit(Point2D location) {
        this.location = location;
    }
}

public class StoneDeposit extends Deposit {
    // TODO: You can fill this with StoneDeposit specifics.
}

Теперь у нас есть код, который будет генерировать остров со всеми его случайными отложениями. Осталось только разместить эти острова. Я собираюсь сделать это просто и добавить только один на карту, но я уверен, что вы можете понять, как добавить более одного (я оставлю несколько комментариев с моими идеями):

public class Map {
    private final int WIDTH = 1000;
    private final int HEIGHT = 1000;

    private List<Island> islands = new ArrayList<Island>();

    public void generate() {
        // TODO: If you want to make more, make a for loop.
        int radius = 100;
        Island island = new Island(new Point2D(WIDTH / 2, HEIGHT / 2), radius);
        // TODO: If you are going to add more, then you can't simply add them
        // all willy-nilly. You are going to have to check if the islands collide
        // and, if they do, find a way to handle that.
        // You could let them collide and create a mountain range where they do, or,
        // you could try to place the next island in a different position (similar
        // to what we used above placing deposits, but both situations require
        // code a bit better than what I've included).
        islands.add(island);
     }
}

Хорошо, теперь у нас есть все необходимые данные. Это подводит нас к финальной точке — рисованию на экране с помощью тайлов. Я не слишком разбираюсь в этом вопросе, так что это может быть неэффективно, но оно должно послужить отправной точкой.

Некоторые функции я обобщил (например, drawTile(int x, int y, тип TileType), потому что я не знаю, как вы рисуете плитки на экране).

// Generate our data.
Map map = new Map();
map.generate();
// Draw to the screen.

// 1. Fill the entire screen with water.
for (int y = 0; y < HEIGHT; y++) {
    for (int x = 0; x < WIDTH; x++) {
        drawTile(x, y, Type.WATER);
    }
}

// 2. Draw the islands.
// We're going to use this algorithm to draw the circle:
// http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
for (Island island : map.getIslands()) {
    int f = 1 - island.getRadius();
    int ddF_x = 1;
    int ddF_y = -2 * island.getRadius();
    int x = 0;
    int y = 0;

    Point2D center = island.getCenter();
    int radius = island.getRadius();
    drawTile(center.getX(), center.getY() + radius, TileType.LAND);
    drawTile(center.getX(), center.getY() - radius, TileType.LAND);
    drawTile(center.getX() + radius, center.getY(), TileType.LAND);
    drawTile(center.getX() - radius, center.getY(), TileType.LAND);

    while(x < y) {
        if(f >= 0) {
          y--;
          ddF_y += 2;
          f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;    
        drawTile(center.getX() + x, center.getY() + y, TileType.LAND);
        drawTile(center.getX() - x, center.getY() + y, TileType.LAND);
        drawTile(center.getX() + x, center.getY() - y, TileType.LAND);
        drawTile(center.getX() - x, center.getY() - y, TileType.LAND);
        drawTile(center.getX() + y, center.getY() + x, TileType.LAND);
        drawTile(center.getX() - y, center.getY() + x, TileType.LAND);
        drawTile(center.getX() + y, center.getY() - x, TileType.LAND);
        drawTile(center.getX() - y, center.getY() - x, TileType.LAND);
      }

      // TODO: Now you have to figure out how to fill in the guts of the island.
    }

// 3. Draw the deposits.
// TODO: I'll leave this one for you.

Так что в принципе так - это не так уж и плохо.

Вы всегда можете пойти дальше и добавить плитки, которые в основном состоят из воды, но с береговой линией. Для этого вам нужно будет проверить, является ли плитка, которую вы рисуете, краем или углом:

+-+-+-+
|1|2|3|
+-+-+-+
+-+-+-+-+
|4|5|6|7|
+-+-+-+-+
+-+-+-+
|8|9|0|
+-+-+-+

Здесь вы можете видеть 2,9 и 4 — все плитки с краями. 1, 3, 8, 0 — все угловые плитки, а 5 — внутренняя плитка. Когда вы распознаете плитку как угол, вам нужно выбрать все прикрепленные плитки воды и нарисовать их как плитки «побережья»:

  +-+-+
  | |x|
  +-+-+-+-+
  |x|1|2|3|
  +-+-+-+-+
    +-+-+-+-+
    |4|5|6|7|
    +-+-+-+-+
    +-+-+-+
    |8|9|0|
    +-+-+-+

Там все иксы будут прибрежными тайлами.

Надеюсь, это немного поможет.

person sdasdadas    schedule 16.12.2012
comment
Однако я понимаю большую часть ответа, как мне получить x и y внутри острова в классе острова? Учитывая данные, которые у меня есть, я не могу придумать способ, если я просто не использую центральную переменную. - person ; 16.12.2012
comment
stackoverflow.com/ вопросы/5837572/ - person sdasdadas; 16.12.2012