ФлеймФорумРазработка игр

Статья: Делаем простенькие игры

Страницы: 1 2 3 4 Следующая »
#0
15:16, 13 янв 2008

Эту статью я написал около месяца назад. Ориентирована она на полных новичков.
До этого выкладывал её только на vladgamedev.com (маленький форум игроделов Владивостока), теперь решил выложить и сюда.

*********

Делаем простенькие игры.

Автор Статьи: Тужик Александр. На момент написания статьи (2007 год) студент ДВГТУ по специальности «Вычислительные машины, комплексы, системы и сети», программированием занимаюсь не профессионально.

    Введение.
    Эта статья рассчитана исключительно на новичков в области игростроения, которые хотели бы научиться создавать простенькие игры своими руками. Цель статьи – помочь сделать первые шаги в этом направлении и совершить переход от обычного программирования к игровому.
    Однако в тексте также объясняются некоторые общие понятия, такие как игровые конструкторы или движки, так что статья может ещё претендовать на вводный курс молодого разработчика.

    Средство разработки.
    Сразу оговорюсь, что наиболее простой способ создания своих игр – это модификации уже существующих или использование так называемых «игровых конструкторов». Действительно, если вам хочется поскорее создать игру приемлемого качества, не вникая во все тонкости, то это наиболее разумный выбор; точно также как человек, который хочет что-нибудь приготовить поесть, вполне может воспользоваться полуфабрикатами.
    Но если вы считаете, что ваше увлечение может перерасти во что-нибудь серьёзное, то я бы советовал переходить на профессиональные языки программирования; ведь чтобы стать хорошим поваром, молодой кулинар начнёт лепить пельмени сам.

    Итак, будем надеяться, что вы избрали второй путь. Теперь определимся с языком. В качестве средства разработки наших будущих игр я выбрал свой «родной» язык – Visual Basic 6.0. Но помимо моей личной симпатии к нему, есть ещё несколько причин, почему я остановил свой выбор на нём:
    Во-первых, если вы уже пробовали учиться программировать, то, скорее всего, знакомы именно с этим языком или его предшественниками.
    Во-вторых, это очень мощный инструмент для создания проектов любого уровня, но в то же время достаточно простой для понимания, что особенно актуально для начинающих. И даже если вы ранее не работали с Visual Basic, то изучить его основы можно без какой бы то ни было специальной подготовки; благо информации на эту тему в Интернете предостаточно.
    И, наконец, в-третьих, несмотря на огромное количество желающих делать игры в Visual Basic, статей именно по созданию игр на нём (особенно на русском языке) ничтожно мало, так что я попробую хоть как-то заполнить этот пробел.

    Графика.
    Я позволю себе допустить, что вы уже ознакомились с языком (нам достаточно знать синтаксис базовых операторов, уметь работать с переменными, функциями и массивами, а также использовать стандартные контролы, вроде форм и кнопок). И если это так, то можно двигаться дальше.

    В процессе обучения языку вы наверняка создавали небольшие приложения, но игра – это нечто большее; и настало время поговорить об одной из самых важных составляющих игр, которая отличает их от обычных программ – о графике. Неважно, что вы предпочитали при обучении азам программирования: будь то лекции в университете, книги или статьи; всё равно, до сих пор вы, скорее всего, работали только со стандартными компонентами Windows. Но я не думаю, что такая графика в игре вас устроит:

Изображение

Рис 1. Игра без графики.

    Разумеется, хочется добиться приемлемой двухмерной графики, как и в других играх (3D в рамках этой статьи я рассматривать не буду). Первое, что приходит на ум для этой цели – использовать PictureBox или Image в качестве картинок, при анимации менять им по кадрам свойство Picture, а при передвижении свойства Left и Top. Но при таком подходе о красивых разноцветных фонах можно забыть, так как картинки всё равно останутся прямоугольными компонентами, и нам придётся всюду «таскать» за собой небольшую рамку:

Изображение    Изображение

Рис 2. Космический корабль на различном фоне.

  Конечно, можно пожертвовать фоном и сделать его везде монотонным, но при любом пересечении двух объектов, один из них будет оказываться впереди, а другой сзади и максимум чего мы сможем добиться, это такой картинки:

Изображение

Рис 3. Пересечение объектов.

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

  Движки.
 
  Вот мы и добрались до движков. Читатель родился не вчера, и наверняка знает, что обычно игры делают на игровых движках, но не каждый ясно представляет себе, что же это такое. В любом случае, пугаться этого слова не стоит, движок – это всего лишь ядро программы, основа, на которой будет строиться игра. Также можно понимать движок, как некую довольно большую подпрограмму, призванную облегчить жизнь программистам.
  Идея движков проста. Функционал по работе с графикой или физикой часто схож в разных играх (особенно, в одном жанре), а реализация его весьма трудоёмкий процесс. Чтобы каждый раз не писать одно и то же, имеет смысл написать все нужные функции один раз, создав движок; а потом использовать наработки для создания самих игр. Так, например, графический движок будет представлять собой набор средств по отображению графики и всевозможных визуальных эффектов. Используя такой движок, не нужно будет думать, как избавиться от рамки у картинки или сделать эффект полупрозрачности, и можно будет сосредоточиться непосредственно на игре.
  Другое дело, что движки быстро устаревают, поэтому разработчикам приходится время от времени писать всё заново, создавая всё новые и новые движки  в бесконечной гонке технологий.

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

  Свои движки.

  Но начиная разговор о движках, мне приходится сделать небольшое отступление о том, стоит ли писать свой движок или воспользоваться готовым. Ответ на вопрос однозначен – взять готовый. Для меня ответ очевиден, но почему-то для многих начинающих игроделов мысль о том, чтобы использовать что-то чужое, оказывается неприемлемой. Им непременно хочется сделать всё самим от начала и до конца, подражая крупным компаниям. Но это неоправданно по двум причинам:
  Во-первых, потому что вы не крупная компания и на разработку движка и игры на нём может не хватить ни времени, ни сил. Сфокусироваться на игре не только намного проще, но и приятнее, ведь вы сразу будете видеть результат своих трудов, тогда как работа над движком проходит долго без какого-то бы ни было видимого результата, и может убить весь интерес к созданию игр в самом начале.
  Во-вторых, вам так и так придётся использовать чужую продукцию. Не станете же вы писать свою операционную систему, или свой язык программирования. Вы воспользуетесь уже созданными, так почему же не взять готовый движок? В конце концов, и тот самый повар, готовящий пельмени, не станет делать муку сам.

  Есть ещё одно мнение о том, что нужно сначала научиться создавать движки, для того, чтобы затем научиться делать игры. На самом деле наоборот, не зная, как устроены игры, нельзя создать движок. Многие ярые поклонники игр думают, что знают, что должно быть в игре, и, стало быть, знают, что должен уметь движок; но геймерского стажа здесь не достаточно. Рассуждать о том, как устроена мука не очень удобно, поэтому я возьму другой пример.
  Пусть игра – это автомобиль, а движок – автомобильный цех. Допустим, вы любите машины, много ездите и знаете, что нужно водителю для полного счастья. Но вы не сможете создать свой цех, пока не будете знать, как устроена машина изнутри. Лучше зайти в чужой цех и создать в нём машину, пройдя все этапы разработки. Только после этого, вы будете иметь представление о том, что на самом деле должен уметь автомобильный цех, чтобы в нём можно было что-нибудь создать

  Чужие движки.

  Итак, выберем наконец-то движок для наших игр. Cамым лучшим решением, на мой взгляд, будет Software Renderer 2D (далее просто SR2D). Автор этого движка – Михаил «Mikle» Ильин, а с моего сайта можно скачать последнюю версию на момент написания статьи. Чем же этот движок хорош? Приведу несколько аргументов в его пользу:

  - Он содержит всё нам необходимое, и при этом очень прост в обращении.
  - Имеет множество примеров использования.
  - Сделан в России, поэтому вся документация уже по-русски.
  - Исходный код открыт, так что можно изучить его устройство, что будет полезно при создании своего движка.
  - В случае чего, можно изменить исходный код движка под свои нужды.
  - Есть версия движка для Visual Basic.net.
  - И последнее, но не по важности – движок абсолютно бесплатен.

#1
15:17, 13 янв 2008

  Боевая готовность.

  Если вы ещё не устали от теории, то предлагаю слегка встряхнуться и приготовиться к написанию кода. Сначала проверим наличие необходимого софта. Мы должны иметь установленную копию Microsoft Visual Basic 6.0., а также распакованный архив движка SR2D.
  Если всё на месте, то продолжим. Запустите Visual Basic и создайте новый проект (Standart Exe). Сразу же сохраните проект где-нибудь на жёстком диске.

ИзображениеСовет. Попробуйте взять в привычку сохранять проект как можно чаще, потому что разработка игры – дело сложное: вполне может произойти ошибка и данные не сохраняться. Если сомневаетесь, то сохраните проект под другим именем: лучше иметь несколько версий проекта на компьютере, чем заново писать код.


  Теперь нужно подключить движок. Для этого выберите пункт «Add Module» в меню «Project». В открывшемся диалоге перейдите на вкладку «Existing», найдите каталог VB6 в директории движка SR2D и подключите файл modSR2D.bas. Затем повторите процедуру, чтобы подключить модуль modUtil.bas.

Изображение

Рис 4. Подключение модулей.

  Для подключения классов поступайте аналогично, только вместо «Add Module», следует воспользоваться пунктом «Add Class Module». Нам потребуется только SR2D_Sprite.cls (он находиться в той же папке), а классы clsWater.cls и clsFire.cls включать в проект необязательно: они нужны для создания реалистичной воды и огня, и пока нам не понадобятся.
  Осталось только скопировать SR2D.dll (всё из того же каталога) в папку с нашим проектом.

ИзображениеЗамечание. SR2D.dll – это библиотека, которую использует движок. Она необходима не только в процессе разработки игры, но и для её успешного запуска у конечного пользователя. Поэтому, когда будете распространять свою игру, не забудьте поместить эту библиотеку в папку с exe-файлом.

 
  Сделаем холст. 

  Движок подключен, и можно приступать к созданию игры. Но если вы думаете, что мы сейчас начнём визуально расставлять компоненты, то вы ошибаетесь. В нашем расположении окажется что-то вроде холста, на котором мы будем программно формировать изображение, которое увидит игрок. Формирование изображения для нас будет сводиться к указанию что, где и как нарисовать. Причём рисовать нужно сначала дальние объекты (в том числе полупрозрачные, например, стекло), а потом ближние; как бы по слоям. Это легко понять, если представить, что мы рисуем на настоящем холсте:

Изображение

Рис 5. Формирование сложного изображения слоями.

  И первое, что нам нужно сделать – это как раз этот холст. В секции Generals главной формы запишем:

Option Explicit
Dim Running As Boolean
Dim SprFon As New SR2D_Sprite

  Здесь мы включаем режим явного объявления переменных; объявляем булевскую переменную Running.
  Третьей строкой мы создаём спрайт SprFon, который будет выступать в роли холста; и теперь всегда, если нам понадобиться изображения или картинки (и холст не исключение) мы будем их создавать и использовать как экземпляр класса SR2D_Sprite (или просто спрайт).

  Двигаемся дальше. Добавим следующий код:

Private Sub Form_Load()
  Me.ScaleMode = vbPixels
  Me.Move Me.Left, Me.Top, 400 * Screen.TwipsPerPixelX, 300 * Screen.TwipsPerPixelY
  Me.Show
  SprFon.Init Me.ScaleWidth, Me.ScaleHeight
  Running = True
  Do While Running
    Render
    DoEvents
  Loop
  Set SprFon = Nothing
  Unload Me
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    Running = False
End Sub

  Тут мы при загрузке формы делаем окно размером 400x300 пикселей и придаём холсту размер, равный размеру формы:
  SprFon.Init Me.ScaleWidth, Me.ScaleHeight

  Затем начинается выполнение цикла, где Render – это функция, формирующая изображение на холсте, и которую мы напишем чуть позже.
А функции DoEvents позволяет пользователю работать с нашим приложением (двигать курсор, нажимать кнопки, влияя на игровую ситуацию) во время выполнения цикла, а не дожидаясь его конца. Если эту строку убрать, то цикл станет выполняться бесконечно, ни на что не реагируя. Фактически это означает, что игра повиснет.

  Условие, которое останавливает этот цикл выполняется только в случае выгрузки формы (Form_QueryUnload), например, при выходе из игры. Но прежде, чем закрыть приложение, мы удаляем из памяти все ранее созданные спрайты, в данном случае это только холст:
  Set SprFon = Nothing
  Unload Me

ИзображениеСовет. Как только добавляете новый спрайт в игру, старайтесь сразу же сделать здесь запись о его уничтожении. Если этим пренебречь, то после выхода из игры в памяти останутся «висеть» ваши изображения. И хотя серьёзные проблемы с памятью могут вызвать только большие игры, привычку ничего за собой не оставлять стоит вырабатывать уже на маленьких программках.

 
  Теперь, как и было обещано, пишем функцию Render:

Sub Render()
SprFon.PaintToHDC Me.hDC
End Sub

  Сейчас она содержит только одну строку – вывод изображения холста на форму, но постепенно мы будем её усложнять; и поскольку эта процедура выполняется в цикле, то от её сложности будет зависеть сколько раз обработать процедуру (а значит, и сколько раз вывести изображение на экран) будет успевать компьютер в единицу времени. Эта величина, называемая FPS (сокращение от английского frames per seconds – кадров в секунду) одна из наиболее важных характеристик производительности игры, поэтому очень полезно всегда знать её значение. Очень удобно выводить значение прямо в заголовке окна. Для этого объявим глобальную переменную FPS как Integer, в процедуру Render добавим строчку:
FPS = FPS + 1

  Далее «кинем» на форму таймер, выставим ему интервал равный 1000 (одна секунда). И запишем в нём следующее:

Private Sub Timer1_Timer()
Me.Caption = FPS
FPS = 0
End Sub

Напоследок позаботимся о выходе из игры. Пусть это будет щелчок в любом месте формы:

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Form_QueryUnload 0, 1
End Sub

Можете запустить программу и полюбоваться на чёрный холст. Значение FPS окажется довольно большое, но в процессе разработки оно будет уменьшаться. Для приятной глазу, не дёрганой картинки достаточно 24-30 кадров в секунду. Но если игра предполагает от пользователя моментальной реакции, как, например, в шутерах, то вам потребуется небольшой запас кадров, чтобы игрок не чувствовал задержки между его действиями и реакцией игры.

Изображение

Рис. 6. Чистый холст.

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

#2
15:17, 13 янв 2008

  Hello World 2.0.

Самая простая программа, которую только можно написать – это программа типа «Привет Мир!», состоящая из одной кнопки, выводящей какое-либо сообщение. Но я уже говорил, что визуально кнопки мы больше расставлять не будем, поэтому задача перестаёт быть столь тривиальной; но зато на её примере мы научимся выводить как кнопки, так и любые другие изображения.

  Избавляемся от рамки.

  Итак, сначала нарисуем кнопку, которую хотим видеть в игре. Чтобы пользователь знал, что это именно кнопка, а не простая картинка; следует её подсвечивать при наведении на неё курсора мыши. Поэтому сразу же сделаем два варианта кнопки:

Изображение  Изображение

Рис. 7. Кнопка (обычная и подсвеченная).

  Обратите внимание, что весь фон, который не должен отображаться в игре, а также все прозрачные места внутри рисунка закрашиваются каким-нибудь одним цветом (красный на рисунке 7). Этот цвет можно выбрать любой, но нужно позаботиться о том, чтобы он не использовался в самом рисунке, иначе в игре он отобразится с дырками.
  Стоит заметить, что SR2D различает множество цветовых оттенков, а нам нужно будет явно указать, какой является фоном; поэтому я рекомендую использовать в качестве последнего только абсолютные цвета: абсолютно чёрный, абсолютно красный, зелёный, синий или белый. Если вы пользуетесь программой MS Paint, то в его палитре эти цвета помечены крестиком на рисунке 8:

Изображение

Рис. 8. Абсолютные цвета.

  Если вы будете ужимать картинку, следите, чтобы сохранился цвет фона. Сжатие JPEG, например, искажает все цвета; мы же сохраним кнопки как Button1.gif и Button2.gif и положим их в папку с предыдущим проектом, который мы возьмём за основу для нашей программы (можете сохранить проект под новым именем или продолжать работать в нём).

Для начала объявим глобальную переменную n, и создадим два спрайта для наших кнопок так же, как и холст:

Dim n As Integer
Dim SprKnopka(1 To 2) As New SR2D_Sprite

Не забудем их уничтожить перед выходом:

For n = 1 To 2
Set SprKnopka(n) = Nothing
Next n

Спрайты созданы, но пока они пусты. Попробуем загрузить в них изображения кнопок. Для этого «бросим» на форму компонент Picture, выставим его свойства AutoRedraw и AutoSize на True, а свойство Visible установим равное False. Кроме того, нам потребуется создать дополнительную процедуру для загрузки изображения:

Public Sub SpriteLoadFromFile(Spr As SR2D_Sprite, fName As String, Optional ByVal ColorKey As Long = -1&)
  Picture1.Picture = LoadPicture(fName)
  Spr.LoadFromObject Picture1, ColorKey
  Picture1.Picture = Nothing
End Sub

  Вникать в работу этой процедуры необязательно, но благодаря ей, мы сможем загружать рисунок в спрайт через посредник (Picture1) в любом поддерживаемым Visual Basic формате (а не только tga). А выставленные свойства позволят загружать рисунок любого размера и скроют Picture от наших глаз.
  Воспользуемся этой процедурой, чтобы загрузить изображения кнопок, указав в последнем параметре цвет фона: 

SprFon.Init Me.ScaleWidth, Me.ScaleHeight
For n = 1 To 2
SpriteLoadFromFile SprKnopka(n), "Button" & n & ".gif", ARGB(0, 255, 0, 0)
Next n

  Настало время поговорить о способе задания цвета. Формат описания цвета RGB – это сокращение от английского: Red, Green, Blue. В нём любой цвет представляется как смешение красного, зелёного и синего; и задаётся тремя числами от 0 до 255, обозначающих долю соответствующего цвета. Однако мы будем использовать формат ARGB, который имеет ещё четвёртое число в начале, означающее степень полупрозрачности, пока оставляйте это поле равное 0.
  Так как мы использовали абсолютно красный цвет (без примесей), то значение красного будет максимальным – 255, а зелёного и синего 0.

ИзображениеПримечание. Абсолютно чёрный – это отсутствие цвета, так что его RGB составляющая будет равна 0, 0, 0. А абсолютно белый – это смешение всех цветов, следовательно, он задаётся как 255, 255, 255.

Теперь выведем кнопку на экран. Для этого перед тем, как отобразить на экран содержимое холста, нарисуем на нём кнопку:

SprFon.Draw SprKnopka(1), 200 - SprKnopka(1).Width / 2, 150 - SprKnopka(1).Height / 2
SprFon.PaintToHDC Me.hDC

Учитывая, что наше окно 400 на 300 пикселей, то его середина будет в точке 200, 150. А поскольку функция Draw помещает в заданную точку левый верхний угол спрайта, то для того, чтобы центр кнопки совпал с центром окна мы «перемещаем» точку вывода спрайта влево ровно на половину длины спрайта и вверх на половину высоты.

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

Изображение

Рис. 9. Кнопка с прозрачным фоном.

Может быть, пока кажется, что полученный результат не стоит тех стараний, которые были на него потрачены; но если продолжить, то вскоре мы добьёмся ощутимых отличий.

#3
15:18, 13 янв 2008

Курсор.

  Продолжим работать над Hello World 2.0. Объявим две глобальные переменные XCur и YCur как Integer. Будем записывать в них текущее положение курсора при его перемещении:

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
XCur = X
YCur = Y
End Sub

  Зная положения мыши, мы можем заменить стандартный курсор своим. Сначала скрываем старый. Для этого выставляем у формы свойство Mouse Pointer на 99 (Custom), чтобы выбрать пользовательский курсор, и ставим в свойстве Mouse Icon полностью прозрачную иконку. Если вы не знаете, как такую сделать, то можете скачать уже готовый вариант.
  Новый курсор – это обычная картинка, назовём её Cursor.gif и положим в папку с игрой:

Изображение

Рис 10. Курсор.

  Курсор выводится точно так же, как кнопка. Для тренировки проделайте эти действия сами: создайте спрайт SprCur, добавьте строку о его уничтожении, сделайте загрузку изображения через SpriteLoadFromFile, где в качестве фона укажите синий.
  Не забываем, что изображение накладывается слоями, поэтому курсор надо рисовать после кнопки (так как он должен оказаться над ней), но перед выводом изображения на форму:

SprFon.Draw SprKnopka(1), 200 - SprKnopka(1).Width / 2, 150 - SprKnopka(1).Height / 2
SprFon.Draw SprCur, XCur, YCur
SprFon.PaintToHDC Me.hDC

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

Изображение

Рис. 11. Неочищенные пиксели.

  Так происходит, потому что мы не стираем старый курсор, но рисуем новый всё на том же холсте. Чтобы этого избежать, надо каждый раз очищать (закрашивать) весь холст, перед тем, как рисовать всё заново. Можно закрашивать любым цветом, например, фиолетовым:
SprFon.ClearBuffer ARGB(0, 200, 100, 255)

  Теперь немного изменим функцию Render и вынесем прорисовку кнопки в отдельную процедуру DrawKnopka:

Sub Render()
With SprFon
    .ClearBuffer ARGB(0, 200, 100, 255)
    DrawKnopka 200, 150
    .Draw SprCur, XCur, YCur
    .PaintToHDC Me.hDC
End With
FPS = FPS + 1
End Sub
Sub DrawKnopka(X As Integer, Y As Integer)
Dim Xsd As Integer, Ysd As Integer
Xsd = SprKnopka(1).Width / 2
Ysd = SprKnopka(1).Height / 2
n = 1
If XCur > X - Xsd And XCur < X + Xsd And YCur > Y - Ysd And YCur < Y + Ysd Then n = 2
SprFon.Draw SprKnopka(n), X - Xsd, Y - Ysd
End Sub

  Мы посылаем процедуре координаты центра спрайта. Таким образом, границы спрайта отстоят от этой точки на половину его длины (высоты) в обе стороны. И если курсор лежит в этих границах, то мы отображаемой не обычную кнопку, а подсвеченную. В запущенном виде это выглядит следующим образом:

Изображение

Рис. 12. Подсвеченная кнопка.

Шрифт.

Осталось вывести сообщение, а оно, как вы уже догадались, тоже создаётся картинкой. Но её можно сгенерировать, а не создавать самим. И поможет нам в этом снова посредник Picture1, на котором мы напечатает текст сообщения с помощью команды Print и передадим получившееся изображение в спрайт. Процедура будет выглядеть примерно так:

Private Sub SpriteCreateText(Spr As SR2D_Sprite, Text As String, Color As Long)
  Picture1.ForeColor = Color
  Picture1.Move 0, 0, Picture1.TextWidth(Text & " "), Picture1.TextHeight(Text & " ")
  Picture1.Picture = Nothing
  Picture1.CurrentX = 0
  Picture1.CurrentY = 0
  Picture1.Print Text
  Picture1.Picture = Picture1.Image
  Spr.LoadFromObject Picture1, Picture1.BackColor
  Picture1.Picture = Nothing
End Sub

Также у Picture1 нужно будет выставить значение ScaleMode равно 3 (Pixel), BackColor установить на чёрный (&H00000000&), чтобы использовать его как фон; а тип и размер шрифта будущего спрайта настраиваются в колонке Font и выбираются исключительно по вкусу.

ИзображениеСовет. Старайтесь избегать экзотических шрифтов, так как они, в отличие от стандартных, могут быть не установлены в системе пользователя. Если же всё-таки такой шрифт необходим, то позаботьтесь о его установке вместе с игрой.

  Давайте создадим спрайт SprText (не забываем тут же уничтожать его) и загрузим в него изображение текста:

SpriteCreateText SprText, "Hello World!", ARGB(0, 0, 150, 0)

  Последние два параметра – это сам текст сообщения и цвет надписи.

  Чтобы надпись отображалась после нажатия кнопки, переделаем обработку Form_MouseDown. Вместо выхода из программы запишем:

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If n = 2 Then Vkl = True
End Sub

  Где Vkl – это новая глобальная переменная типа Boolean, а значение n во время выполнения цикла Render, если вы помните из предыдущих примеров, у нас будет равно двум,
только если курсор находится над кнопкой.
  Для красоты можно чуть-чуть «поднять» кнопку вверх, а надпись выводить под ней:

DrawKnopka 200, 100
If Vkl = True Then .Draw SprText, 200 - SprText.Width / 2, 200 - SprText.Height / 2

Работу над созданием Hello World 2.0. мы закончили. Как должен выглядеть окончательный результат вы можете посмотреть на рисунке 13, а исходный код этой программы и всех промежуточных стадий вы найдёте в том же архиве=http://www.alprog.ru/Article/Source.rar с примерами, о котором я уже говорил раньше.

Изображение

Рис. 13. Hello World.

Тетрис.

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

#4
15:19, 13 янв 2008

Полноэкранный режим.

До сих пор наша программа отображалась в окне, и нас это устраивало. Но тетрис – это уже полноценная игра, а игры смотрятся намного привлекательнее в полноэкранном режиме. И если мы хотим развернуть приложение на весь экран, то нам придётся считаться с тем, что у разных пользователей стоит разное расширение. Таким образом, нам следует научиться менять расширение экрана.
Продолжим старый проект и создадим для этих целей отдельный модуль. Перейдите в меню «Project» >> «Add Module» >> «New» >> «Module». Появившийся модуль переименуйте в Resolution и откройте его двойным щелчком:

Изображение

Рис 14. Модуль Screen.

Теперь вставим в модуль следующий код:

Private Declare Function ChangeDisplaySettings Lib "user32" Alias "ChangeDisplaySettingsA"_
 (lpDevMode As Any, ByVal dwflags As Long) As Long
Private Declare Function EnumDisplaySettings Lib "user32" Alias "EnumDisplaySettingsA"_
 (ByVal lpszDeviceName As Long, ByVal iModeNum As Long, lpDevMode As Any) As Boolean

Const DM_PELSWIDTH = &H80000
Const DM_PELSHEIGHT = &H100000
Const CCFORMNAME = 32
Const CCDEVICENAME = 32

Private Type DEVMODE
dmDeviceName As String * CCDEVICENAME
dmSpecVersion As Integer
dmDriverVersion As Integer
dmSize As Integer
dmDriverExtra As Integer
dmFields As Long
dmOrientation As Integer
dmPaperSize As Integer
dmPaperLength As Integer
dmPaperWidth As Integer
dmScale As Integer
dmCopies As Integer
dmDefaultSource As Integer
dmPrintQuality As Integer
dmColor As Integer
dmDuplex As Integer
dmYResolution As Integer
dmTTOption As Integer
dmCollate As Integer
dmFormName As String * CCFORMNAME
dmUnusedPadding As Integer
dmBitsPerPel As Integer
dmPelsWidth As Long
dmPelsHeight As Long
dmDisplayFlags As Long
dmDisplayFrequency As Long
End Type

Public Sub ChangeResolution(iWidth As Single, iHeight As Single)
Dim DevM As DEVMODE
Dim a As Boolean
Dim i As Long
Dim b As Long
i = 0
Do
a = EnumDisplaySettings(0&, i&, DevM)
i = i + 1
Loop Until (a = False)
DevM.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT
DevM.dmPelsWidth = iWidth
DevM.dmPelsHeight = iHeight
b = ChangeDisplaySettings(DevM, 0)
End Sub

Здесь нам интересна только процедура ChangeResolution. Посылая ей значение ширины и высоты, мы сможем в любой момент сменить расширение экрана.
Вернёмся на основную форму. При запуске программы надо «запомнить» старое расширение экрана, поменять его на нужное в игре и сделать размер окна равным размеру экрана:

OldX = Screen.Width / Screen.TwipsPerPixelX
OldY = Screen.Height / Screen.TwipsPerPixelY
ChangeResolution 800, 600
Me.ScaleMode = vbPixels
Me.Move 0, 0, 800 * Screen.TwipsPerPixelX, 600 * Screen.TwipsPerPixelY
Me.Show

OldX и OldY – это глобальные переменные типа Single, которые нужны для того, чтобы перед выходом из игры можно было восстановить прежнее расширение экрана:
ChangeResolution OldX, OldY

Осталось только убрать строку заголовка. Сделаем у формы свойство ControlBox = False, а свойство Caption сделаем пустым.
Соответственно и FPS больше нельзя выводить в заголовке окна. Будем сохранять его в новый спрайт как текст:

Private Sub Timer1_Timer()
SpriteCreateText SprFPS, Str(FPS), ARGB(0, 255, 255, 255)
FPS = 0
End Sub

Выведем на экран значение FPS перед курсором:
.Draw SprFPS, 0, 0

Осталось только позаботиться о выходе из игры (так как кнопки «закрыть» больше нет). Пусть выход будет при нажатии на кнопку, место вывода Hello World:
If n = 2 Then Form_QueryUnload 0, 1

Теперь можете убрать всё, что связано со спрайтом SprText и переменной Vkl (остатки от проекта Hello World, которые нам больше не пригодятся) и запустить программу. Если всё сделано верно, то вы получите нечто вроде этого:

Изображение

Рис. 15. Полноэкранный режим.

Игровое поле.

Окно развёрнуто на весь экран, но игровое поле тетриса занимает далеко не всё пространство. Падающие кубики расположатся в правой части экрана, слева можно сделать меню, но всё оставшееся место будет пустовать. Поэтому, если вам ещё не надоел монотонный фиолетовый, то давайте сменим фон. Загрузим обычную картинку 800x600 как спрайт SprBack и будем выводить её сразу после очистки холста.
Ещё два необходимых спрайта – это два вида кубика: падающий, и уже упавший:

Изображение    Изображение

Рис 16. Спрайты кубика.

  Загрузим их (SprCube) по аналогии с тем, как мы ранее загружали два вида кнопки:

Dim SprBack As New SR2D_Sprite
Dim SprCube(0 To 2) As New SR2D_Sprite

For n = 1 To 2
SpriteLoadFromFile SprKnopka(n), "Button" & n & ".gif", ARGB(0, 255, 0, 0)
SpriteLoadFromFile SprCube(n), "Cube" & n & ".jpg"
Next n
SpriteLoadFromFile SprBack, "Back.jpg"

Обратите внимание на то, что мы создали три спрайта кубика, а загрузили только два. Нулевой спрайт специально создаём и оставляем пустым: при попытке вывести его на холст, спрайт выродится в точку, что позволит не писать обработчик для пустой клетки, а заодно обеспечит разметку поля в виде чёрных точек.
Все спрайты загружены, так что можно приступать к созданию самого игрового поля. Оно представляет собой матрицу размерами 10x25, где каждая ячейка может принимать одно из трёх значений: 0, 1 или 2, означающих номер нужного спрайта кубика. Также нам потребуется дополнительная матрица для хранения и операций с фигурами. В классическом тетрисе любая фигура помещается в квадрат 4x4; поэтому, учитывая, что нумерация начинается с нуля, запишем:

Dim Pole(9, 24) As Byte
Dim Matrix(3, 3) As Boolean
Dim X As Integer, Y As Integer
Dim m As Integer, k As Integer

Поскольку дополнительная матрица будет соответствовать какой-то области на поле, и эта область будет меняться, то мы ввели две переменные X и Y, в которых мы будем хранить координаты ячейки на игровом поле, соответствующей верхней левой ячейке дополнительной матрицы.
Ну а m и k это просто служебные переменные, которые понадобятся нам в цикле вывода игрового поля:

For m = 0 To 9
For k = 0 To 24
.Draw SprCube(Pole(m, k)), 500 + m * 23, 12 + k * 23
Next
Next

Запустите программу. Мы должны увидеть фоновый рисунок и множество точек на месте клеток:

Изображение 

Рис. 17. Разметка точками.

#5
15:19, 13 янв 2008

Перемещение фигур.

Сделаем появление фигуры при старте программы:

Pole(4, 0) = 1
Pole(4, 1) = 1
Pole(5, 1) = 1
Pole(5, 2) = 1
X = 3
Y = 0

Так как X и Y означают левый верхний угол дополнительной матрицы, то мы задаём её область по x с 3 по 6 ячейку, и по y с 0 по 3. В эту область попадает наша фигура, значит, мы сможем совершать над ней преобразования. Чтобы видеть, где сейчас находится матрица, с которой мы работает, создадим и загрузим прозрачный спрайт SprRama:

Изображение 

Рис. 18. Рама.

Чтобы спрайт отображался в игре, в цикл рисования точек добавим строчку:
If m = X And k = Y Then .Draw SprRama, 500 + m * 23, 12 + k * 23

Изображение 

Рис 19. Рабочая матрица.

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

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyLeft Then Dvig X - 1, Y
If KeyCode = vbKeyRight Then Dvig X + 1, Y
If KeyCode = vbKeyDown Then Dvig X, Y + 1
End Sub

  При нажатии любой из этих кнопок запускается процедура Dvig, параметрами которой будут координаты матрицы после соответствующего перемещения. Другими словами, указывается куда должно «стать» матрица. Несмотря на кажущуюся простоту, процедура Dvig достаточно сложна. Мы не можем просто прогнать цикл, в котором поэлементно копируются клетки из старой области в новую, потому что при движении этим методом в одну сторону, мы оставляем за собой неубранные кубики, а при движении в другую и вовсе уничтожаем всю фигуру, как показано на рисунке:

Изображение . . . . . . Изображение   

Рис 20. Перемещение матрицы.

  И вот как раз здесь нам и пригодится матрица 4x4, которую мы создали в предыдущем примере. Алгоритм перемещения фигуры будет примерно такой:

1. Копируем рабочую область в дополнительную матрицу;
2. Проверяем можно ли поставить эту матрицу на новое место и если да то:
    3. Удаляем фигуру из рабочей области;
    4. Ставим на новое место фигуру из дополнительной матрицы;
    5. Меняем координаты рабочей области на новые.

  Первый и третий пункт – это однотипные циклы; поэтому мы реализуем их как процедуру, вызываемую из основного алгоритма:

Sub Dvig(NewX As Integer, NewY As Integer)
Deistv "Copy"
If Proverka(NewX, NewY) Then
    Deistv "Delete"
    For m = 0 To 3
    For k = 0 To 3
       If Matrix(m, k) = 1 Then Pole(NewX + m, NewY + k) = Matrix(m, k)
    Next
    Next
    X = NewX
    Y = NewY
End If
End Sub
Sub Deistv(H As String)
For m = 0 To 3
For k = 0 To 3
If H = "Copy" Then Matrix(m, k) = Pole(X + m, Y + k)
If H = "Delete" And Pole(X + m, Y + k) = 1 Then Pole(X + m, Y + k) = 0
Next
Next
End Sub

  Заметьте, что при удалении фигуры, мы уничтожаем только ячейки с падающим кубиком (Pole(X + m, Y + k) = 1); а кубики уже упавшие, остаются на своих местах.
  Ещё, прежде, чем мы продолжим, надо подумать об ограничении перемещений у границы. Ведь иногда, когда рамка находится вплотную к стенке, а сама фигура – нет, то перемещение сделать можно, но при этом рамка должна остаться на месте:

Изображение 

Рис 21. Перемещение у границы.

  При этом движении, пустые кубики слева пытаются «выскочить» за пределы игрового поля, что может вызвать ошибку; поэтому внесём в Dvig некоторые изменения:

For m = 0 To 3
For k = 0 To 3
   If NewX + m >= 0 And NewX + m < 10 And NewY + k < 25 Then
      If Matrix(m, k) = 1 Then Pole(NewX + m, NewY + k) = Matrix(m, k)
   End If
Next
Next
If NewX >= 0 And NewX < 7 Then X = NewX
If NewY < 22 Then Y = NewY

  Здесь мы, перед тем как переместить каждый кубик, проверяем, не выходит ли он за пределы поля. И в конце точно также проверяем положение рамки; учитывая, что слева и внизу от её координат должны поместится по три кубика (10-3=7, 25-3=22).

  Что ж, движение во всех разрешённых случаях у нас реализовано. Осталось написать только функцию Proverka, чтобы предотвратить перемещение на те клетки, куда этого сделать нельзя. В первой части процедуры проверяем, не попадёт ли какой-нибудь из наших кубиков на клетку, уже занятую упавшим кубиком:

Function Proverka(NewX As Integer, NewY As Integer) As Boolean
Proverka = True
For m = 0 To 3
For k = 0 To 3
   If NewX + m > -1 And NewX + m < 10 And NewY + k < 25 Then
      If Pole(NewX + m, NewY + k) = 2 And Matrix(m, k) = 1 Then Proverka = False
   End If
Next
Next
…

  А заканчиваем тремя блоками на случай, если рамка двигается за какой-нибудь край поля, и при этом, на её прислонённой грани есть не пустые клетки:

…
If NewX < 0 Then
   m = 0
   For k = 0 To 3
   If Matrix(m, k) = 1 Then Proverka = False
   Next
End If
If NewX >= 7 Then
   m = 3
   For k = 0 To 3
   If Matrix(m, k) = 1 Then Proverka = False
   Next
End If
If NewY >= 22 Then
   k = 3
   For m = 0 To 3
   If Matrix(m, k) = 1 Then Proverka = False
   Next
End If
End Function

  Наконец-то движение сделано, так что можете откомпилировать код и убедиться в том, что оно происходит корректно во все стороны.

#6
15:19, 13 янв 2008

  Падение и исчезновение.

  Следующих шаг на пути создания тетриса – это падение фигур. Поставим на форму второй таймер. Пусть он каждую секунду вызывает процедуру
Dvig X, Y + 1

  Кубики начнут опускаться не только при нажатии кнопки вниз, но и по времени. Но это ещё не полноценное падение, ведь если фигура «наткнётся» на препятствие (пол или занятые ячейки), то нужно все её кубики сделать «застывшими» и создать вверху экрана новый предмет. Сделаем для этих целей по процедуре.
  С застыванием всё просто, это такой же поэлементный цикл, как удаление или копирование, поэтому просто добавим строку в процедуру Deistv:
If H = "Zastiv" And Pole(X + m, Y + k) = 1 Then Pole(X + m, Y + k) = 2

  Теперь перечислим координаты кубиков, которыми можно задать 7 типов тетрисных фигур и сделаем процедуру появления случайного предмета:

Sub NewElement()
Dim a As Byte
a = Int(Rnd * 7)
Select Case a
Case 0 'Палка
    Pole(4, 0) = 1
    Pole(4, 1) = 1
    Pole(4, 2) = 1
    Pole(4, 3) = 1
Case 1 'Пол креста
    Pole(4, 0) = 1
    Pole(3, 1) = 1
    Pole(4, 1) = 1
    Pole(4, 2) = 1
Case 2 'Квадрат
    Pole(4, 1) = 1
    Pole(4, 2) = 1
    Pole(5, 1) = 1
    Pole(5, 2) = 1
Case 3 'Ход конём
    Pole(4, 0) = 1
    Pole(5, 0) = 1
    Pole(4, 1) = 1
    Pole(4, 2) = 1
Case 4 'Зеркальный ход конём
    Pole(5, 0) = 1
    Pole(4, 0) = 1
    Pole(5, 1) = 1
    Pole(5, 2) = 1
Case 5 'Молния
    Pole(4, 0) = 1
    Pole(4, 1) = 1
    Pole(5, 1) = 1
    Pole(5, 2) = 1
Case 6 'Зеркальная молния
    Pole(5, 0) = 1
    Pole(5, 1) = 1
    Pole(4, 1) = 1
    Pole(4, 2) = 1
End Select
X = 3
Y = 0
End Sub

  Не забудьте в Form_Load убрать появление первой фигуры, ведь она тоже должна выбираться случайным образом:
Randomize
NewElement

ИзображениеЗамечание. Если не написать слова Randomize, то во время игры элементы будут появляться в случайном порядке, но при повторном запуске фигуры будут выпадать в той же последовательности, что и первый раз. А единожды вызванная при загрузке формы функция Randomize обеспечивает при каждом запуске различную череду событий.

  Итак, процедуры готовы. Осталось их только вызвать при падении и контакте с препятствием. Cделаем это, когда при попытке движения (dvig), перемещение было запрещено (Proverka = false), а это движение совершалось вниз (NewY > Y):

Sub Dvig(NewX As Integer, NewY As Integer)
Deistv "Copy"
If Proverka(NewX, NewY) Then
  …

  …
Else
   If NewY > Y Then
      Deistv "Zastiv"
      Lines
      NewElement
   End If
End If
End Sub

  Третья функция Lines – это проверка, не собрана ли какая-либо из линий целиком, которую очень удобно делать между этими функциями; поймав момент, когда кубики уже застыли, но новые не появились, а, значит, рабочая матрица всё ещё внизу. Её координата Y нам нужна для того, чтобы не перебирать все строки, а проверить только те, на которые упал кубик:

Sub Lines()
Dim Polnoe As Boolean
For k = Y To Y + 3
   Polnoe = True
   For m = 0 To 9
      If Pole(m, k) = 0 Then Polnoe = False
   Next
   If Polnoe = True Then Sdvig k
Next
End Sub

  Здесь, если мы находим собранную линию, то вызываем процедуру Sdvig, чтобы все линии, которые находятся выше её, опустились на одну ячейку вниз:

Sub Sdvig(ByVal Start As Byte)
Dim L As Integer, p As Integer
For L = Start To 5 Step -1
   For p = 0 To 9
      Pole(p, L) = Pole(p, L - 1)
   Next
Next
End Sub

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

Изображение 

Рис 22. Застывающие кубики.

#7
15:19, 13 янв 2008

  Вращение фигур.

  Несмотря на то, что почти всё сделано, толком поиграть ещё нельзя. А всё потому, что фигуры пока нельзя переворачивать. И хотя это, наверное, самая сложная часть тетриса, нам её будет сделать довольно легко, потому что мы с самого начала делали всё с использованием дополнительной матрицы, и поворот фигуры будет сводиться к повороту этой матрицы.
  Итак, поворот матрицы вправо – это процесс, когда номер столбца равен номеру строки соответствующей ячейки в новой матрице, а номер строки равен номеру столбца ячейки в новой матрице, если считать с конца. При повороте влево, естественно, тоже, но наоборот. Это легко понять, если взглянуть на рисунок:

Изображение 

Рис. 23. Поворот матрицы.

  Таким образом, поворот практически не отличается от обычного копирования из рабочей области. Добавим две новых строки в Deistv:

If h = "Copy" Then Matrix(m, k) = Pole(X + m, Y + k)
If h = "Vpravo" Then Matrix(3 - k, m) = Pole(X + m, Y + k)
If h = "Vlevo" Then Matrix(k, 3 - m) = Pole(X + m, Y + k)

  Пусть эти повороты будут происходить при нажатии клавиш Q и E:

If KeyCode = vbKeyQ Then Dvig X, Y, "Vlevo"
If KeyCode = vbKeyE Then Dvig X, Y, "Vpravo"

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

Sub Dvig(NewX As Integer, NewY As Integer, Optional Param As String = "Copy")
Deistv Param
…

  Если Param не будет задан явно, то по умолчанию выполнится копирование, так что все старые вызовы процедуры останутся корректными.
  Вот и всё, наш тетрис практически готов. Но не время расслабляться, надо ещё «навести красоту», приделав нормальное меню, очки, и тому подобные вещи.

Наводим красоту.

Для начала давайте сделаем две кнопки место одной: «Новая игра» и «Выход». Просто вывести два раза одну и ту же кнопку нам недостаточно, ведь нам нужно их отличать. Поэтому добавим в процедуру вывода кнопки третий параметр, его уникальный индекс:
Sub DrawKnopka(X As Integer, Y As Integer, Index As Single)

И дальше, в теле процедуры, мы будем использовать его, чтобы присвоить глобальной переменной KnopkaNum индекс подсвеченной кнопки:

If XCur > X - Xsd And XCur < X + Xsd And YCur > Y - Ysd And YCur < Y + Ysd Then
n = 2
KnopkaNum = Index
End If

Разумеется, действия по нажатию этих кнопок тоже будут различны:

If KnopkaNum = 1 Then
    For m = 0 To 9
        For k = 0 To 24
            Pole(m, k) = 0
        Next
    Next
    NewElement
    Game = True
End If
If KnopkaNum = 2 Then Form_QueryUnload 0, 1

Здесь вторая кнопка – это выход из игры, а первая должна начать игру заново. Для этого мы очищаем поле ото всех кубиков, создаём новый элемент (падающую фигуру), и включаем переменную Game. Она должна быть объявлена глобально, и в случае, если она равна False, игра должна останавливаться (для этого просто допишите проверку значения Game в процедуре Dvig).
Остановка игры происходит, когда застывшие кубики добрались до 5 строчки, поэтому проверку сделаем при их застывании:

Deistv "Zastiv"
Lines
For m = 0 To 9
      If Pole(m, 4) = 2 Then Game = False
Next
If Game = True Then NewElement

Заметьте, что если игра остановилась, то выводить новый элемент не нужно (он появится, когда мы нажмём «Новая игра»).

Последний штрих – это очки и возрастание скорости. Сделаем их вывод текстом, а заодно и подпишем кнопки. На всё про всё нам понадобится 6 текстовых спрайтов и 2 переменные для хранения количества очков и текущего уровня:

Dim KnopkaNum As Byte
Dim Game As Boolean
Dim Ochki As Integer, Level As Integer
Dim SprText(1 To 6) As New SR2D_Sprite

Загрузим спрайты в Form_Load:

SpriteCreateText SprText(1), "Новая игра", ARGB(0, 200, 200, 0)
SpriteCreateText SprText(2), "Выход", ARGB(0, 200, 200, 0)
SpriteCreateText SprText(3), "Уровень:", ARGB(0, 0, 255, 255)
SpriteCreateText SprText(4), "Игра:", ARGB(0, 0, 255, 255)
Obnov

Последние два спрайта, выводящие значения переменных, будут меняться, поэтому они вынесены в процедуру Obnov, которая выглядит так:

Sub Obnov()
SpriteCreateText SprText(5), Str(Level), ARGB(0, 0, 255, 255)
SpriteCreateText SprText(6), Str(Ochki), ARGB(0, 0, 255, 255)
Level = Int(1 + Sqr(Ochki / 500))
Timer2.Interval = 1000 / Sqr(Level)
End Sub

Здесь мы ещё устанавливаем уровень сложности в зависимости от набранных очков, и меняем скорость падения кубиков. В моём примере получается следующая таблица:

Изображение

Очки можно начислять при падении кубика (количество очков зависит от уровня):

Ochki = Ochki + 10 * Level
Obnov

  А также при исчезновении линий:
Ochki = Ochki + 100 * Level

Осталось только вывести на экран все надписи и кнопки (не забываем, что у кнопок есть уникальный индекс):

TextDraw 100, 50, 1
DrawKnopka 100, 100, 1
TextDraw 100, 150, 2
DrawKnopka 100, 200, 2
TextDraw 250, 50, 3
TextDraw 250, 150, 4
TextDraw 250, 100, 5
TextDraw 250, 200, 6

Процедура TextDraw примет такой вид:

Sub TextDraw(X As Integer, Y As Integer, Num As Byte)
SprFon.Draw SprText(Num), X - SprText(Num).Width / 2, Y - SprText(Num).Height / 2, OpAdd
End Sub
ИзображениеЗамечание. Необязательный параметр OpAdd позволяет отобразить текстовый спрайт с эффектом полупрозрачности. В арсенале движка SR2D есть различные эффекты для вывода графики, полный список которых вместе с расшифровкой вы найдёте в справке движка.

Теперь уберём вывод FPS и рамки рабочей матрицы; добавим обнуление счёта при старте новой игры; и тетрис готов. Можете себя поздравить, мы только что сделали нашу первую игру. Если теперь откомпилировать exe-файл и запустить, то мы должны увидеть примерно такую картинку:

Изображение 

Рис 24. Финальный вариант тетриса.

  Кстати, если вам во время игры в оригинальный тетрис казалось, что разработчики специально сделали так, что на высокой сложности нужная фигра выпадает реже, нежели всё остальное; то теперь вы можете убедиться, что заветная «палка» не попадает к нам в руки исключительно по закону подлости :)
  В заключение напомню, что если вы до сих пор не скачали исходный код нашего тетриса, то можете скачать его сейчас.

Что делать дальше?

Теперь вы можете посмотреть в папке движка примеры реализации различных эффектов, таких как огонь, вода, бамп мэппинг:

Изображение 

Рис 25. Свет и огонь.

  После этого вы можете приступать к созданию своей игры. Но не стоит замахиваться на что-то большое. Не стоит браться за игры в жанре RPG или RTS. Я бы советовал начать с арканоида или в крайнем случае, аркадного скролера или платформера.
  На этом я закончу свою статью, если у вас возникли какие-то вопросы, связаться со мной можно так:
e-mail:
ICQ: 254110101

#8
7:05, 14 янв 2008

Я понимаю, что читать это всё лениво; но, может быть, хоть как-нибудь откомментируете?

#9
8:06, 14 янв 2008

Alprog
Жесть. Респект за проделанный труд. К сожалению, а может и к счастью, мой путь развия шел не через спрайтово-тайловые игры и вижуал бейсик, как у тебя, поэтому прокомментировать затруднюсь и просто еще раз выражу респект :)

//омг, злобный орк с темным эльфом особенно порадовали :D

#10
8:14, 14 янв 2008

Suslik
Спасибо :)

Кстати, тетрис я впервые в жизни писал как раз-таки для статьи.

#11
8:22, 14 янв 2008

Alprog
>Кстати, тетрис я впервые в жизни писал как раз-таки для статьи.
Маленькое и Очень скромное РПГ ты тоже для статьи пишешь? =)

Меня почему-то, когда я только начинал заниматься программированием, тянуло к максимальной свободе действий - то есть никаких тайлов, никакой сетки и ключей прозрачности. Моя первая змейка могла поворачиваться на любой угол, не обязательно на 90o - отлично помню, как синусы и косинусы мне в шестом классе объяснял дед, который сам-то с трудом все помнил :D Но у такого подхода есть свои очевидные минусы - я бОльшую часть времени не игры писал, а пытался разбираться в каких-то учебниках, узнавал у старших основы математики, было забавно наблюдать, как мама пыталась сгенерить мне какую-нибудь информацию по афинным преобразованиям - мне очень хотелось нарисовать спрайт, заданный своими четырьмя углами :D

#12
8:42, 14 янв 2008

Suslik
>Маленькое и [b]Очень[/b] скромное РПГ ты тоже для статьи пишешь? =)
На что намекаешь? 8)

#13
8:58, 14 янв 2008

Alprog
Только на твою скромность! Ни на что более! :)

#14
9:25, 14 янв 2008

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

А вот таких записей ...

Private Declare Function EnumDisplaySettings Lib "user32" Alias "EnumDisplaySettingsA"_
(ByVal lpszDeviceName As Long, ByVal iModeNum As Long, lpDevMode As Any) As Boolean

...я бы испугался и убежал:)

Страницы: 1 2 3 4 Следующая »
ФлеймФорумРазработка игр

Тема в архиве.