Продолжаем писать Mario.
0.) Доброе время...
Доброе время суток мои читатели. Этот пост - продолжение постов про gamedev(игростроении). Давайте мельком пробежимся по уже сделанному нами.
- Итак:
Мы мельком рассмотрели основы графики и даже создали нашего героя.
- Рассмотрели компоненты которые вскоре и помогут нам создать полноценную игру.
- И рассмотрели стандартные классы и их иерархию.
Какие у нас планы на этот урок:
- Мы должны поработать с DXInput - компонент отвечающий за ввод данных с устройств ввода
- Рассмотреть основы анимации
- Заставить mario бегать.
Вроде всё сказано, чего медлить ближе к делу!
1.) Чтение с устройств ввода
Я уже упоминал в прошлом посте про компонент TDXInput (подробнее тут) . Разберём его концепцию.
То есть TDXInput.States принимает состояние, если же оно совпадает с табличным значением(которое можно настраивать):
Когда мы должны узнавать о изменениях состояния? - Всё время. По этой причине мы должны добавить компонент таймер - TDXTimer. Его значение Interval лучше установить на 33. То есть частота обновления будет 33 милисекунды. Это же будет и частота обновления графики. Создаём его процедуру OnTimer.
Всё , что у нас было мы изменим. Теперь проверим можем ли мы рисовать? т.е. установлен ли DirectX , рабочая видеокарта и т.д.
Получим код :
if not DXDraw.CanDraw then exit; //если не можем рисовать - значит выход
Тем самым мы избавимся от ошибок. Интересно, что DirectX позволяет выводить текст. Мы этим и воспользуемся! Выведем нужную информацию:
{ Информация }
with DXDraw.Surface.Canvas do //работаем с холстом
begin
Brush.Style := bsClear; //прозрачный фон
Font.Color := clBlack; //цвет шрифта
Font.Size := 12; //размер шрифта
Textout(0, 0, 'FPS: '+inttostr(DXTimer.FrameRate)); //FS
Textout(0, 24, 'Sprite: '+inttostr(DXSpriteEngine.Engine.AllCount)); //кол - во спрайтов
Textout(0, 48, 'Draw: '+inttostr(DXSpriteEngine.Engine.DrawCount));
if FMoveMode then //Time mod
Textout(0, 72, 'Time mode: 60 FPS')
else
Textout(0, 72, 'Time mode: Real time');
Release; //Освобождаем память...
end; //конец работы с холстом
Мы хотели сделать проверку нажатий а занимаемся какой-то ерундой. Исправляемся! Всего одна строка :
DXInput.Update;
Обновит состояние Нашего "чтеца".
И в конце-концов наша процедура получит полностью извращённый вид:
procedure TMainForm.DXTimerTimer(Sender: TObject; LagCount: Integer);
begin
if not DXDraw.CanDraw then exit; //выходим при проблемах
DXInput.Update; //обновляем состояние
if FMoveMode then
LagCount := 1000 div 60; //кол-во перемещений
DXSpriteEngine.Move(LagCount); //перемещяем
DXSpriteEngine.Dead; //уираем
{ Рисование }
DXDraw.Surface.Fill(0);
DXSpriteEngine.Draw; //Нарисовать
{ Информация }
with DXDraw.Surface.Canvas do //работаем с холстом
begin
Brush.Style := bsClear; //прозрачный фон
Font.Color := clBlack; //цвет шрифта
Font.Size := 12; //размер шрифта
Textout(0, 0, 'FPS: '+inttostr(DXTimer.FrameRate)); //FS
Textout(0, 24, 'Sprite: '+inttostr(DXSpriteEngine.Engine.AllCount)); //кол - во спрайтов
Textout(0, 48, 'Draw: '+inttostr(DXSpriteEngine.Engine.DrawCount));
if FMoveMode then //Time mod
Textout(0, 72, 'Time mode: 60 FPS')
else
Textout(0, 72, 'Time mode: Real time');
Release; //Освобождаем память...
end; //конец работы с холстом
DXDraw.Flip; //Нарисовать
end;
"Не чего не изменилось!Он не бегает" - скажите вы.
Ответный вопрос - "А мы. что сказали ему , что либо сделать?".
Объясним mario!
Мы когда-то описывали процедуру
TMarioHero.DoMove(MoveCount: Integer);
Теперь мы её усовершенствуем. Мы уже имеем строку которая зовёт папу, что бы тот всё сделал. После неё пишем:
AnimLooped := False; //Прекращаем анимацию
Хотя анимацию мы разберём позже, строка нужна.
После опишем огромный код:
if (MainForm.DXInput.Joystick.X<>0) or (MainForm.DXInput.Joystick.Y<>0) then //если значения не равны нулю переместимся
begin
X := X + (MainForm.DXInput.Joystick.X/1000)*MoveCount;
Y := Y + (MainForm.DXInput.Joystick.Y/1000)*MoveCount;
end
else
begin
if isUp in MainForm.DXInput.States then //марио должен идти вверх?
begin
Y := Y - (300/1000)*MoveCount;
end;
if isDown in MainForm.DXInput.States then //или в низ?
begin
//
end;
if isLeft in MainForm.DXInput.States then //а может на лево
begin
X := X - (300/1000)*MoveCount;
end;
if isRight in MainForm.DXInput.States then //или на право
begin
X := X + (400/1000)*MoveCount;
end;
end;
Collision; // проверим столкновения
//обновим информацию движку
Engine.X := -X+Engine.Width div 2-Width div 2;
Engine.Y := -Y+Engine.Height div 2-Height div 2;
Как мы говорили выше мы просто проверили значения полученные от Клавиатуры.
2.) Анимация
Вот спрайты которые мы будем использовать(Эх сколько работы!)
Выделю важные моменты относительно Листа Изображений.
PaternWidth и PaternHeight обязаны быть отличны от нуля!
Рассмотрим основы анимации:
После всего процедура будет иметь такой вид:
procedure TMarioHero.DoMove(MoveCount: Integer);
begin
inherited DoMove(MoveCount);
AnimLooped := False;
if (MainForm.DXInput.Joystick.X<>0) or (MainForm.DXInput.Joystick.Y<>0) then
begin
X := X + (MainForm.DXInput.Joystick.X/1000)*MoveCount;
Y := Y + (MainForm.DXInput.Joystick.Y/1000)*MoveCount;
end
else
begin
if isUp in MainForm.DXInput.States then
begin
Y := Y - (300/1000)*MoveCount;
if Image.Name='mario_down' then
begin
Image := MainForm.ImageList.Items.Find('mario_downtoup'); //загружаем его спрайт
AnimCount := Image.PatternCount;
AnimLooped := True;
AnimSpeed := (400/1000);
end;
end;
if isDown in MainForm.DXInput.States then
if Image.Name<>'mario_down' then
begin
Image := MainForm.ImageList.Items.Find('mario_down'); //загружаем его спрайт
AnimCount := Image.PatternCount;
AnimLooped := True;
AnimSpeed := (400/1000);
end;
if isLeft in MainForm.DXInput.States then
begin
X := X - (300/1000)*MoveCount;
Image := MainForm.ImageList.Items.Find('mario_left'); //загружаем его спрайт
AnimCount := Image.PatternCount;
AnimLooped := True;
AnimSpeed := (300/1000);
end;
if isRight in MainForm.DXInput.States then
begin
X := X + (400/1000)*MoveCount;
Image := MainForm.ImageList.Items.Find('mario_right'); //загружаем его спрайт
AnimCount := Image.PatternCount;
AnimLooped := True;
AnimSpeed := (400/1000);
end;
end;
Collision;
Engine.X := -X+Engine.Width div 2-Width div 2;
Engine.Y := -Y+Engine.Height div 2-Height div 2;
end;
Я надеюсь, что вы уже сами разберётесь с каждой строчкой.
Теперь наша игра станет ещё интереснее и привлекательней, действия более анимированы, изменён фон игры, и добавлена игровая информация.
Сегодняшний The End :