Galaxy2D Tutorials Галактика 2D
Введение в игры, построенные на основе клеток

Предисловие

 

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

 

На уровне растровой картинки, каждый пиксель - это индекс соответствующего цвета в палитре. А на уровне карты из клеток, каждая клетка представляет индекс в Вашем заранее созданном массиве или коллекции изображений клеток. Это похоже на работу в текстовом режиме, в котором один байт (более точно два байта, второй байт задает атрибуты символа) определяет символ, который на экране представлен картинкой этого символа.

 

Клетка – это всего лишь картинка заданного размера. Так что основная цель использования подобных клеток при создании игр – это сделать процесс более абстрактным. То есть вместо того, чтобы работать с картой заданной изображением некоторого фиксированного размера, Вы можете работать с массивом из номеров клеток, из которого можно выложить изображение карты любого размера и просто вывести на экран только часть ее. Помимо этого можно задать атрибуты каждой клетки и накладывать клетки друг на друга, что позволит создавать динамические карты.

 

Как это использовать?

 

Помните старую игру Packman? Там игрок находится в лабиринте, заполненным пирогами. Он должен съесть все пироги, избегая столкновения с четырьмя призраками, которые его преследуют. Вы бы могли реализовать подобную игру, просто нарисовав лабиринт обычными средствами. Но как только станет необходимо проверить столкнулся ли игрок с пирогом, стеной или призраками, возникнут проблемы.

 

Можно предложить такой способ использования клеток: построить лабиринт из определенного набора блоков. К примеру, стенка, угол или пирог. Так что наша проблема исчезает – теперь мы запросто проверяем нужное поле, и, если его индекс указывает на пирог или стену, производим нужное действие.

 

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

 

Отображение и прокрутка

 

Для начала зададим некоторые константы:

 

        #define TILE_WIDTH  16

        #define TILE_HEIGHT 16

        #define TILE_COUNT_X SCREEN_WIDTH  / TILE_WIDTH

        #define TILE_COUNT_Y SCREEN_HEIGHT / TILE_HEIGHT

 

Здесь был выбран размер клетки равный 16х16, но Вы можете взять и 10x10, 32x32, 20x15, 32x10 и так далее. Размер полей зависит только от того, как Вы намереваетесь их использовать. Две последние константы содержат число клеток по осям, отображаемым на видимой части экрана. Ну а вывести их очень просто:

 

        bitmap *tile[100];                              /* 100 клеток */

        unsigned char map[MAP_WIDTH][MAP_HEIGHT];       /* Карта */

 

        for (y=0; y < TILE_COUNT_Y; y++)

                for (x=0; x < TILE_COUNT_X; x++)

                     blit_tile(x*TILE_WIDTH, y*TILE_WIDTH, tile[map[x][y]]);

 

Blit_tile (x, y, tile *n) выводит клетку n в позицию (x, y). В вашей графической библиотеке должна быть функция вывода прямоугольных областей на экран.

 

Прокрутка

 

Для того чтобы иметь, возможность плавно прокручивать на экране мир из клеток, необходимы некоторые усовершенствования. Прежде всего, нужен двойной буфер (он, конечно, не обязателен, но зато с ним прокрутка происходит плавней и быстрее) с шириной screensize + 2*tile_width и высотой screenheight + 2*tile_height. Вот отрывок кода, который описывает двойной буфер и производит прокрутку.

 

/*

 * Скроллинг карты на основе клеток

 * Нужна библиотека JLib для компиляции

 */

#define TILE_COUNT_X (SCREEN_WIDTH  / TILE_WIDTH )+2*TILE_WIDTH

#define TILE_COUNT_Y (SCREEN_HEIGHT / TILE_HEIGHT)+2*TILE_HEIGHT

 

void render_map(buffer_rec *buff,

                UBYTE *map,

                USHORT x, USHORT y);

 

/*

   ...

 */

 

main()

{

    int x   =0, y   =0,

        ox  =0,oy   =0;

    /*

        ...

     */

 

    /* Инициализировать двойную буферизацию */

    buffer=buff_init(SCREEN_WIDTH+ 2*TILE_WIDTH, SCREEN_HEIGHT+2*TILE_HEIGHT);

 

    /* Скроллинг на 10 клеток влево и 10 вниз */

    while (x <10)

    {

        /* Увеличить x смещение */

        ox++;

        if (ox == TILE_WIDTH)

        {

            /* Если смещение взять больше, чем ширина клетки,

             * увеличить позицию x и сбросить ox

             */

            x++;

            ox=0;

        }

 

        oy++;

        if (ox == TILE_WIDTH)

        {

            /* Тоже для y

             */

            y++;

            oy=0;

        }

 

        /* визуализация мира */

        render_map(buffer, map, x, y);

 

        /* Сделаем плавный скроллинг экрана только на SCREEN_WIDTH *

         * SCREEN_HEIGHT пикселей, начиная с заданного смещения

         */

        screen_blit_buff_to(0,0,    /* Левый верхний угол экрана */

                            buffer, /* Взять данные отсюда */

                            ox,oy,  /* Позиция в буфере */

                            SCREEN_WIDTH

    }

}

 

void

render_map(buffer_rec *buff,UBYTE *map,USHORT x, USHORT y)

{

    int px,py,      /* Позиция пикселя   */

        tx,ty;      /* Позиция на карте  */

 

    /* (px/py) – позиция следующей клетки (map[tx][ty]), которая будет выведена */

    for (px=0, tx=x; tx < TILE_COUNT_X; px+=TILE_WIDTH, tx++)

        for (py=0, ty=y; ty < TILE_COUNT_Y; py+=TILE_HEIGHT, ty++)

            buff_blit_buff_toNC(buff,px,py,tile[map[tx][ty]],0,0,TILE_WIDTH,TILE_HEIGHT);

}

 

Надеюсь, эта статья показала Вам, как правильно использовать клетки в своих играх и дала толчок к Вашему дальнейшему творчеству с помощью Jlib.

 

Верстка и поддержка Lennart Steinke

 

PMG  5 января 2004 (c)  Сергей Анисимов, Сергей Иванов