Вот такое задание: Даны два шарика диаметром М. Промоделировать движение шариков по всей плоскости экрана. Шарики могут двигаться по прямым линиям. Изменение угла движения после столкновения с границей экрана или друг с другом может быть случайной величиной. Шарик не может вылететь за пределы квадрата. Пользователь может варьировать скорости движения шариков с помощью стрелок управления курсором.
Хотелось бы разобраться в коде программы... ( дело в том, что код не мой) Объясните пожалуйста назначение каждой из процедур в тексте приведённой ниже программы!
Марина, я покажу пример программы, которая гоняет объекты по полю. Программа работает в текстовой моде и без меню, но в ней есть управление скоростями. Надеюсь, она будет тебе полезна в некотором отношении.. Дело в том, что в ней четко проведено деление: все, что относится к выводу на экран, выделено в отдельный модуль. Сама же основная программа осуществляет движение и управление. По идее, если вместо существующего модуля, работающего с текстом, написать модуль, работающий с графикой, то основную программу менять не нужно (кроме строчки uses). Модуль для графики можно написать по образу и подобию текстового, заменив текстовый вывод на графический (советы volvo тут очень пригодятся).
Попробуй разобраться в том, как все это работает. Используются абстрактные координаты и экранные, что дает гибкость и удобство. В тексте вместо кружочков - квадратики, но это не самое важное . Отражение происходит просто со сменой знака соответствующей составляющей скорости (без случайностей). Начальное положение, скорости и размеры задаются случайно. Названия процедур отражают их суть. Сами объекты называются "It" (оно, нечто ).
Управление скоростями такое: нужно выбрать объект, нажав клавишу с его номером (по умолчанию выбран первый). Стрелки вверх/вниз увеличивают/уменьшают вертикальную скорость выбранного объекта, влево/вправо - горизонтальную. Пробел делает паузу (продолжение любой клавишей), Esc завершает программу. Можкшь также поиграть параметрами в программе - например, NIt (количество объектов), скоростью.. По идее, она работает в окне любого размера.
Я не совсем до конца протестировал на точность перевода, если заметишь сбои - говори.
Вот головная программа:
{Moving It's} {By Lapp} uses CRT,ItsText;
var x1,x2,y1,y2,MaxR,MaxV0:real;
procedure CreateIt(n:integer); begin with It[n] do begin r:=Random*MaxR; rx:=Round(r/(x2-x1)*(MaxX-MinX)); ry:=Round(r/(y2-y1)*(MaxY-MinY)); x:=x1+r+Random*(x2-x1-r*2); y:=y1+r+Random*(y2-y1-r*2); vx:=Random*MaxV0-MaxV0/2; vy:=Random*MaxV0-MaxV0/2; c:=(n+8)mod 15+1 end end;
procedure MoveIt(n:integer); begin with It[n] do begin x:=x+vx; if x<x1+r then begin x:=2*(x1+r)-x; vx:=-vx end; if x>x2-r then begin x:=2*(x2-r)-x; vx:=-vx end; y:=y+vy; if y<y1+r then begin y:=2*(y1+r)-y; vy:=-vy end; if y>y2-r then begin y:=2*(y2-r)-y; vy:=-vy end; xs:=Round((x-x1)/(x2-x1)*(MaxX-MinX))+MinX; ys:=Round((y-y1)/(y2-y1)*(MaxY-MinY))+MinY; end end;
var i,NIt,The:integer; ch:char; GameOver:boolean;
begin NIt:=9; {Number of It's, 1<=NIt<=9} MaxV0:=50; {Maximum speed } MaxR:=50; {Maximum radius}
OpenBoard; x1:=0; x2:=MaxX*10; y1:=0; y2:=Round(MaxY*10*Aspect); Randomize; for i:=1 to NIt do begin CreateIt(i); MoveIt(i) end; GameOver:=false; for i:=1 to NIt do ShowIt(i); The:=1; repeat for i:=1 to NIt do begin MoveIt(i); with It[i] do if (xs<>xs0)or(ys<>ys0) then begin HideIt(i); ShowIt(i) end end; if KeyPressed then begin ch:=ReadKey; case ch of #0: begin ch:=ReadKey; with It[The] do case ch of #72:if vy<100 then vy:=vy*2; #75:if vx>0.01 then vx:=vx*0.5; #77:if vx<100 then vx:=vx*2; #80:if vy>0.01 then vy:=vy*0.5; end end; '1'..'7':The:=Ord(ch)-48; ' ':repeat ReadKey until not KeyPressed; #27:GameOver:=true end end until GameOver; for i:=1 to NIt do HideIt(i); CloseBoard end.
А это - текстовый модуль:
{unit for moving It's} {by Lapp} unit ItsText; interface uses CRT;
const MIt=9; {max number of Iteatures}
type tIt=record x,y,vx,vy,r:real; {position, speed, real radius} rx,ry,c,xs,ys,xs0,ys0:integer; {screen radius, color, screen pos, old screen pos} end;
var It:array[1..MIt]of tIt; NIt:integer; Aspect,MinX,MinY,MaxX,MaxY,x0,y0:integer; TA:byte;
procedure HideIt(n:integer); var i,j:integer; begin with It[n] do begin for j:=ys0-ry to ys0+ry do for i:=xs0-rx to xs0+rx do begin GoToXY(i,j); Write(' ') end; end end;
procedure OpenBoard; begin MinX:=1; MaxX:=WindMax and 255+1; MinY:=1; MaxY:=WindMax shr 8+1; Aspect:=2; x0:=WhereX; y0:=WhereY; TA:=TextAttr end;
procedure CloseBoard; begin GoToXY(x0,y0); TextAttr:=TA end;
begin end.
--------------------
я - ветер, я северный холодный ветер я час расставанья, я год возвращенья домой
за что отвечают переменные x1,x2,y1,y2 и Aspect,MinX (относительно чего определяется максимальные и минимальные координаты?),MinY,MaxX,MaxY,x0,y0 и ТА ?
за что отвечают переменные x1,x2,y1,y2 и Aspect,MinX (относительно чего определяется максимальные и минимальные координаты?),MinY,MaxX,MaxY,x0,y0 и ТА ?
Вот, смотри. x1,x2,y1,y2 - это абстрактные координаты, приписанные окну (границы по Х и по У). Им можно приписать любые значения (есть только ограничения x1<x2 и y1<y2, но и они появляются не сразу, а только при рассмотрении выхода шарика за границу окна). Например, если исходить из реальных размеров на экране, то им можно дать значения в см, типа от 0 до 20 по Х и от 0 до 10 по У. Но если ты представляешь себе окно, как поле космического сражения (в игре), то можно присвоить им значения от 100 до 200 парсеков по Х и от 0 до 50 парсеков по У, например. Если (другой пример) ты используешь окно для построения графика, при этом строишь в нем график синуса, то разумно по Х сделать от -пи до +пи, а по У - от -1 до +1 (или от +1 до -1, поскольку при построении графика не нужно проверять выход за границы окна).
Эти абстрактные координаты мспользуются для осуществления движения шариков. То есть пересчет координат по скорости со времением происходит именно в них. Перевод в реальные координаты экрана (пикселы в графике или символы в тексте) производится только перед выводом (за небольшим исключением, скажу позже). Существуют формулы для перевода, они явно написаны, например, в процедурах ShowIt (ПоказатьЭто) и HideIt (СпрятатьЭто).
Aspect - это параметр, который учитывает "неквадратность" точки. Точка - это маленькое пятнышко. В идеале это квадратик (в ЖК-мониторах так и есть) поверхности, который закрашен в некоторый цвет. В современных мониторах пиксел имеет одинаковые размеры по горизонтали и вертикали (даже если это не квадратик, а пятнышко, как в ЭЛТ-мониторе). Иными словами, сместившись на 100 пикселов вправо и 100 вниз, мы пройдем (примерно) 2.5 см вправо и 2.5 см вниз. Это было не всегда - в старых мониторах типа EGA и CGA эти пути были не равны - но сейчас можно считать, что это так (хотя в некоторых модах все же бывает искажение). Совсем другое дело в текстовой моде.. Пиксел (символ) в ней совсем не квадратный, это прямоугольник. Вот это число, Aspect, как раз и учитывает эту неквадратность. Сейчас объясню, как.
Окно может быть разным - большим, маленьким. (Попробуй менять размер окна. Правда, для этого, пожалуй, нужно использовать FPC.. Кстати, какой компилятор ты используешь?) Я стараюсь сделать абстрактные координаты так, чтоб они как-то соответствовали размерам окна. Поэтому я сначала, в процедуре OpenBoard (ОткрытьПоле) выясняю его размеры (в символах):
MinX:=1; MaxX:=WindMax and 255+1; MinY:=1; MaxY:=WindMax shr 8+1;
Информацию о переменной WindMax смотри в описании модуля CRT. MinX,MinY,MaxX,MaxY - это реальные координаты окна на экране, в пикселах (символах).
После этого я вычисляю абстрактные координаты по реальным, давая по 10 абстрактных единиц на одну ширину пиксела (символа) :
Применение переменной Aspect тут должно учесть, что пиксел наш не квадратный. В моем примере я ему присвоил значение 2, поскольку высота символа примерно вдвое больше ширины (если хочешь, можешь измерить и уточнить это значение).
Что это нам дает? А вот, что. Если скорости объекта (ну, пока скажем, пиксела) по Х и У одинаковые, то без учета Aspect'а, если пиксел смещать за один цикл на одну позицию, то реальная скорость пиксела на экране по У будет вдвое больше, чем по Х. Мы же делаем так: по формулам с учетом скоростей по Х и У мы высчитываем новое положение объекта. Допустим, по Х он смещается на расстояние 1 пкс, и скорости по Х и У равны. Тогда использование Aspect=2 даст то, что по У смещение будет 0.5 пкс, то есть реально объект по У не сместится. На следующем цикле объект сместится по Х еще на 1 пкс, а по У - еще на 0.5 пкс, что в сумме со смещением на предыдущем ходу даст 1 (0.5+0.5=1). На этот раз объект сместится по У на 1 пкс. Мы же увидим на экране ломаную траекторию, но ведущую в правильном направлении (под 45 градусов).
Этот способ - двойные координаты, абстрактные и реальные - очень эффективен в разных ситуациях. Иногда желательно иметь тройные координаты (например, пикселы - экранные_сантиметры - парсеки). Большей глубины вложения я пока не встречал, хотя возможно.
Таким образом, можно реально осуществлять движение объектов во всех направлениях и при этом скорости по Х и У будут соотноситься между собой правильно. Идея понятна? Про реализацию еще можно повыяснять..
Ну, а ТА, x0, y0 - это глупости... В некотором смысле лишнее , но желательное. В эту переменную я запоминаю текущие текстовые атрибуты (цвет символа и цвет фона), чтобы вернуть их по завершении работы программы на место. Я также возвращаю на место курсор (все это в процедуре CloseBoard, ЗакрытьПоле), для запоминания которых я и использую x0 и y0. Если это не сделать, то курсор останется в том месте, где рисовался последний объект, и цвет символов будет такой же, как цвет последнего объекта. Это все тебе в графике не понадобится..
Успехов, и продолжай задавать вопросы.
--------------------
я - ветер, я северный холодный ветер я час расставанья, я год возвращенья домой