IPB
ЛогинПароль:

 
 Ответить  Открыть новую тему 
> Игра с корабликами, графический режим
сообщение
Сообщение #1





Группа: Пользователи
Сообщений: 9
Пол: Мужской
Реальное имя: тьома

Репутация: -  0  +


Привета!
Задача формулировалась преподом довольно размыто, поэтому может присутствовать некоторая вольность в выполнении, что не может не радовать. Смысл в том, чтоб написать на Pascal (к сожалению) игру, в которой есть кораблики, соревнующиеся между собой Препод седой уже, кстати. Он хотел там видеть возможность выбора направления ветра и хода корабликов галсами при встречном ветре, но реализовать управление парусами, по-моему, слишком сложно, а как без этого галсами ходить? Поэтому реализация мне видится так: вид сверху на остров либо замкнутый кольцевой канал, один кораблик с мотором, цель игры - управляя корабликом стрелками пройти дистанцию за наименьшее время. Если у кого-то возникнет желание помочь или вдруг есть какие-то наработки, буду очень признателен.
Вот код:
program MAPA3M;
uses crt, graph;
var
Gd,Gm:integer;
posX,posY:integer;
ch:char;

procedure bereg;
begin
setcolor(yellow);
setfillstyle(7,yellow);
sector(280,200,0,360,100,50);
sector(360,200,0,360,80,80);
sector(280,280,0,360,120,70);
sector(360,280,0,360,60,75);
sector(15,450,0,360,55,80);
sector(260,460,0,360,60,30);
sector(600,470,0,360,150,30);
sector(15,410,0,360,20,80);
sector(30,20,0,360,40,25);
sector(500,0,0,360,90,50);
sector(600,15,0,360,80,50);
sector(630,230,0,130,50,100);
sector(610,160,0,120,50,120);
sector(610,450,0,110,75,100);
sector(650,350,0,220,60,100);
sector(140,15,0,200,80,30);
sector(200,20,0,360,100,50);
sector(60,150,60,220,150,200);
sector(150,30,0,360,100,100);
sector(5,240,30,330,40,90);
sector(630,240,0,360,70,100);
sector(360,5,0,360,100,30);
sector(630,240,110,300,70,100);
sector(0,280,0,360,80,100);
sector(100,470,360,360,120,60);
pieslice(380,420,200,340,100);
end;
begin
clrscr;
Gd:=VGA;
Gm:=VGAhi;
initgraph(Gd,Gm,'');
if graphresult=grok then
begin
bereg;
setcolor(brown);
setfillstyle(1,brown);
posX:=500;
posY:=250;
fillellipse(posX,posY,1,1);
repeat
ch:=readkey;
case ch of
#72:begin posY:=posY-5; fillellipse(posX,posY,1,1);setcolor(black);
setfillstyle(0,black);fillellipse(posX,posY+5,1,1);setcolor(brown);
setfillstyle(1,brown); end;
#75:begin posX:=posX-5; fillellipse(posX,posY,1,1);setcolor(black);
setfillstyle(0,black);fillellipse(posX+5,posY,1,1); setcolor(brown);
setfillstyle(1,brown); end;
#77:begin posX:=posX+5; fillellipse(posX,posY,1,1);setcolor(black);
setfillstyle(0,black);fillellipse(posX-5,posY,1,1);setcolor(brown);
setfillstyle(1,brown); end;
#80:begin posY:=posY+5; fillellipse(posX,posY,1,1);setcolor(black);
setfillstyle(0,black);fillellipse(posX,posY-5,1,1);setcolor(brown);
setfillstyle(1,brown); end;
#27:halt;
end;{case}
until ch=#27;
closegraph;
end
else
writeln(grapherrormsg(graphresult));
end.
Тут нарисованы берега "лагуны" с островом посередине, вид сверху. Кораблик обозначен коричневой точкой и управляется с клавиатуры, это пока все =/ Как видите, нету навыка работы с процедурами, пишется сложно =/
Что конкретно неясно как реализовать:
Как сделать чтоб кораблик при нажатиии клавиши начинал двигаться в направлении нажатой стрелки и не прекращал движение? Сейчас он двигается только, когда стрелка нажата.
Можно ли сделать движение по диагонали? Сейчас судно движется только в четырех направлениях.
Как реализовать крушение кораблика при столкновении с берегом? (Видимо, нужно написать функцию, возвращающую значение цвета пикселов вокруг кораблика, и если эти значения равны значению цвету берега - корабль тонет)
Как с помощью Pascal реализовать счетчик времени? Ведь цель игры - пройти дистанцию за наименьшее время. Искал в учебниках, а там работа только с временем системы. Как сделать, чтоб игрок вводил свое имя и результаты записывались в текстовый файл?
Как сделать скорость кораблика независимой от тактовой частоты? Ведь delay зависит, если не ошибаюсь, от этого параметра, значит на разных компьютерах игра с этой процедурой будет иметь разную скорость?

Заранее спасибо.
Файл pas в аттаче.


Прикрепленные файлы
Прикрепленный файл  _.PAS ( 2.16 килобайт ) Кол-во скачиваний: 389
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #2


Гуру
*****

Группа: Пользователи
Сообщений: 1 168
Пол: Мужской
Реальное имя: Сергей Андрианов

Репутация: -  28  +


>Как сделать чтоб кораблик при нажатиии клавиши начинал двигаться в направлении нажатой стрелки и не прекращал движение? Сейчас он двигается только, когда стрелка нажата.

Сначала надо полностью продумать систему уравления. Как, например, остановить корабль? В играх от Sierra, например, было принято, что остановка происходит при повторном нажатии на клавишу. Но может оказаться целесообразной и остановка путем нажатия на клавишу противоположного направления. И в том, и в другом случае необходимы переменные, в которых запоминается текущее состояние движения.

>Можно ли сделать движение по диагонали? Сейчас судно движется только в четырех направлениях.

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

>Как реализовать крушение кораблика при столкновении с берегом? (Видимо, нужно написать функцию, возвращающую значение цвета пикселов вокруг кораблика, и если эти значения равны значению цвету берега - корабль тонет)

Можно и так.

>Как с помощью Pascal реализовать счетчик времени? Ведь цель игры - пройти дистанцию за наименьшее время. Искал в учебниках, а там работа только с временем системы. Как сделать, чтоб игрок вводил свое имя и результаты записывались в текстовый файл?

Правильно. Другого времени и не существует.
В момент старта запоминаешь текущаа состояние времени, а потом вычитаешь его из текущих показаний.

Ввод имени, очевидно, осуществляется процедурой realln, а запись его в файл - writeln.

>Как сделать скорость кораблика независимой от тактовой частоты? Ведь delay зависит, если не ошибаюсь, от этого параметра, значит на разных компьютерах игра с этой процедурой будет иметь разную скорость?

А она зависит? Delay специально сделана так, чтобы не зависела. Но с одной стороны - ты ее не используешь, а с другой - скорость в твоем случае определяется скоростью автоповтора клавиатуры. Чтобы избавиться от этого эффекта, можно использовать:
if keypressed then ch := readkey;
Но в этом случае тебе придется заботиться о скорости другим способом (пока ты вообще об этом не заботишься).

Еще пара замечаний:
- насколько я понял, при нажатии Esc у тебя происходит мгновенный выход из программы в обход конца цикла и восстановления режима.
- целесообразно внутри case выполнять только минимум действий: в данном случае лишь вычисление приращений координат, а всю остальную работу делать в одном месте (чтобы при изменении способа отображения корабля не пришлось менять код в 4-х местах одновременно).
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #3





Группа: Пользователи
Сообщений: 9
Пол: Мужской
Реальное имя: тьома

Репутация: -  0  +


andriano, спасибо за обстоятельный ответ.
Я попробовал сделать движение без постоянного удержания клавиши, и реализовать столкновение с берегом. Насчет системы движения: корабль должен двигаться непрерывно, не останавливаясь, есть возможность только менять направление движения, поэтому переменных для описания состояния движения не вводил. Однако, я совсем запутался даже здесь.
case ch of
#72:repeat
begin
while
(getpixel(posX+1,posY+1)and getpixel(posX-1,posY+1)and
getpixel(posX-1,posY-1)and getpixel(posX+1,posY-1)<>14)do
{проверка условия, не является ли цвет пикселей вокруг корабля цветом берега}
begin posY:=posY-5; fillellipse(posX,posY,1,1);setcolor(black);
setfillstyle(0,black);fillellipse(posX,posY+5,1,1);setcolor(brown);
setfillstyle(1,brown);delay(700) {отрисовка движения}
end;
end
until keypressed=true; {задумывалось как выход из цикла при нажатии клавиши}
{cleardevice; write('Game over. Press Enter, then Esc to exit.');readln;}
{задумывалось как выход из цикла и завершение игры}

Для непрерывности движени ввел цикл, а для крушения, как видно, проверку цвета пикселей вокруг корабля.
Но корабль двигаеся непрерывно именно до столкновения с берегом, то есть, движение прерывается только при выполнении условия цикла while, а дожен вроде бы и при нажатии клавиши (условие цикла repeat). Да и в столкновении с берегом ошибка - корабль наталкивается на берег, потом делает еще одно перемещение - уже по берегу, а только после этого движение прекращается. Еще и надпись 'Game over. Press Enter, then Esc to exit.' не выводится - поставил временно фигурные скобки, потому что программа не работает без них, не смог обнаружить ошибку.
Цитата
- целесообразно внутри case выполнять только минимум действий: в данном случае лишь вычисление приращений координат, а всю остальную работу делать в одном месте (чтобы при изменении способа отображения корабля не пришлось менять код в 4-х местах одновременно).
Хорошо бы, но я не знаю как. Подскажи, если не затруднит.
Цитата
- насколько я понял, при нажатии Esc у тебя происходит мгновенный выход из программы в обход конца цикла и восстановления режима.
Да. А как нужно?
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #4


Гуру
*****

Группа: Пользователи
Сообщений: 1 168
Пол: Мужской
Реальное имя: Сергей Андрианов

Репутация: -  28  +


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

Собственно, путаница у тебя начинается с организации циклов.
Основной цикл в игре должен быть ОДИН.
А у тебя их 3 вложенных:
- repeat ... until ch=#27
- repeat ... until keypressed=true
- while

(сразу отмечу, что "keypressed = true" и "keypressed" логически абсолютно эквивалентны, но первое вычисляется дольше и занимает в коде больше места)

Так вот, второй repeat у тебя даже не дойдет до проверки условия, пока не закончится while. А while у тебя закончится только при столкновении с берегом. О первом repeat я уже не говорю - он не успеет совершить даже одного "оборота".

Все должно делаться в одном цикле последовательно.
repeat
if keypressed then begin
ch := readkey;
case ch of
#72: direction := 1; {переменная, указывающая направление движения}
#75: direction := 2;
#77: direction := 3;
#80: direction := 4;
end; {case ch}
end; {if keypressed}
MoveShip(direction); {процедура, перемещающая корабль}
DrawShip; {процедура, рисующая корабль}
delay(50);
if CheskCollision then begin {функция, проверяющая столкновение с берегом}
for i := 0 to 19 do begin {я думаю, 20 кадров анимации крушения достаточно?}
DrawAnimation(i);
delay(50);
end; {if CheskCollision}
ch := #27; {просто способ принудительно выйти из цикла}
end;
until ch = #27;

Естественно, ВСЕ вызываемые процедуры не содержат задержек и циклов с выходом по изменению состояния.

Это в общем.
Теперь несколько конкретных ошибок:
(getpixel(posX+1,posY+1)and getpixel(posX-1,posY+1)and getpixel(posX-1,posY-1)and getpixel(posX+1,posY-1)<>14)
Здесь ты пытаешься над цветом пикселя совершать логические операции.
Правильно:
(getpixel(posX+1,posY+1) <> 14) and (getpixel(posX-1,posY+1) <> 14) and (getpixel(posX-1,posY-1) <> 14)and (getpixel(posX+1,posY-1)<>14)

Вот примерно так.
Набивал прямо здесь, код синтаксически не проверял (да и не на чем - TP у меня нет).

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

Сообщение отредактировано: andriano -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #5





Группа: Пользователи
Сообщений: 9
Пол: Мужской
Реальное имя: тьома

Репутация: -  0  +


andriano, спасибо больщущее! cool.gif
Благодаря тебе кое-что получилось, теперь уже можно играть. Конечно, пока все очень примитивно - анимация крушения, например, это всего лишь мигание кружка. Правила там не описал пока, поэтому здесь кратко: управляем корабликом - кружок, пока не врежемся в берег. Цель - не врезаться подольше.
program MAPA3M;
uses crt, graph;
var
Gd,Gm:integer;
posX,posY,i:integer;
ch:char;
dir:integer;
procedure
bereg;
begin
setcolor(yellow);
setfillstyle(7,yellow);
sector(280,200,0,360,100,50);
sector(360,200,0,360,80,80);
sector(280,280,0,360,120,70);
sector(360,280,0,360,60,75);
sector(15,450,0,360,55,80);
sector(260,460,0,360,60,30);
sector(600,470,0,360,150,30);
sector(15,410,0,360,20,80);
sector(30,20,0,360,40,25);
sector(500,0,0,360,90,50);
sector(600,15,0,360,80,50);
sector(630,230,0,130,50,100);
sector(610,160,0,120,50,120);
sector(610,450,0,110,75,100);
sector(650,350,0,220,60,100);
sector(140,15,0,200,80,30);
sector(200,20,0,360,100,50);
sector(60,150,60,220,150,200);
sector(150,30,0,360,100,100);
sector(5,240,30,330,40,90);
sector(630,240,0,360,70,100);
sector(360,5,0,360,100,30);
sector(630,240,110,300,70,100);
sector(0,280,0,360,80,100);
sector(100,470,360,360,120,60);
pieslice(380,420,200,340,100);
end;
procedure
moveship;
begin
if dir=1 then
begin
posY:=posY-5;
fillellipse(posX,posY,2,2);
setcolor(black);
setfillstyle(0,black);
fillellipse(posX,posY+5,2,2);
setcolor(brown);
setfillstyle(1,brown);
end;{if dir=1}
if dir=2 then
begin
posX:=posX-5;
fillellipse(posX,posY,2,2);
setcolor(black);
setfillstyle(0,black);
fillellipse(posX+5,posY,2,2);
setcolor(brown);
setfillstyle(1,brown);
end;{if dir=1}
if dir=3 then
begin
posX:=posX+5;
fillellipse(posX,posY,2,2);
setcolor(black);
setfillstyle(0,black);
fillellipse(posX-5,posY,2,2);
setcolor(brown);
setfillstyle(1,brown);
end;{if dir=1}
if dir=4 then
begin
posY:=posY+5;
fillellipse(posX,posY,2,2);
setcolor(black);
setfillstyle(0,black);
fillellipse(posX,posY-5,2,2);
setcolor(brown);
setfillstyle(1,brown);
end;{if dir=1}
end;{procedure}
procedure drawcollision;
begin
fillellipse(posX,posY,2,2);
delay(5000);
setcolor(black);
setfillstyle(1,black);
fillellipse(posX,posY,2,2);
delay(5000);
setcolor(brown);
setfillstyle(1,brown);
end;
{*******************************main*************************************}
begin
clrscr;
Gd:=VGA;
Gm:=VGAhi;
initgraph(Gd,Gm,'');
if graphresult=grok then
begin
bereg;
setcolor(brown);
setfillstyle(1,brown);
posX:=500;
posY:=250;
fillellipse(posX,posY,2,2);
repeat
if keypressed then
begin
ch:=readkey;
case ch of
#72:dir:=1;
#75:dir:=2;
#77:dir:=3;
#80:dir:=4;
end; {case ch}
end;
moveship;
delay(900);
if (getpixel(posX+5,posY)=14) or (getpixel(posX-5,posY)=14) or
(getpixel(posX,posY+5)=14) or (getpixel(posX,posY-5)=14) then
begin
i:=1;
for i:=1 to 8 do
begin
drawcollision;
end;{for}
ch:=#27;
end;{if..then}
until ch=#27;
closegraph;
writeln('Game over. Press Enter to exit.');
readln;
end
else
writeln(grapherrormsg(graphresult));
end.
В аттаче - exe-шник, смотрите. Только у меня exe-шник всего лишь один раз запускается, не знаю почему, а потом пишет код графической ошибки "No error" и выкидывает.
Буду теперь добавлять время, старт-финиш, пытаться сделать меню и прочее. Можно над парусом таки поработать. Пишите пожелания и предложения.

P.S. Как репутацию подымать? cool.gif


Прикрепленные файлы
Прикрепленный файл  Motor_boat.zip ( 14.97 килобайт ) Кол-во скачиваний: 272
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #6


Уникум
*******

Группа: Пользователи
Сообщений: 6 823
Пол: Мужской
Реальное имя: Лопáрь (Андрей)

Репутация: -  159  +


Цитата(Lazzy @ 18.12.2007 23:19) *

P.S. Как репутацию подымать? cool.gif

Есть два способа:
1. набрать 25 тематических постов;
2. попросить того, у кого они уже есть smile.gif.

Я поднимаю за тебя репу andriano - ты этого хотел?


--------------------
я - ветер, я северный холодный ветер
я час расставанья, я год возвращенья домой
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #7


Гуру
*****

Группа: Пользователи
Сообщений: 1 168
Пол: Мужской
Реальное имя: Сергей Андрианов

Репутация: -  28  +


По поводу ошибки - ничего сказать не могу, нет у меня ТР/ВР, да и когда был, предпочитал не пользоваться модулем graph, так что тонкостей его использования просто не знаю.
По поводу кода, рекомендую все-таки вызывать из основной программы не 1, а 3 процедуры: RestoreBackground, MoveShip и DrawShip. Просто потому, что они выполняют разную работу.
И еще: ты сначала рисуешь корабль, а потом удаляешь его старое изображение. Это неправильно. Правильно: сначала восстановить фон, затем нарисовать новое изображение.

procedure RestoreBackground;
begin
setcolor(black);
setfillstyle(0,black);
fillellipse(posX,posY,2,2);
end;

procedure MoveShip(direction : integer);
begin
case direction of
1:...
2:...
3:...
4:...
end;
end;

procedure DrawShip;
begin
setcolor(brown);
setfillstyle(1,brown);
fillellipse(posX,posY,2,2);
end;

Примерно так.

PS. Если интересно, можно оснасить программу и более адекватным интерфейсом. В 2001-2002 гг. писал по этому поводу: http://www.osp.ru/pcworld/2001/07/161910/
Увы, сейчас там почему-то не смог найти листингов, зато они есть http://pascal.sources.ru/articles/087.htm
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #8


Пионер
**

Группа: Пользователи
Сообщений: 58
Пол: Мужской
Реальное имя: Андрей

Репутация: -  2  +


Да, Delay зависит от производительности ЦП.
Вот модуль, который позволит делать задержку независимо, от ЦП:
Unit Timer;
interface
procedure Start (var T:longint);
procedure Stop (var T:longint);
procedure Pause (T:longint; Show:boolean);
Implementation
SystemTimer:longint absolute $0040:$006C;
procedure Start (var T:longint);
begin
T:=SystemTimer;
end;
procedure Stop (var T:longint);
begin
T:=SystemTimer-T;
end;

procedure Pause (T:longint; Show:boolean);
var Xn,Xt:longint;
begin
Xt:=0;
Xn:=SystemTimer;
While ((Xt-Xn)/18.2)*1000 < T do
begin
Xt:=SystemTimer;
If Show then
writeln((xt-xn)/18.2:6:4)
end;
end;
end.
Тут есть 3 процедуры: Start, Stop, Pause
Pause – тот же Delay, в параметрах – время задержки в миллисекундах, и выводить/не выводить текущее время (работает только в текстовом режиме).
Start, Stop нужны для измерения времени работы программы. В старт нужно передать вещественную переменную, в стоп ту же переменную, но после процедуры в ней будет храниться время, которое прошло с момента вызова Старт с этой переменной (проще говоря, старт сохраняет туда текущее время, а стоп вычитает из текущего это время).
Время хранится в тиках. Чтобы перевести в секунды нужно это число разделить на 18.2
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #9


Гуру
*****

Группа: Пользователи
Сообщений: 1 168
Пол: Мужской
Реальное имя: Сергей Андрианов

Репутация: -  28  +


Вообще-то это не очень хороший вариант.
Начиная с IBM PC AT поддерживается 15h прерывание, одной из функций которого как раз и есть формирование временной задержки. Кстати, величина ее задается в микросекундах, но зачем - непонятно, все равно такая точность не выдерживается.
Сейчас уже не 1992 год, компьютеры IBM PC и IBM PC XT свое отслужили и о совместимости с ними вряд ли надо заботиться. С другой стороны, на старших моделях 486 и большинстве Pentium'ов программы, содержащие модуль crt "падали" из-за переполнения при инициализации этого модуля, вызванного как раз вычислением константы для процедуры delay.
Собственно, сегодня практически все компиляторы ТР содержат уже пропатченный модуль crt. Грамотно сделанный патч выглядит так:

procedure delay(Wait:word); assembler; {єбсСαЄ¬б у јЯ}
asm
mov ax,Wait
mov dx,1000
mul dx
mov cx,dx
mov dx,ax
mov ah,$86
int $15
end;


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

Сообщение отредактировано: andriano -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #10


Пионер
**

Группа: Пользователи
Сообщений: 58
Пол: Мужской
Реальное имя: Андрей

Репутация: -  2  +


Вопрос в том, как проверить, пропаченый модуль или нет?
Проще описать вот эту процедуру, только назвать ее MyDelay (чтобы не конфликтовали) и использовать ее, чтобы не волноваться
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #11


Гуру
*****

Группа: Пользователи
Сообщений: 1 168
Пол: Мужской
Реальное имя: Сергей Андрианов

Репутация: -  28  +


Если на компе с тактовой частотой 200 МГц и выше работает, значит, патченый.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #12





Группа: Пользователи
Сообщений: 9
Пол: Мужской
Реальное имя: тьома

Репутация: -  0  +


Прошу прощения за долгое отсутствие.
Цитата
По поводу ошибки - ничего сказать не могу, нет у меня ТР/ВР, да и когда был, предпочитал не пользоваться модулем graph, так что тонкостей его использования просто не знаю.

Причина ошибки, по-видимому, в том, что в той директории, где находится исполняемый файл, для корректной его работы обязательно должен находиться еще и файл EGAVGA.BGI. Я увеличил задержку для того, чтобы программа была "играбельной" на более мощных, чем мой, ПК и перезалил архив Motor_boat.zip.
И на всякий случай залил TP smile.gif
Цитата
По поводу кода, рекомендую все-таки вызывать из основной программы не 1, а 3 процедуры: RestoreBackground, MoveShip и DrawShip. Просто потому, что они выполняют разную работу.
И еще: ты сначала рисуешь корабль, а потом удаляешь его старое изображение. Это неправильно. Правильно: сначала восстановить фон, затем нарисовать новое изображение.

Спасибо, я так и сделал. Здесь не привожу полный код программы, чтоб не загромождать топик. ####.pas можно глянуть в архиве. При разделении на три отдельные процедуры читать и редактировать программу гораздо легче.
Цитата
Есть и еще кое-какие детали, к которым можно будет вернуться в случае, если тебя интересует нечто большее, чем просто сдать и забыть.

Какие еще замечания? Я собираюсь нарисовать получше сам кораблик, и сделать меню, таймер, таблицу рекордов и т. п. Эти элементы, насколько я понимаю, во всех играх однотипны, то есть можно не писать их с нуля, а адаптировать чужие, правильно?
Всем еще раз спасибо!
Цитата
Я поднимаю за тебя репу andriano - ты этого хотел?

Именно! good.gif

Сообщение отредактировано: Lazzy -


Прикрепленные файлы
Прикрепленный файл  Motor_boat.zip ( 19.83 килобайт ) Кол-во скачиваний: 212
Прикрепленный файл  tp7.zip ( 976.18 килобайт ) Кол-во скачиваний: 218
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #13





Группа: Пользователи
Сообщений: 9
Пол: Мужской
Реальное имя: тьома

Репутация: -  0  +


Я поискал по форуму меню, наткнулся на такую версию:
Цитата
ch := readkey;
SetColor(BLACK);
X := TextWidth(Strings[i]) + 10;
Y := TextHeight(Strings[i]) + 10;
Rectangle(GetMaxX div 2 - X div 2,
GetMaxY div 2 - (Count - 1) * H div 2 + i * H - Y div 2,
GetMaxX div 2 + X div 2,
GetMaxY div 2 - (Count - 1) * H div 2 + i * H + Y div 2);
case ch of
#0: case ReadKey of
#72: begin
Dec(i);
if i < 0 then
i := Count - 1;
end;
#80: begin
Inc(i);
if i >= Count then
i := 0;
end;
end;
end;
until ch = #13;
Menu := i;
end;


Примитивнее не могу.

Пример вызова такой процедуры:
Choise := Menu('New'#13'Open'#13'Save'#13'Exit');
Возвращает номер выбранного пункта, причём нумерация от нуля (New - это 0, а Exit - это 3)

То есть параметр - одна строчка, которая содержит все пункты меню, разделённые #13.
Количество пунктов - не более 15, каждый пункт не длинее 100 символов.
Если перед вызовом написать SetTextStyle(4, 0, 7); то выглядит интересно...

Правда, на самом деле это функция, а не процедура. Мое меню состоит всего из двух пунктов: "Play" и "Exit". Основное его предназначение - предоставление игроку выбора сыграть еще раз или выйти при проигрыше. Раньше при проигрыше нельзя было сыграть еще раз. Изменился текст основной программы:
begin
clrscr;
Gd:=VGA;
Gm:=VGAhi;
initgraph(Gd,Gm,'');
if graphresult=grok then
begin
choice:=Menu('Play'#13'Exit');
while (choice=0) do
begin
bereg;
setcolor(brown);
setfillstyle(1,brown);
posX:=500;
posY:=250;
fillellipse(posX,posY,2,2);
repeat
if keypressed then
begin
ch:=readkey;
case ch of
#72:dir:=1;
#75:dir:=2;
#77:dir:=3;
#80:dir:=4;
end; {case ch}
end;
restore_background;
moveship(dir);
draw_ship;
delay(900);
if (getpixel(posX+5,posY)=14) or (getpixel(posX-5,posY)=14) or
(getpixel(posX,posY+5)=14) or (getpixel(posX,posY-5)=14) then
begin
i:=1;
for i:=1 to 8 do
begin
drawcollision;
end;{for}
ch:=#27;
end;{if..then}
until ch=#27;
choice:=Menu('Play'#13'Exit');
dir:=0;
ch:=' ';
end;
closegraph;
halt;
end
else
writeln(grapherrormsg(graphresult));
end.

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

Сообщение отредактировано: Lazzy -


Прикрепленные файлы
Прикрепленный файл  Motor_boat.zip ( 20.76 килобайт ) Кол-во скачиваний: 231
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #14


Гость






Программа была мною еще доработана - появилась анимация крушения и анимация поворота. Однако при доработке не удалось сохранить первоначальную стройную структуру, предложенную andriano. Видимо, эта проблема вызвана отсутствием навыка в построении блок-схем. Надеюсь услышать коструктивную критику
program MAPA3M;
uses crt, graph;
var
Gd,Gm:integer;
posX,posY,i,k,n:integer;
ch:char;
dir,ldir,choice:integer;
m:array [1..6] of pointtype;
const z=10;{zaderzgka}
function checkcollision(posX,posY:integer):boolean;
begin
checkcollision:=false;
if (getpixel(posX+8,posY+3)=darkgray) or (getpixel(posX-8,posY-3)=darkgray) or
(getpixel(posX+3,posY+8)=darkgray) or (getpixel(posX-3,posY-8)=darkgray) or
(getpixel(posX+8,posY-3)=darkgray) or (getpixel(posX-8,posY+3)=darkgray) or
(getpixel(posX-3,posY+8)=darkgray) or (getpixel(posX+3,posY-8)=darkgray)
then
checkcollision := true;
end;

function Menu(Params: string): integer;
var
Count: integer;
Strings: array [0 .. 15] of string [127];
i: integer;
Ch: char;
X, Y, H: integer;
begin
Count := 1;
Strings[0] := '';
for i := 1 to Length(Params) do begin
if Params[i] = #13 then begin
Inc(Count);
Strings[Count - 1] := '';
end else begin
Strings[Count - 1] := Strings[Count - 1] + Params[i];
end;
end;
SetTextJustify(CenterText, CenterText);
H := TextHeight('A') + 10;
ClearDevice;
SetColor(WHITE);
for i := 0 to Count - 1 do
OutTextXY(GetMaxX div 2,
GetMaxY div 2 - (Count - 1) * H div 2 - H div 4 + i * H, Strings[i]);
i := 0;
repeat
SetColor(GREEN);
X := TextWidth(Strings[i]) + 10;
Y := TextHeight(Strings[i]) + 10;
Rectangle(GetMaxX div 2 - X div 2,
GetMaxY div 2 - (Count - 1) * H div 2 + i * H - Y div 2,
GetMaxX div 2 + X div 2,
GetMaxY div 2 - (Count - 1) * H div 2 + i * H + Y div 2);
ch := readkey;
SetColor(BLACK);
X := TextWidth(Strings[i]) + 10;
Y := TextHeight(Strings[i]) + 10;
Rectangle(GetMaxX div 2 - X div 2,
GetMaxY div 2 - (Count - 1) * H div 2 + i * H - Y div 2,
GetMaxX div 2 + X div 2,
GetMaxY div 2 - (Count - 1) * H div 2 + i * H + Y div 2);
case ch of
#0: case ReadKey of
#72: begin Dec(i);
if i < 0 then
i := Count - 1;
end;
#80: begin
Inc(i);
if i >= Count then
i := 0;
end;
end;
end;
until ch = #13;
Menu := i;
end;

procedure delay(Wait:word); assembler;
asm
mov ax,Wait
mov dx,1000
mul dx
mov cx,dx
mov dx,ax
mov ah,$86
int $15
end;


procedure
bereg;
begin
cleardevice;
setcolor(darkgray);
setfillstyle(2,darkgray);
{sector(280,200,0,360,100,50);}
sector(360,200,0,360,80,80);
sector(280,280,0,360,120,70);
sector(360,280,0,360,60,75);
sector(15,450,0,360,55,80);
sector(260,460,0,360,60,30);
sector(600,470,0,360,150,30);
sector(15,410,0,360,20,80);
sector(30,20,0,360,40,25);
sector(500,0,0,360,90,50);
sector(600,15,0,360,80,50);
sector(630,230,0,130,50,100);
sector(610,160,0,120,50,120);
sector(610,450,0,110,75,100);
sector(650,350,0,220,60,100);
sector(140,15,0,200,80,30);
sector(200,20,0,360,100,50);
sector(60,150,60,220,150,200);
sector(150,30,0,360,100,100);
sector(5,240,30,330,40,90);
sector(630,240,0,360,70,100);
sector(360,5,0,360,100,30);
sector(630,240,110,300,70,100);
sector(0,280,0,360,80,100);
sector(100,470,360,360,120,60);
pieslice(380,420,200,340,100);
end;
procedure
restore_background(dir:integer);
begin

if dir=0 then
begin
setcolor(brown);
setfillstyle(1,brown);
fillellipse(posX,posY,8,3);
end;
if (dir=2) or (dir=3) then
begin
setcolor(blue);
setfillstyle(0,blue);
fillellipse(posX,posY,14,4);
end;
if (dir=1) or (dir=4) then
begin
setcolor(blue);
setfillstyle(1,blue);
fillellipse(posX,posY,4,14);
end

end;
procedure
moveship(dir:integer);
begin
case dir of
1: posY:=posY-5;
2: posX:=posX-5;
3: posX:=posX+5;
4: posY:=posY+5;
end;{case}
end;{procedure}
procedure drawcollision(dir:integer);
var a,z:integer;
begin
if (dir=1) then
begin
setcolor(blue);
setfillstyle(0,1);
fillellipse(posX,posY+13,4,4);
for z:=0 to 8 do
begin
for a:=0 to 6 do
begin
putpixel(posX+3-a,posY+8-z,blue);
delay(z*500);
end;
end;
end;
if (dir=2) then
begin
setcolor(blue);
setfillstyle(0,1);
fillellipse(posX+13,posY,4,4);
for z:=0 to 8 do
begin
for a:=0 to 6 do
begin
putpixel(posX+8-z,posY+3-a,blue);
delay(z*500);
end;
end;
end;
if (dir=3) then
begin
setcolor(blue);
setfillstyle(0,0);
fillellipse(posX-13,posY,4,4);
for z:=0 to 8 do
begin
for a:=0 to 6 do
begin
putpixel(posX-8+z,posY-3+a,blue);
delay(z*500);
end;
end;
end;
if (dir=4) then
begin
setcolor(blue);
setfillstyle(0,0);
fillellipse(posX,posY-13,4,4);
for z:=0 to 8 do
begin
for a:=0 to 6 do
begin
putpixel(posX-3+a,posY-8+z,blue);
delay(z*500);
end;
end;
end;
end;


procedure draw_ship(dir:integer);
begin
setcolor(brown);
setfillstyle(1,brown);
if (dir=1) then
begin
fillellipse(posX,posY,3,8);
setfillstyle(11,lightgray);
randomize;
setcolor(blue);
fillellipse(posX,posY+10+random(2),3,2);


end;
if (dir=2) then
begin
fillellipse(posX,posY,8,3);
setfillstyle(11,lightgray);
randomize;
setcolor(blue);
fillellipse(posX+10+random(2),posY,2,3);
end;
if (dir=3) then
begin
fillellipse(posX,posY,8,3);
setfillstyle(11,lightgray);
randomize;
setcolor(blue);
fillellipse(posX-10+random(2),posY,2,3);
end;
if (dir=4) then
begin
fillellipse(posX,posY,3,8);
setfillstyle(11,lightgray);
randomize;
setcolor(blue);
fillellipse(posX,posY-10+random(2),3,2);
end;
end;


procedure changedirhv;
begin
for k := 0 to 6 do with m[k] do
begin
x:=trunc(posX+((8-n)*cos(k*1.05)));
y:=trunc(posY-((3+n)*sin(k*1.05)));
end;
setcolor(blue);
setfillstyle(0,blue);
fillellipse(posX,posY,12,12);
setcolor(brown);
setfillstyle(1,brown);
fillpoly(6,m);
delay(z*1900);
end;
procedure changedirvh;
begin
setcolor(brown);
setfillstyle(1,brown);
for k := 0 to 6 do with m[k] do
begin
x:=trunc(posX+((3+n)*cos(k*1.05)));
y:=trunc(posY-((8-n)*sin(k*1.05)));
end;
setcolor(blue);
setfillstyle(0,blue);
fillellipse(posX,posY,12,12);
setcolor(brown);
setfillstyle(1,brown);
fillpoly(6,m);
delay(z*1900);
end;

procedure changedir(dir,ldir:integer);
begin
for n:=0 to 5 do
begin
if ((ldir=2)or(ldir=3)or(ldir=0))and((dir=1)or(dir=4)) then
begin
changedirhv;
moveship(dir);
end;
if ((ldir=1)or(ldir=4))and((dir=2)or(dir=3)) then
begin
changedirvh;
moveship(dir);
end;
end;
if ((ldir=2)and(dir=3))or((ldir=3)and(dir=2)) or
((ldir=1)and(dir=4))or((ldir=4)and(dir=1)) then
delay(z*1900);
end;
{*******************************main*************************************}
begin
clrscr;
Gd:=VGA;
Gm:=VGAhi;
initgraph(Gd,Gm,'');
if graphresult=grok then
begin
choice:=Menu('Play'#13'Exit');
while (choice=0) do
begin
setbkcolor(blue);
cleardevice;
bereg;
posX:=500;
posY:=250;
{draw_ship(2);}
repeat
if keypressed then
begin
ch:=readkey;
case ch of
#72:begin ldir:=dir; dir:=1; changedir(dir,ldir); end;
#75:begin ldir:=dir; dir:=2; changedir(dir,ldir); end;
#77:begin ldir:=dir; dir:=3; changedir(dir,ldir); end;
#80:begin ldir:=dir; dir:=4; changedir(dir,ldir); end;
end; {case ch}
end;
restore_background(dir);
moveship(dir);
draw_ship(dir);
delay(z*1000);
if checkcollision(posX,posY)=true then
begin
drawcollision(dir);
ch:=#27;
setbkcolor(black);
cleardevice;
end;{if..then}
until ch=#27;
choice:=Menu('Play'#13'Exit');
dir:=0;
ch:=' ';
end;
closegraph;
halt;
end
else
writeln(grapherrormsg(graphresult));
end.

Как видно, я вставил ассемблерную вставку для того, чтобы программа работала с одинаковой задержкой на разных машинах, но корабль стал плавать слишком быстро, а при попытке увеличить параметр Delay выдается ошибка "Constant out of range", т. е. эта вставка не подходит, помогите пожалуйста сделать лучше.
Еще возник вопрос: как осуществить движение корабля под разными углами, чтоб он при нажатии клавиши постепенно менял направление движения? С помощью FillEllipse нарисовать такой корабль не получится, видимо, нужно делать с помощью FillPoly, желательно с большим каоличеством вершин. Но главное - неясно, как задать перемещение, т. е. смену координат.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #15





Группа: Пользователи
Сообщений: 9
Пол: Мужской
Реальное имя: тьома

Репутация: -  0  +


Предыдущее сообщение мое.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #16


поиск
****

Группа: Пользователи
Сообщений: 347
Пол: Мужской
Реальное имя: nir

Репутация: -  2  +


Поставь 2 задержки подряд...


--------------------
typedef void Śūnyatā ;
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #17


Гуру
*****

Группа: Пользователи
Сообщений: 1 168
Пол: Мужской
Реальное имя: Сергей Андрианов

Репутация: -  28  +


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

2. Почему проблемы с delay, честно говоря, не понимаю. В том виде, как она записана, она позволяет делать задержку до 65.5 сек, чего, думаю, более, чем достаточно. Если этого по каим-то причинам мало, то можно сделать так:
procedure delay(Wait:longint);
var l : longint;
begin
l := wait*1000;
asm
mov cx,word ptr [l+2]
mov dx,word ptr [l]
mov ah,$86
int $15
end;
end;


3. Изменение координат - в полном соответствии со школьными курсами физики и геометрии:
dx := V*dt*cos(alpha);
dy := V*dt*sin(alpha);
естественно, скорости, координаты и углы - величины вещественные, а при рисовании координаты округляются до целых.
Полигон, образующий корабль, думаю, лучше всего вращать при помощи матрицы поворота. Это аналитическая геометрия за 1-й курс или любой FAQ по 3D-графике, только надо упростить до двумерного случая.

 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #18





Группа: Пользователи
Сообщений: 9
Пол: Мужской
Реальное имя: тьома

Репутация: -  0  +


Вот что получилось - корабль нарисован из круга, прямоугольника и треугольника. Анимация реализована максимально просто - экран каждый раз очищается полностью. Константы s, v, c позволяют менять соответственно размер, скорость и цвет кораблика.
Код:
uses crt,graph;
var
Gd,Gm:integer;
x,y,dx,dy,ds,alpha:real;
ch:char;
const
s=10; {size}
v=3; {speed}
c=white; {colour}
procedure kvad(x,y:integer;a:real);
var s1,s2,s3:real;
begin
s1:=s/2;
s2:=s*(1+sqrt(3))/2;
s3:=s2/3;
line(round(x-s1*cos(a)-s1*sin(a)),round(y+s1*sin(a)-s1*cos(a))
,round(x-sin(a)*s2),round(y-cos(a)*s2));
line(round(x-sin(a)*s2),round(y-cos(a)*s2),
round(x+s1*cos(a)-s1*sin(a)),round(y-s1*sin(a)-s1*cos(a)));

line(round(x-s1*cos(a)+s1*sin(a)),round(y+s1*sin(a)+s1*cos(a)),
round(x-s1*cos(a)-s1*sin(a)),round(y+s1*sin(a)-s1*cos(a)));

line(round(x+s1*cos(a)+s1*sin(a)),round(y-s1*sin(a)+s1*cos(a)),
round(x+s1*cos(a)-s1*sin(a)),round(y-s1*sin(a)-s1*cos(a)));

circle(round(x+s1*sin(a)),round(y+s1*cos(a)),
round(s1));
floodfill(round(x+s1*sin(a)),round(y+s1*cos(a)),c);
floodfill(round(x-sin(a)*s3),round(y-cos(a)*s3),c);
end;
{********main*******}
begin
ClrScr;
Gd:=detect;
InitGraph(Gd,Gm,'');
if GraphResult = GrOk then
begin
x:=GetMaxX div 2;
y:=GetMaxY div 2;
ds:=1;
alpha:=0;
SetBkColor(Blue);
ClearDevice;
repeat
if KeyPressed then
begin
ch:=readkey;
case ch of
#77: alpha:=alpha-0.1;
#75: alpha:=alpha+0.1;
#27: begin CloseGraph; halt; end;
end; {case}
end;
{SetColor(Blue);
SetFillStyle(1,blue);
kvad(round(x),round(y),alpha-pi/2);}
dx:=ds*cos(alpha); dy:=ds*sin(alpha);
x:=x+dx;
y:=y-dy;
ClearDevice;
SetColor©;
SetFillStyle(1,c);
kvad(round(x),round(y),alpha-pi/2);
Delay(round(10/v)*200);
until ch = #27;
end
else WriteLn(GraphErrorMsg(GraphResult));
end.

Спасибо еще раз!

Сообщение отредактировано: Lazzy -
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 

 Ответить  Открыть новую тему 
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 





- Текстовая версия 24.11.2020 0:26
500Gb HDD, 6Gb RAM, 2 Cores, 7 EUR в месяц — такие хостинги правда бывают
Связь с администрацией: bu_gen в домене octagram.name