Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Как самому создать компьютерную игру #15


Служба Рассылок Subscribe.Ru
Как самому создать компьютерную игру
Выпуск 15

Сайт рассылки - www.gamemaker.ru 
Адрес для обратной связи - gamemaker@pisem.net

Приветствую всех, кто читает эту рассылку!

Сегодня в выпуске:
1. Профессиональный хостинг от 0.85$ в месяц.
2. Проекты на GameMaker.ru
3. Соглашение.
4. Спрайты.
5. Послесловие.



Профессиональный хостинг.

      Когда я в первый раз захотел разместить свой сайт на платном хостинге, меня ждало разочарование. Цены за месяц "обслуживания" были в среднем 10$. Дороговато. Пускай даже сейчас они упали до 8-6$, все равно для обычного пользователя интернета, захотевшего разместить свой сайт в сети это неприемлемо. Но сейчас появилось новое решение, которое, возможно, Вас заинтересует. Разместить свой сайт в сети за 0.85$ в месяц могут ТОЛЬКО подписчики рассылки "Как самому создать компьютерную игру", т.е. Вы. Кстати, одноименный сайт тоже выбрал этот хостинг.
      Если Вас хоть немного это заинтересовало, смотрите подробности на www.gamemaker.ru/host.


Проекты на GameMaker.ru

      Неделю назад читателям предлагалось придумать свой игровой сценарий. Пора подвести некоторые промежуточные итоги. (Кто не в курсе, зайдите на сайт в раздел "Форум"->"Сценарии игр")
      Во-первых, есть люди, которые готовы что-то делать. Это хорошо. Во-вторых, есть из чего выбрать сценарий для будущей игры. И, наконец, я понял, что это Вам тоже интересно.
      Теперь остался вопрос о том, какой сценарий выбрать. Мне лично нравиться "Менеджер единоборств", но, по общей реакции посетителей сайта, лидируют два проекта (см. на сайте "Форум"->"Сценарии игр"): автосимулятор и стратегия. Результаты (когда хоть что-то можно посмотреть и запустить) будут быстрее в первом случае, чем во втором. Но для начала нужно сделать какую-то основу (движок), которую потом можно переделать под конкретный сценарий. Предлагаю начать сразу два эти проекта. Предлагаю свою скромную кандидатуру на роль координатора. Возможно, потом смогу доверить эту роль кому-нибудь из Вас. Итак, первые два проекта сайта GameMaker.ru - это разработка автосимулятора и стратегии. Язык программирования - C++. Жду Ваших окончательных заявок на участие в любом из этих проектов. Если Вы хотите делать автосимулятор, то присылайте письмо с темой "Avto", если стратегию - "Strategy". В письме укажите, каким образом Вы хотели бы участвовать в проекте (быть художником, музыкантом, коде! ром, программистом ai итд).
      И последнее. Если у кого-нибудь есть незаконченный проект, который по каким-то причинам не был завершен, пишите. Может быть, вместе мы сможем вдохнуть в него новую жизнь.


Соглашение.

      Для тех, кто хочет участвовать в проектах, советую внимательно прочитать данное соглашение и согласиться с ним. Смысл его в том, что все игры, которые будут сделаны, должны поставляться в открытых кодах. (вместе с исходниками)
     
Соглашение

      Все участники проектов GameMaker.ru должны соблюдать эти правила. Администрация сайта GameMaker.ru оставляет за собой право изменять данное Соглашениe, если сочтёт это необходимым.
     - Каждый участник проекта обязуется оставить все наработки, сделанные в результате участия в проекте, как open source.
     - Все участники проекта соглашаются с тем, что сделанная в результате игра не будет коммерческой.
     - Все команды-участники проекта, будут входить в состав "GameMaker.ru Group". (название команды)
     - В игре будут упомянуты имена всех ее создателей, даже тех, чей вклад в игру очень мал и тех, кто покинул проект до его завершения. (Но только тех, кто был в команде не менее 3-х недель)
     - При возникновении разногласий между участниками команды решающее слово имеет руководитель данного проекта, либо его заместитель.
     - Все участники соглашаются с тем, что все материалы по данному проекту будут размещены на сайте www.gamemaker.ru.
     - Администрация сайта GameMaker.ru обязуется рассмотреть любое предложение участника проекта, но в праве вынести свое решение с объяснением причин.
     - Текущее соглашение может быть изменено администрацией сайта GameMaker.ru в одностороннем порядке, о чем каждый участник проекта будет своевременно поставлен в известность.
Администрация сайта GameMaker.ru



Спрайты.

      Здесь речь пойдет о том, что Вы, скорее всего, уже знаете. Но постараюсь рассказать об этой теме с учетом уже открывшихся проектов.
      Итак, что такое спрайт. Это небольшие графические изображения, находящиеся на игровом поле, способные двигаться и изменять свою форму. Наверное, многие из Вас видели в игре сменяющийся набор картинок (состояний), например, при реализации движения человека. Кстати, и в автосимуляторе, и в стратегии предлагаю на первое время обойтись спрайтами.
      Определим набор требований, предъявляемых к набору изображений:
1. Изображения должны быть одинакового размера;
2. В изображении должен быть определенный цвет, который не должен выводиться на экран. (прозрачный цвет) Как в формате gif.
      Какие есть способы вывода спрайтов на экран. Когда спрайт рисуется на экране, проблем нет. Но потом, если мы хотим, например, сдвинуть спрайт или поменять его состояние, то нам придется перерисовывать весь экран. Во многих играх этот способ не вызывает особых сложностей. Но что делать, если обновление экрана связано с определенными сложностями? Тогда можно сохранить область экрана под спрайтом и потом ее вывести.
      Все эти рассуждения можно проиллюстрировать кодом. Вот, что предлагается по этому поводу:
// sprite.h
#ifndef        __SPRITE__
#define        __SPRITE__

#define        MAX_STAGES        20
#define        TRANSP_COLOR        0xFF

class        Sprite
{
public:
        int        x, y;                // location of upper-left corner
        int        width, height;        // size of single image
        int        stageCount;        // number of stages
        int        curStage;        // current stage
        char  * underImage;        // place to store image under the sprite
        char  * image [MAX_STAGES];

        Sprite ( int, int, char *, ... );
        ~Sprite ()
        {
                free ( underImage );
        }

        void        set ( int ax, int ay )
        {
                x = ax;
                y = ay;
        }

        void        draw         ();
        void        storeUnder   ();
        void        restoreUnder ();
};

inline        int        min ( int x, int y )
{
        return x < y ? x : y;
}

inline        int        max ( int x, int y )
{
        return x > y ? x : y;
}

extern        char far * videoAddr;
extern        int           screenWidth;
extern        int           screenHeight;
extern        int           orgX;
extern        int           orgY;

#endif

// sprite.cpp
#include        < alloc.h >
#include        < dos.h >
#include        "sprite.h"

Sprite :: Sprite ( int w, int h, char * im1, ... )
{
        char ** imPtr = &im1;

        x          = 0;
        y          = 0;
        width      = w;
        height     = h;
        curStage   = 0;
        underImage = (char *) malloc ( width * height );

        for ( stageCount = 0; * imPtr != NULL; imPtr ++, stageCount ++ )
                image [stageCount] = * imPtr;
}

void        Sprite :: draw ()
{
        int           x1 = max ( 0, x - orgX );
        int           x2 = min ( screenWidth, x - orgX + width );

        if ( x2 < x1 )
                return;

        int           y1 = max ( 0, y - orgY );
        int           y2 = min ( screenHeight, y - orgY + height );

        if ( y2 < y1 )
                return;

        char far * videoPtr = videoAddr + y1 * screenWidth + x1;
        char         * dataPtr  = image [curStage] + (y1 - y)*width + (x1 - x);
        int           step     = screenWidth - (x2 - x1);

        for ( register int y = y1; y < y2; y++, videoPtr += step )
        {
                for ( int x = x1; x < x2; x++, dataPtr++, videoPtr++ )
                        if ( * dataPtr != TRANSP_COLOR )
                                * videoPtr = * dataPtr;

                dataPtr += width - (x2 - x1);
        }
}

void        Sprite :: storeUnder ()
{
        int           x1 = max ( 0, x - orgX );
        int           x2 = min ( screenWidth, x - orgX + width );
        int           y1 = max ( 0, y - orgY );
        int           y2 = min ( screenHeight, y - orgY + height );
        char far * videoPtr = videoAddr + y1 * screenWidth + x1;
        char         * ptr = underImage;
        int           step     = screenWidth - (x2 - x1);

        for ( register int y = y1; y < y2; y++, videoPtr += step )
                for ( register int x = x1; x < x2; x++ )
                        * ptr ++ = * videoPtr ++;
}

void        Sprite :: restoreUnder ()
{
        int           x1 = max ( 0, x - orgX );
        int           x2 = min ( screenWidth, x - orgX + width );
        int           y1 = max ( 0, y - orgY );
        int           y2 = min ( screenHeight, y - orgY + height );
        char far * videoPtr = videoAddr + y1 * screenWidth + x1;
        char         * ptr = underImage;
        int           step     = screenWidth - (x2 - x1);

        for ( register int y = y1; y < y2; y++, videoPtr += step )
                for ( register int x = x1; x < x2; x++ )
                        * videoPtr ++ = * ptr ++;
}


     Можно ускорить вывод спрайтов на экран, если закодировать каждую строку изображения методом RLE. Об этом методе подробнее поговорим в следующем выпуске, а пока посмотрим на реализацию RLE-кодирования применительно к спрайтам.
      Смысл RLE-кодирования в том, что мы разбиваем изображение на прозрачные и непрозрачные участки. Начинается такой участок с байта, задающего его длину. Старший бит этого байта определяет тип участка (прозрачный или непрозрачный), а остальные длину. Это приводит к тому, что количество байт, задающих строку, уже не является константой. Поэтому в коде нужно задать начало каждой строки относительно начала изображения.
// sprite.h
#ifndef        __SPRITE__
#define        __SPRITE__

#define        MAX_STAGES        20
#define        MAX_HEIGHT        100
#define        TRANSP_COLOR        0xFF

class        Sprite
{
public:
        int        x, y;                // location of upper-left corner
        int        width, height;        // size of single image
        int        stageCount;        // number of stages
        int        curStage;        // current stage
        char  * underImage;        // place to store image under the sprite
        char  * lineStart [MAX_STAGES*MAX_HEIGHT];

        Sprite ( int, int, char *, ... );
        ~Sprite ()
        {
                free ( underImage );
        }

        void        set ( int ax, int ay )
        {
                x = ax;
                y = ay;
        }

        void        draw         ();
        void        storeUnder   ();
        void        restoreUnder ();
};

inline        int        min ( int x, int y )
{
        return x < y ? x : y;
}

inline        int        max ( int x, int y )
{
        return x > y ? x : y;
}

extern        char far * videoAddr;
extern        int        screenWidth;
extern        int           screenHeight;
extern        int           orgX;
extern        int           orgY;

#endif

//sprite.cpp
#include        < alloc.h >
#include        < dos.h >
#include        "sprite.h"

Sprite :: Sprite ( int w, int h, char * im1, ... )
{
        char ** imPtr = &im1;

        x          = 0;
        y          = 0;
        width      = w;
        height     = h;
        curStage   = 0;
        underImage = (char *) malloc ( width * height );

        for ( int lineCount = 0; * imPtr != NULL; imPtr ++ )
        {
                char  * ptr = * imPtr;

                for ( int i = 0; i < height; i++ )
                {
                        lineStart [lineCount] = ptr;

                        for ( int j = 0; j < width; j++ )
                        {
                                int        count = * ptr++;

                                if ( ( count & 0x80 ) == 0 )        // не прозрачный
                                        ptr += count & 0x7F;

                                j += count & 0x7F;
                        }
                }
        }
}

void        Sprite :: draw ()
{
        int           x1 = max ( 0, x - orgX );
        int           x2 = min ( screenWidth, x - orgX + width );

        if ( x2 < x1 )
                return;

        int           y1 = max ( 0, y - orgY );
        int           y2 = min ( screenHeight, y - orgY + height );

        if ( y2 < y1 )
                return;

        char far * videoPtr = videoAddr + y1 * screenWidth + x1;
        int           step     = screenWidth - (x2 - x1);

        for ( register int y = y1; y < y2; y++, videoPtr += step )
        {
                char      * dataPtr  = lineStart [curStage * height + y1 - y];
                char far  * videoPtr = videoAddr + y * screenWidth + x1;

                for ( int i = x; x < x1; )        // пропускаем прозрачные блоки
                {
                        int        count = * dataPtr++;

                        if ( count & 0x80 )        // прозрачный
                                i += count & 0x7F;
                        else
                        {
                                count &= 0x7F;

                                if ( i + count < x1 )
                                {
                                        i       += count;
                                        dataPtr += count;
                                }
                                else
                                {
                                        dataPtr += x1 - i;
                                        count   -= x1 - i;

                                        if ( count > x2 - i )
                                                count = x2 - i;

                                        for ( ; count > 0; count-- )
                                                * videoPtr++ = * dataPtr++;
                                }
                        }

                }

                for ( ; i < x2; i++ )
                {
                        int        count = * dataPtr++;

                        if ( count & 0x80 )
                        {
                                count    &= 0x7F;
                                i        += count;
                                videoPtr += count;
                        }
                        else
                        {
                                count &= 0x7F;

                                if ( count > x2 - i )
                                        count = x2 - i;

                                i += count;

                                for ( ; count > 0; count-- )
                                        * videoPtr++ = * dataPtr++;
                        }
                }
        }
}

void        Sprite :: storeUnder ()
{
        int           x1 = max ( 0, x - orgX );
        int           x2 = min ( screenWidth, x - orgX + width );
        int           y1 = max ( 0, y - orgY );
        int           y2 = min ( screenHeight, y - orgY + height );
        char far * videoPtr = videoAddr + y1 * screenWidth + x1;
        char         * ptr = underImage;
        int           step     = screenWidth - (x2 - x1);

        for ( register int y = y1; y < y2; y++, videoPtr += step )
                for ( register int x = x1; x < x2; x++ )
                        * ptr ++ = * videoPtr ++;
}

void        Sprite :: restoreUnder ()
{
        int           x1 = max ( 0, x - orgX );
        int           x2 = min ( screenWidth, x - orgX + width );
        int           y1 = max ( 0, y - orgY );
        int           y2 = min ( screenHeight, y - orgY + height );
        char far * videoPtr = videoAddr + y1 * screenWidth + x1;
        char         * ptr = underImage;
        int           step     = screenWidth - (x2 - x1);

        for ( register int y = y1; y < y2; y++, videoPtr += step )
                for ( register int x = x1; x < x2; x++ )
                        * videoPtr ++ = * ptr ++;
}


      Теперь, используя описанные классы можно набросать простейший вариант стратегии. Пусть у нас есть карта уровня. Делим ее на прямоугольники. Допустим, что вся карта не помещается на экране. (А так и бывает в стратегиях) Поэтому необходимо выводить на экран только часть тех прямоугольников, на которые мы разбили карту. Пусть каждый такой прямоугольник имеет размер tileWidth на tileHeight. Координаты текущего положения левого верхнего угла экрана относительно карты (orgX,orgY). Получаем следующий набросок:

char        screenMap [MAX_TILE_X][MAX_TILE_Y];
Image * tiles [MAX_TILES];
Array * sprites;
Sprite  mouse;
int        orgX = 0;
int        orgY = 0;
int        done = 0;

void        drawScreen ()   // рисует изображение в буфере
{
        int        i0 = orgX / tileWidth;
        int        i1 = (orgX + screenWidth - 1) / tileWidth;
        int        x0 = i0 * tileWidth - orgX;
        int        j0 = orgY / tileHeight;
        int        j1 = (orgY + screenHeight - 1) / tileHeight;
        int        y0 = j0 * tileHeight - orgY;

        for ( int i = i0, x = x0; i < = i1; i++, x += tileWidth )
                for ( int j = j0, y = y0; j < = j1; j++, y += tileHeight )
                        tiles [screenMap [i][j]] -> draw ( x, y );

        for ( i = 0; i < sprites -> getCount (); i++ )
                ( (Sprite *) sprites -> objectAt ( i ) ) -> draw ();

        mouse.draw ();

        swapBuffers ();     // делает изображение видимым (выводит буфер)
}

main ()
{
        MouseState mouseState;

        loadMap     ();
        loadTiles   ();
        loadSprites ();
        initVideo   ();
        resetMouse  ();


        for ( ; !done; )
        {
                performActions ();
                drawScreen     ();
                readMouseState ( mouseState );

                if ( mouseState.loc.x > = screenWidth - 2 && orgX < maxOrgX )
                        orgX++;
                else
                if ( mouseState.loc.x < = 2 && orgX > 0 )
                        orgX--;

                if ( mouseState.loc.y > = screenHeight - 2 && orgY < maxOrgY )
                        orgY++;
                else
                if ( mouseState.loc.y < = 2 && orgY > 0 )
                        orgY--;

                mouse.set ( mouseState.loc.x + orgX, mouseState.loc.y + orgY );

                if ( mouseState.buttons )
                        handleMouse ( mouseState );

                handleKeyboard ();
        }

        doneVideo   ();
        freeTiles   ();
        freeSprites ();
}
Еще раз повторюсь, что это не работающая программа, а только набросок.


Послесловие.

      Вот и все на сегодня. Заходите на сайт, пишите, не забывайте. :) Присоединяйтесь к проектам. В любом случае все, что связано с проектами будет отражено на сайте. Все рассуждения, исходные тексты итд. Также ждем Вашу критику по поводу сайта или рассылки. Спасибо за внимание.
     До скорой встречи!


SlyMagic.

"Как самому создать компьютерную игру" (с) 2002
Использование любых материалов рассылки возможно только с разрешения автора.
Тираж 6340 экземпляров.


http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное