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


Георгий Мошкин
tmtlib@narod.ru

Файлы к статье:
pulki.zip (10kb)
- движение игрока и летающие пульки.
WAREXE.RAR - 188 kb - реализация описанного здесь искуственного интеллекта (EXE и исходники на Delphi).
WARCRAFT.ZIP - 25kb - только исходники (без EXE)

Такой подход:
- сначала пишешь программу для вывода точки с координатой (x,y) на экран
- потом делаешь так, чтобы (x,y) можно было менять с клавиатуры
- потом пробуешь двигать этой точкой по формулам.

Например, смещение вправо:
x:=x+1;
draw(x,y);

Или движение по кругу:
t:=t+1;
x:=10*sin(t);
y:=10*cos(t);
draw(x,y);

- потом вместо точки выводишь спрайт
- потом пытаешься добавить if-ы

Например, движение влево-вправо с отталкиванием от краёв экрана:
x:=x+dx
if x>1024 then dx:=-1;
if x<0 then dx:=+1;
draw(x,y)

- потом учитываешь скорость работы компьютера

Например, для движения вправо будет
speed:=10*vremyaotrisovkikadra;
x:=x+speed;
draw(x,y);

- потом делаешь движение спрайта по экрану с помощью клавиатуры, и прописываешь if-ами края экрана

- потом пробуешь добавить мышку, при клике запоминаешь координаты "куда идти"

if MouseLeftButtonClick=true then
 begin
  kuda_x:=mouse.x;
  kuda_y:=mouse.y;
 end;

и двигаешь туда свой спрайт по какому-нибудь алгоритму. Например так:
if (x-kudda_x)>0 then x:=x-speed else x:=x+speed;
if (y-kudda_y)>0 then y:=y-speed else y:=y+speed;

- потом, если игра "вид сбоку", пробуешь делать прыжки и "гравитацию"

Если стоит на земле, то при нажатии кнопки вверх - прыгать:
if ((KeyboardUP) and (y=0)) then y:=10;
if y>0 then y:=y-speed;

- потом вместо примитивного управления делаешь более "продвинутое":

Для игры "вид сверху" управление идёт НЕ КООРДИНАТАМИ x,y, а скоростью SPEED и
углом поворота Phi.
Например, если нажата кнопка вперёд,
то делаешь SPEED:=10*vremyaotrisovkikadra;
Если назад: SPEED:=-10*vremyaotrisovkikadra;
Если ничего не нажато, то SPEED:=0;
Влево/вправо: меняешь Phi:
If KeyboardLeft then Phi:=Phi-5*vremyaotrisovkikadra;
If KeyboardRight then Phi:=Phi+5*vremyaotrisovkikadra;

Координаты тогда меняются по sin/cos:
x:=x+speed*sin(Phi));
y:=y+speed*cos(Phi));
Т.е. если speed=0 то спрайт останется на месте.

DrawSprite(x,y);

- потом начинаешь задавать типы и массивы. Создаёшь тип живого существа и масссив
переменных этого типа (много существ).

- потом создаёшь массив летящих пуль/стрел/ракет в уровне

Tpulya=record
x,y:single;
phi:single;
tippuli:integer;
lifetime:integer;
end;

puli:array[0..1000] of Tpulya;

Если tippuli = 0 - элемент массива свободен
Если tippuli > 0 - то это пуля с разрушительной силой, соответствующей индексу

После выстрела:
- ищешь свободный элемент массива (где tippuli=0);
- загоняешь в этот элемент данные (координату откуда вылетает (x,y) и угол под
которым она выпущена (phi)).
- ставишь этой пуле какой-нибудь тип в соответствии с оружием, из которого
 вылетела пуля (tippuli:=...);
- ставишь этой пуле время жизни (например, lifetime=10 секунд).

В игре меняешь координаты пули также, как и у игроков (см. выше). Каждый кадр
уменьшаешь время жизни пули для всех пуль в массиве:
for i:=0 to 1000 do
 if puli[i].tippuli>0 then
  begin
//   МЕНЯЕМ КООРДИНАТЫ ПУЛИ
   puli[i].x:=puli[i].x+10*sin(puli[i].phi);
   puli[i].y:=puli[i].y+10*cos(puli[i].phi);
//   ВЫРУБАЕМ ПУЛЮ, ЕСЛИ СЛИШКОМ ДОЛГО ЛЕТИТ:
   puli[i].lifetime:=puli[i].lifetime - vremyaotrisovkikadra;
   if puli[i].lifetime<0 then puli[i].tippuli=0;
  end;

- в этом же цикле с пулями можешь проверять на столкновение с игроками. Если
происходит столкновение с игроком, то можно отнять у игрока часть его "энергии"
power:=power-1 и ДЕЗАКТИВИРОВАТЬ ПУЛЮ: puli[i].tippuli=0;

- создаёшь формат уровня на своё усмотрение. Или какую-нибудь "сетку" (двухмерный
массив). Или одномерный массив, в котором задаются координаты и тип объектов
(например, 157 элемент такого массива говорит нам, что это стена с координатой (x,y)
=1414.44,776.15)
ДАЛЬШЕ

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

 1. Сохраняешь координаты игрока old.x:=x; old.y:=y;
 2. Перемещаешь игрока по клавиатуре, мышке или по AI (меняешь x,y).
 3. Проверяешь координаты игрока x,y на совпадение со стенами и любыми преградами.
 4. Если есть совпадение (точка x,y попала "в стену"), то восстанавливаешь
старые значения:
 x:=old.x;
 y:=old.y;

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

- ищешь другие алгоритмы - выбираешь какая проверка столкновений тебе больше нравится

- если делаешь стратегию, то дополняешь запись данных игрока данными "свой-чужой":

tigrok=record
ai:integer; // 0 - управляется с клавиатуры/мышки, 1,2,3,4 - управляется компом по
 алгоритму AI-1, AI-2, AI-3,... (1-мышь, 2-овца, 3-крокодил, 4-робот, 5-вертолёт и т.д.).
color:integer // ЦВЕТ (на самом деле и есть данные "свой-чужой"
end;

- продолжаю про стратегию: делаешь массив из игроков. Допустим будет три рассы (люди,
орки и нло).

igroki:array[0..300] of TIgrok;
Треть игроков делаешь ( 0-100) color:=1;
Вторую треть (100-200) - color:=2;
А третью (200-300) - color:=3;

В алгоритме AI и т.п. делаешь проверку: Если игрок видет игрока с другим цветом,
то AI переходит в режим "атаки".

- режим атаки, и как сделать кто за кем гоняется.

tigrok=record
ai:integer;
color:integer;
x,y,phi:single;
power:integer;
ВЫШЕ ВСЁ ПОНЯТНО, ТЕПЕРЬ НОВОЕ:
zakemhodit:integer;
kogoatakovat:integer;
end;

То есть если ты с клавиатуры нажал мышкой атаку над монстром igrok[123],
то делаешь у своего игрока kogoatakovat:=123;

Вообщем, если ты мышкой выбрал своего игрока под номером,
допустим, 555, то делаешь так:

igrok[555].kogoatakovat:=123;

- Откуда взялись числа 555 и 123? Это просто. Ты просто сделаешь алгоритм выбора мышкой. Допустим, у тебя на экране есть спрайты. И есть курсор мышки. Нужно сделать процедуру, которая при нажатии мышкой проверяет КТО СИДИТ ПОД МЫШКОЙ! Если под мышкой кто-то есть, то ты проверяешь его номер (например, 555). И задаёш SeychasUpravlyaem:=555;

igrok[SeychasUpravlyaem].kogoatakovat:=PoluchitNomerIgrokaPodMishkoy(x,y);
а смысл будет прежним:
igrok[555].kogoatakovat:=123;

- Потом делаешь AI, который проверяет параметры всех, кто вокруг него. Если вокруг слишком много ВРАГОВ (враги - это другой цвет). То убегаешь (AI убегает).

- Чтобы в стратегии могли ходить друг за другом и группами ты одного выбираешь главным и его управление переключается с AI на клавиатуру. Хотя может быть и комбинированно (управляешь с клавиатуры, но если наткнёшься на врага - то автаматически врубать режим атаки).

tigrok=record
ai:integer;
color:integer;
x,y,phi:single;
power:integer;
zakemhodit:integer; - ВОТ ЭТО ЗА КЕМ ХОДИТЬ
kogoatakovat:integer;
end;

- Опять же, если стратегия, можешь сделать подражание действиям главного. Например, если у десятерых игроков zakemhodit:=15, то они все будут брать себе kogoatakovat от 15-го игрока.

- Как только сделан формат уровней, сразу же нужно писать УДОБНЫЙ РЕДАКТОР УРОВНЕЙ. Простой удобный редактор уровней - это главное. То есть хороший формат (типы, массивы) и хороший к ним редактор. И вообще разные утилиты для редактирования игры.

- Как только хорошо получилось делать один уровень. Редактировать его, загружать, играть в нём. Тогда уже делаешь переходы между уровнями.

- Делаешь всё просто. Не стоит изумляться чужим творениям и исходникам. Порою за красивой графикой скрывается ужасающе кошмарный исходник. Глюки в своих программах замечаешь лучше, чем в новом чужом исходнике. Новый чужой исходних ощущаешь как "дар от внеземных цивилизаций", хотя на самом деле это не так!

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

ВСЁ =)