Хороший способ сделать это на современном языке OO C-like? - PullRequest
5 голосов
/ 26 мая 2009

У меня есть Tile s, которые представляют плитки в двухмерном мире игры. У плиток могут быть стены на любом количестве их 4 сторон. У меня сейчас что-то вроде этого:

interface Tile {
    boolean isWallAtTop();
    boolean isWallAtRight();
    boolean isWallAtLeft();
    boolean isWallAtBottom();
}

Где-то еще у меня также есть 16 изображений, по одному для каждой возможной конфигурации плитки. Как то так:

static final Image WALLS_ALL_AROUND = ...
static final Image WALL_ON_TOP_AND_RIGHT = ...
/* etc etc all 16 possibilities */

Я хочу написать

static Image getWallImage(Tile tile)

То, чего я бы хотел избежать , - это пытка использования таких возможностей, как

if (tile.isWallTop && tile.isWallRight 
    && !tile.isWallBottom && !tile.isWallLeft) {
    return WALL_ON_TOP_AND_RIGHT;
}

Кто-нибудь знает более симпатичный способ сделать это?

Ответы [ 7 ]

15 голосов
/ 26 мая 2009

Иди иди гаджет битовых масок. Используйте 4-битную маску для каждой плитки, указав, какая сторона имеет стену.

A B C D

Бит A обозначает стену сверху, B справа, C снизу, D слева. Определите константы, чтобы вы могли логически пересекаться с маской, т. Е.

if (tile.Walls & (WALL_LEFT | WALL_RIGHT))
  // Do stuff

Для поиска изображения эта 4-битная маска дает 16 возможностей. Используйте его как указатель на «массив» изображений, чтобы вы могли без труда найти правильное изображение.

4 голосов
/ 26 мая 2009

Есть ли у плиточных объектов какие-либо другие свойства? Если нет (или если вы можете выделить их), вы можете превратить сами объекты мозаики в перечисление 16 констант с помощью метода Tile.getImage(), который возвращает фиксированное изображение, переданное конструктору. Это известно как шаблон Flyweight :

class Tile {
    public final boolean isWallAtTop;
    public final boolean isWallAtRight;
    public final boolean isWallAtLeft;
    public final boolean isWallAtBottom;
    public final Image image;

    private Tile(boolean top, boolean right, boolean left, 
                 boolean bottom, Image image)
    {
        this.isWallAtTop = top;
        this.isWallAtRight = right;
        this.isWallAtLeft = left;
        this.isWallAtBottom = bottom;
        this.image = image;
    }

    public static final Tile WALLS_ALL_AROUND = 
        new Tile(true, true, true, true, new Image("allWalls.png"))
    // more constants here, plus other methods that work with
    // only the wall data
}

В Java вы даже можете реализовать это как «реальное» перечисление.

Для карты, состоящей из плиток, у вас может быть простой двумерный массив ссылок на плитки, или, если вам нужны другие данные для отдельных плиток, есть другой класс SpecificTile, который содержит «другие данные» и ссылка на один из объектов Tile выше.

2 голосов
/ 26 мая 2009

Я предлагаю создать перечисление битового флага, как показано ниже.

[Flags]
public enum WallLocations
{
    None = 0,
    Left = 1,
    Right = 2,
    Top = 4,
    Bottom = 8
}

Затем вы можете использовать словарь для сопоставления местоположений стен с изображениями.

Dictionary<WallLocations, Image> map = new Dictionary<WallLocations, Image>();

map.Add(WallLocations.None, image0000);
map.Add(WallLocations.Left, image1000);
map.Add(WallLocations.Right, image0100);
map.Add(WallLocations.Top, image0010);
map.Add(WallLocations.Bottom, image0001);
map.Add(WallLocations.Left | WallLocations.Right, image1100);
// ....

По крайней мере, в C # вы также можете расширить определение enum на все 16 случаев.

[Flags]
public enum WallLocations
{
    None = 0,

    Left = 1,
    Right = 2,
    Top = 4,
    Bottom = 8,

    LeftAndRight = Left | Right,
    LeftAndTop = Left | Top,
    LeftAndBottom = Left | Bottom,
    RightAndTop = Right | Top,
    RightAndBottom = Left | Bottom,
    TopAndBottom = Top | Bottom,

    AllExceptLeft = Right | Top | Bottom,
    AllExceptRight = Left | Top | Bottom,
    AllExceptTop = Left | Right | Bottom,
    AllExceptBottom = Left | Right | Top,

    All = Left | Right | Top | Bottom
}
0 голосов
/ 26 мая 2009

Приличное ОО-решение, вероятно, будет включать шаблон стратегии . Одна из возможных реализаций будет иметь класс WallConfiguration и WallFactory для их создания. Плитка будет тогда содержать WallConfiguration. Реализация (стиль C ++) будет выглядеть примерно так:

class Tile
{
private:
    WallConfiguration walls;
    // Other Tile data

public:
    enum Walls
    { 
        TOP,
        BOTTOM,
        LEFT,
        RIGHT,
        TOP_RIGHT,
        TOP_LEFT
        // Fill in the rest
    }; //Enums have a place!
    Tile(Walls wall)
    {
        walls = WallFactory.getConfiguration(wall);
    }

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

0 голосов
/ 26 мая 2009

Вместо создания WALLS_ALL_AROUND изображения сделайте его объектом, включающим в себя изображение, а также любую другую логику, необходимую для работы с разрешенными движениями и тому подобным. В объекте Location вы можете просто установить для свойства walls значение WALLS_ALL_AROUND, которое вы можете запросить для изображения и иметь дело с вашей другой связанной со стеной логикой.

Поскольку для каждого набора изображений требуется только шестнадцать экземпляров этих (неизменяемых) объектов (каждый из которых, вероятно, представляет собой одноэлементный подкласс Tile, если у вас только один набор изображений), вы также экономите память.

0 голосов
/ 26 мая 2009

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

0 голосов
/ 26 мая 2009

Не можете ли вы сделать что-нибудь с перечислением флагов?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...