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

2 страниц V  1 2 >  
 Ответить  Открыть новую тему 
> Шарики, ООП
сообщение
Сообщение #1


Гуру
*****

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

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


Тема зародилась Задачник по ООП, а это ее продолжения.
Вот структура того, что я написал (измененная)

1 TGObject -// движущейся графический объект, умеет:
// - инитиализировать себя
constructor init(speed,color:byte; angle,time:real);
// - двигаться
procedure moveto;
//- высчитывать свое новое положение, перекрывается в наследниках
procedure calculation; virtual; //abstract;
//- вызывается с Supervisor, только в случае столкновения.
// Меняем скорость и угол полета.
procedure ChangeDirection(speed:byte; angle:real);
// рисуем себя, перекрывается в наследниках
procedure show; virtual; // abstract;
// стираем себя, перекрывается в наследниках
procedure hide; virtual; // abstract;
// стирает себя с экрана
destructor done; virtual;

2 TBall - наследник TGObject, теперь это движущейся шарик
// - добавили новое поле r -радиус, инициализацию остальных полей наследуем
constructor init(x,y:integer; speed,color:byte; angle,time:real; r:byte);
procedure moveto;// перекрываем, наслудуем
procedure calculation; virtual;// перекрываем
procedure show; virtual; // тоже перекрываем
procedure hide; virtual; // тоже перекрываем
destructor done; virtual; // перекрываем, наслудуем


3 TItem - элемент списка указателей на объекты TBall
// инициализирует свою информационную часть
constructor constructor init(Info:TPGObject; Sled:TPItem);
destructor done;// удаляем информационную часть

4 TList - содержит список указателей на объеты типа TBall
constructor init;//-инициализирует список
function AddItem(Data:TPGObject):boolean; // добавляем новый элемент
function DeleteItem(pdel:TPItem):boolean; // удаляем элемент
destructor done;//удаляем весь список


Исходники в виде модулей для FPC - Прикрепленный файл  Balls.rar ( 15.06 килобайт ) Кол-во скачиваний: 542



Возникли затруднения в написания модуля TSupervisor, а именно с главным циклом и наследием этого объекта.
Вот, что я написал

uses objects,graphl,UnitTGObject,UnitList;
const
background=black;
ballcolor=red;


{------------------------TSupervisor------------------}
type
TPSuperVisor=^TSuperVisor;
TSuperVisor=object
GObjectsList:TList;
Rect:TRect;
constructor init(xa,ya,xb,yb:integer);
function AddGObject(p:TPGObject):boolean;
function DeleteGObject(p:TPGObject):boolean;
function ChangeDirection;
function Calculation;
function WriteResults;
procedure main;
destructor done;
end;
constructor TSuperVisor.init;
begin
GObjectsList.init;
Rect.assign(xa,ya,xb,yb);
SetColor(white);
Rectangle(xa,ya,xb,yb);
end;

function TSuperVisor.AddGObject(p:TPGObject);
begin
AddGObject:=GObjectsList.AddItem(p);
end;

function TSuperVisor.DeleteGObject(p:TPGObject):boolean;
begin
DeleteTPGObject:=GObjectsList.DeleteItem(p);
end;

procedure TSuperVisor.main;
begin
end;

destructor TSuperVisor.done;
begin
GObjectsList.done;
ClearDevice;
end;



--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #2


Гость






Так... Ну, у меня после прочтения программы тоже шарики... В глазах бегают только... wacko.gif

Погоди до завтра, надо на свежую голову посмотреть... Что сразу в глаза бросилось - это реализация List-а... Я помню, что ты просил выложить мою версию, но теперь - no1.gif Будем твою дорабатывать, пока она станет функционально одинаковой с моей...
 К началу страницы 
+ Ответить 
сообщение
Сообщение #3


Гуру
*****

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

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


Цитата
Так... Ну, у меня после прочтения программы тоже шарики... В глазах бегают только...

respect.gif ROFL.gif lol.gif
Цитата
Погоди до завтра, надо на свежую голову посмотреть... Что сразу в глаза бросилось - это реализация List-а... Я помню, что ты просил выложить мою версию, но теперь - no1.gif Будем твою дорабатывать, пока она станет функционально одинаковой с моей...

Спасибо! Буду ждать...


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #4


Гость






smile.gif Значит, так... По порядку:

С такой реализацией списка тоже можно жить... Неудобно, правда, но ничего - это FPC, в конце концов - перегрузишь операторы - будет смотреться получше... Теперь о том, какие огрехи и в каком порядке у тебя были замечены:
  1. function TSuperVisor.DeleteGObject(p:TPGObject):boolean;
    begin
    // DeleteGObject:=GObjectsList.DeleteItem(p);
    end;
    - несоответствие типов, копаться, чтобы исправить это я не стал, просто закомментировал вызов процедуры, т.к. не совсем понятно, почему при добавлении к списку функция AddItem получает указатель типа TPGObject, а при удалении DeleteItem требует указатель типа TPItem... blink.gif
  2. Довольно опасно - потому, что проблема не синтаксическая, а логическая - ошибку придется искать отладкой:
    constructor TBall.init(x,y:integer; speed,color:byte; angle,time:real; r:byte);
    begin
    inherited init(speed,color,angle,time);
    pos_x:=x;
    pos_y:=y;
    r:=radius; // <--- Здесь !!! Надо - наоборот: radius := r
    show;
    oldx:=pos_x;
    oldy:=pos_y;
    end;
    В твоем варианте привело к тому, что для всех создаваемых объектов - шаров радиус оставался нулевым => ничего не отчерчивалось, хотя и должно было бы...
  3. Объясни смысл введения переменных oldx, oldy, если ты все равно сначала скрываешь объект, потом его пересчитываешь, и уже с новыми координатами заново перерисовываешь? Я заменил вот эту процедуру:
    procedure TBall.hide;
    begin
    bufcolor:=GetColor;
    SetColor(GetBkColor);
    // Circle(oldx,oldy,radius);
    Circle(pos_x,pos_y,radius);
    SetColor(bufcolor);
    end;
    - теперь она работает...

    + к этому - еще кое что... Зачем ты делаешь bufcolor членом объекта? Памяти много доступной? Это тебе только кажется - ее скоро не будет хватать... Ты переменной bufcolor пользуешься локально - для временного сохранения текущего цвета отрисовки, так? Шарик твой что-то выигрывает от того, что знает, какой текущий цвет установлен? Нет... Тогда зачем ему лишняя информация? Убирай это из объекта...
  4. О скорости выполнения ты тоже, как я вижу, особенно не задумываешься, сколько у тебя будет шариков максимально? 5? 10? А если понадобится имитировать "броуновское движение" и счет пойдет на сотни? Зачем просто так занимать ресурсы и время никому не нужной работой? Я про это:
      procedure TBall.calculation;
    begin
    // Здесь ты все время переводишь градусы в радианы
    pos_x:=pos_x+round(cos(degtorad(l))*v*t);
    pos_y:=pos_y+round(sin(degtorad(l))*v*t);
    l:=ArcTanDeg(pos_y,pos_x);
    end;

    +
      function TBall.ArcTanDeg(x,y:real):real;
    begin
    // А здесь - наоборот, радианы в градусы ???
    ArcTanDeg:=radtodeg(arctan2(x,y));
    end;

    Проще (и быстрее, кстати) сразу решить, что все углы хранятся в радианах (раз уж тригонометрические функции Паскаля работают именно с радианами)... Я понимаю, что пользователю как раз удобнее задавать угол в градусах, НО ведь есть конструктор!!! Пускай он возьмет на себя эту работу:
      // Получать от вызывающего класса угол в градусах ...
    constructor TGObject.init(speed,color:byte; angle,time:real);
    begin
    v:=speed;
    col:=color;
    // Здесь - сразу переводить в радианы, и работать только в радианах
    l:=angle;
    t:=time;
    end;
    Преимущество: это делается только при инициализации класса, а следовательно - только однажды, причем, функцию перевода Grad -> Rad можно заменить константой Pi / 180
Вот та основная программа, которая у меня заработала (шарики корректно двигаются, естественно, ни о каких коллизиях - ни об отталкиваниях от стенок, ни об ударениях друг об друга - пока нет речи, это добавится позднее...)

uses
objects,crt, graph,
ball, UnitTGObject, UnitItem, UnitList;
const
background=black;
ballcolor=red;


{------------------------TSupervisor------------------}
type
TPSuperVisor=^TSuperVisor;
TSuperVisor=object
GObjectsList:TList;
Rect:TRect;
constructor init(xa,ya,xb,yb:integer);
function AddGObject(p:TPGObject):boolean;
function DeleteGObject(p:TPGObject):boolean;
function ChangeDirection: boolean;
function Calculation: boolean;
function WriteResults: boolean;
procedure main;
destructor done;
end;
constructor TSuperVisor.init;
var i: integer;
begin
GObjectsList.init;
Rect.assign(xa,ya,xb,yb);
SetColor(white);
Rectangle(xa,ya,xb,yb);

for i := 1 to 10 do
AddGObject(new(tpball, init(random(getmaxx - 20) + 10,
random(getmaxy - 20) + 10,
2, red, random(360), 1, 5)));
end;

function TSuperVisor.AddGObject(p:TPGObject);
begin
AddGObject:=GObjectsList.AddItem(p);
end;

function TSuperVisor.DeleteGObject(p:TPGObject):boolean;
begin
// DeleteGObject:=GObjectsList.DeleteItem(p);
end;

procedure TSuperVisor.main;
var p: TPItem;
begin

repeat

delay(10);
p := GObjectsList.List;
while p <> nil do begin
p^.data^.moveto;
p := p^.next;
end;

// При нажатии на Esc - выходим из цикла
if keypressed and (readkey = #27) then break
else
while keypressed do readkey;

until false;
end;

// Это - пока пустые функции, похоже, Calculation здесь вообще не нужна -
// вычислениями будет заниматься конкретный объект, а не Наблюдатель
function TSuperVisor.ChangeDirection: boolean;
begin end;
function TSuperVisor.Calculation: boolean;
begin end;
function TSuperVisor.WriteResults: boolean;
begin end;


destructor TSuperVisor.done;
begin
GObjectsList.done;
ClearDevice;
end;


var
grdriver, grmode, errcode: smallint;
sv: TSuperVisor;

begin
grDriver := d8bit; grMode := m800x600;
InitGraph(grDriver, grMode, '');
ErrCode := GraphResult;
if ErrCode <> grOk then begin
Writeln('Graphics error:', GraphErrorMsg(ErrCode));
writeln('Press Enter to halt()'); readln; halt(100);
end;


SV.init(1, 1, getmaxx, getmaxy);
SV.main;
SV.done;
end.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #5


Гуру
*****

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

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


Спасибо! smile.gif

Учел все замечания, исправил, извиняюсь что сам не отладил некоторые. unsure.gif

Отказ от ненужных переменных oldx,oldy избавил от надобности переопределять метод moveto в объекте TBall.
Также поставил в основной цикл отрисовку границ поля - без этого они затираются шариками.
И чуточку изменил деструктор TSuperVisor-а - вместо ClearDevice используем такой способ:

destructor TSuperVisor.done;
var bufcolor:byte;
begin
GObjectsList.done;
bufcolor:=GetColor;
SetColor(GetBkColor);
rectangle(rect.a.x,rect.a.y,rect.b.x,rect.b.y);;
SetColor(bufcolor);
end;

Измененные модули вмести с SuperVisor - Прикрепленный файл  Ball.rar ( 15.36 килобайт ) Кол-во скачиваний: 472


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

Что ты имеешь ввиду? Может вместо списков использовать коллекции?

Цитата
несоответствие типов, копаться, чтобы исправить это я не стал, просто закомментировал вызов процедуры
Да, надо изменить на
function TSuperVisor.DeleteGObject(p:TPItem):boolean;

Цитата
т.к. не совсем понятно, почему при добавлении к списку функция AddItem получает указатель типа TPGObject, а при удалении DeleteItem требует указатель типа TPItem...

Для инициализации звена списка, объект UnitItem должен знать свою информационную часть, и по-этому мы передаем ему переменную типа TPGObject, но потом, при работе с UnitList-ом для удаления своего элемента все, что он должен - это указатель на звено, а не информационную часть (ведь таких звеньев может быть несколько). Вот почему в DeleteGObject как передаваемый параметр я использую TPItem.

Цитата
/// Это - пока пустые функции, похоже, Calculation здесь вообще не нужна -
// вычислениями будет заниматься конкретный объект, а не Наблюдатель

Я тоже согласен оставить эти методы абстрактными в TSuperVisor и переопределять их в наследниках.
По-поводу Calculation не совсем представляю как это будет. Ты хочешь передавать в TGObject точку, где он столкнулся, а все остальное пусть высчитывает сам?


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #6


Гость






Цитата
Я тоже согласен оставить эти методы абстрактными в TSuperVisor и переопределять их в наследниках.
А вот с этого места - поподробнее... Что это будут за наследники от TSuperVisor? СуперНаблюдатели? НаблюдателиСПравомВмешатьсяИПредотвратит
ьСтолкновение? Что ты вкладываешь в смысл "унаследоваться от объекта" в этом случае? Какие это тебе даст преимущества? Шарики будут так же кататься? Сталкиваться? Отскакивать друг от друга? Это все будет работать и с этим же Наблюдателем, без наследников...

Понимаешь в чем дело, ведь просто так наследование - оно не панацея... Его надо применять тогда, когда это необходимо, а не потому, что оно есть в языке... Вот я и прошу тебя объяснить, ЧЕМ программа с ПЕРЕопределенным Наблюдателем будет отличаться от программы, где методы TSuperVisor будут доработаны?

Цитата
Может вместо списков использовать коллекции?
Аналогично, какие ты в этом видишь преимущества? Что есть такое в коллекциях, чего нет (и нельзя сделать, заметь!!!) в списке? Только то, что там ЭТО уже готово, а для TList придется сделать - это для меня не аргумент, тем более в целях обучения... Я, конечно, против изобретения велосипедов, но вот тут как раз - не тот случай... Смотри: есть у меня коллекция. Что я могу с ней сделать (на примере TCollection)? Добавить элемент в коллекцию, найти его в коллекции, удалить оттуда, правда? А со списком все гораздо интереснее... Я же могу переопределить TList на TPriorityList, скажем, и добавлять в список объекты, согласно их приоритету... Например, для того, чтобы обработка квадратов была предпочтительнее обработки треугольников в 3 раза. И, заметь, я это полностью буду контролировать, ибо ЭТО - написано мной, а не кем-то, возможно, даже, лучше меня программирующим, НО не знающим специфики моего приложения... Мне, например, может быть критична скорость операции добавления в список, а по какому критерию оптимизированы коллекции, ты можешь мне гарантировать? smile.gif Я, наконец, список элементарно заменю на дерево, если надо будет... Так что ТАКИЕ вещи я предпочитаю делать сам; если ты хочешь пользоваться готовым - дело твое, только потом НЕ говори, что я тебя НЕ предупреждал, когда тебе станет не хватать скорости обработки, или емкости коллекции (ну, это - вряд ли, они достаточно вместительны), или гибкости ее... (Если тебя прельстил метод ForEach, то это тоже преодолимо, как ты помнишь...)

Цитата
Ты хочешь передавать в TGObject точку, где он столкнулся, а все остальное пусть высчитывает сам?
Да я бы вообще ничего не передавал... Чего, объект своего положения не знает? Зачем еще ему что-то передавать? Надо просто вызвать процедуру обработки столкновения двух объектов (причем она совершенно не обязательно будет методом TSuperVisor, я бы как раз ее сделал посторонней процедурой), а там уже пускай в зависимости от формы и свойств столкнувшихся объектов она разруливает, какой процент энергии будет потерян, и какой объект получит импульс под каким углом (и как именно она это будет делать - тоже никоим образом Наблюдателя не касается - это не его проблема, он Наблюдатель!!! Зафиксировал факт столкновения - сообщил, куда следует. Все, спасибо, продолжай наблюдать)... А дальше - прямая обязанность объекта - пересчитать свои координаты с учетом новых данных...
 К началу страницы 
+ Ответить 
сообщение
Сообщение #7


Гуру
*****

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

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


Цитата
А вот с этого места - поподробнее... Что это будут за наследники от TSuperVisor? СуперНаблюдатели?
Вот я и прошу тебя объяснить, ЧЕМ программа с ПЕРЕопределенным Наблюдателем будет отличаться от программы, где методы TSuperVisor будут доработаны?

lol.gif
Думал наследием воспользоваться как инструментом поддержки новых типов наследников TGObject... но по твоему способу организации нужда в нем действительно отпадает.
Цитата
Если тебя прельстил метод ForEach, то это тоже преодолимо, как ты помнишь...

yes2.gif
Все, забыл, я даже такого слова никогда не слышал... smile.gif Оставляем списки, я только за.
Цитата
Надо просто вызвать процедуру обработки столкновения двух объектов (причем она совершенно не обязательно будет методом TSuperVisor, я бы как раз ее сделал посторонней процедурой), а там уже пускай в зависимости от формы и свойств столкнувшихся объектов она разруливает, какой процент энергии будет потерян, и какой объект получит импульс под каким углом (и как именно она это будет делать - тоже никоим образом Наблюдателя не касается - это не его проблема, он Наблюдатель!!! З

yes2.gif
С появлением все новых наследников TGObject-а TSuperVisor должен запускать все новые и новые процедуры (именно о них я писал в теме "Задачник по ООП", только хотел их всунуть в TSuperVisor), а для этого нам надо как-то дать знать TSuperVisor-у какие процедуры запускать в зависимости от столкнувшихся объектов. Может и бред, но мне на ум приходит сделать еще один список в TSuperVisor , который будет хранить записи типа

record
typeof1:pointer;//тип первого объекта
typeof2:pointer;//тип второго объекта
proc:@procedure; //процедура обработки
end;

и при появлении новых фигур просто добавлять в этот список новые процедуры.


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #8


Гость






Цитата
С появлением все новых наследников TGObject-а TSuperVisor должен запускать все новые и новые процедуры
no1.gif Совершенно не обязательно... Хочешь способ, по которому будет вообще ОДНА процедура
Procedure ComputeCollision(Var first_obj, second_obj: TGObject);
? Значится, так: при столкновении считаем, что произошло столкновение двух точечных объектов (точка столкновения, допустим, известна; векторы скоростей обоих объектов - тоже; возможно - даже коэффициенты упругости - если это неприменимо к понятию "точечный объект", заменим его понятием "абстрактный объект" и их массы; что еще надо?) Ты УЖЕ можешь по тем данным, которые я перечислил пересчитать траектории движения, так? Пересчитал, получил новый вектор скорости для first_obj и second_obj, и ... поменял на него старый - один дополнительный метод в классе TGObject...

Пока вижу только одну проблему - само обнаружение столкновения, ибо ту ссылку я так и не могу найти, а все, что находится - очень сложно, и, я боюсь, крайне медленно... Хотя для начала - вполне пойдет и вот такой простой алгоритм: ты в каждый момент времени знаешь, КУДА движется объект, так? Найти ту его точку, которая, скажем так, "движется впереди всех остальных" - то есть, первой вступит в соприкосновение с другим объектом/стенкой (я не говорю сейчас о случае, когда в данный объект врезалИСЬ, рассматривается только случай, что данный объект врезалСЯ в кого-то) сможешь? А проверить, находится ли точка с известными тебе координатами внутри фигуры (т.е., реализовать виртуальную функцию Function IsInside(P: TPoint): Boolean)?

Понимаешь, куда я клоню? Точка "движущаяся первой" - есть, проверяешь, не находится ли она случайно внутри другого объекта, и все, если находится (внутри или на границе) - то есть столкновение!!! Конечно, можно подобрать форму объекта, для которого такой вариант не будет работать, но в большинстве случаев достаточно простых фигур (не забывай, со сложнвми есть проблемы еще и при отрисовке, так что очень усложнять тоже нежелательно), мне кажется, проблем быть не должно... По крайней мере, попробуй это реализовать, даже если это ошибочно, ничего плохого не будет, если ты заставишь этот алгоритм работать хотя бы только на кругах/квадратах...
 К началу страницы 
+ Ответить 
сообщение
Сообщение #9


Гуру
*****

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

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


Цитата
Ты УЖЕ можешь по тем данным, которые я перечислил пересчитать траектории движения, так?

Пересчет идет где? В TGObject?
Цитата
Найти ту его точку, которая, скажем так, "движется впереди всех остальных"

Для квадрата уже будет целая сторона в некоторых случаях..


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #10


Гость






Цитата
Пересчет идет где? В TGObject?
Зачем? Прямо в ComputeCollision - у тебя же "абстрактный объект"

Цитата
Для квадрата уже будет целая сторона в некоторых случаях..
Значит, придется запоминать массив/список "впереди-идущих" точек (если сторона - значит, конечно, не каждая ее точка, а, скажем, 5-10 равноотстоящих точек между началом и концом этой стороны включительно), и проверять каждую из них на попадание в другую фигуру... blum.gif

Собственно, полезные ссылки:
Определение столкновений выпуклых объектов движущихся с постоянными скоростями

Collision Detection and Impulse Dynamics in Real time Applications (это больше для 3D, но все равно интересно)

excl.gif
А вообще - у меня к тебе вопрос: КАКИЕ фигуры ты хочешь вводить в иерархию движущихся объектов? Перечисли ВСЕ фигуры, которые ты себе представляешь... (Лучше всего при ответе на этот вопрос не просто перечислить их, а, хотя бы, привести для каждой фигуры конструктор и методы Show/Hide, понимаешь, о чем я?)
 К началу страницы 
+ Ответить 
сообщение
Сообщение #11


Гуру
*****

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

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


Спасибо! smile.gif

Извиняюсь, что заставил себя ждать, зато отладил некоторые модули и сделал реализацию твоего алгоритма - теперь шарики умеют отталкиваться. smile.gif
Изменения в структуре объектов:

TGObject
// - определяем впереди идущую точку, новая
function GetFrontPoint:TPoint; virtual;//abstract;
// - проверяем является ли P частью TGObject, новая
function IsInside(P:TPoint):boolean; virtual;//abstract;
// - теперь этот метод не содержит параметров
procedure ChangeDirection; virtual;//abstract;
TBall=object(TGObject)
// вместо двух переменных используем одну специализированную
pos:TPoint;
// переопределяем, теперь мы умеем менять направление польота
procedure ChangeDirection; virtual;
// переопределяем, новая
function GetFrontPoint:TPoint; virtual;
// переопределяем, новая
function IsInside(P:TPoint):boolean; virtual;
TSuperVisor=object
// проверяем не вышел ли объект за поля, новая
function IsInField(obj:TPGObject):boolean;
// проверяем столкнулись ли объекты, новая
function IsCollision(first_obj,second_obj:TPGObject):boolean;
// эта процедура обзавелась новым циклом
procedure main;

ChangeDirection, Calculation, WriteResults - абстрактные до этого времени методы я удалил.

Других объектов я не трогал..


Очередная поставка модулей - Прикрепленный файл  Balls.rar ( 17.56 килобайт ) Кол-во скачиваний: 491

Если администрация разрешает - Exe-шник :Прикрепленный файл  SuperVisor.rar ( 77.21 килобайт ) Кол-во скачиваний: 519


Цитата
Значит, придется запоминать массив/список "впереди-идущих" точек (если сторона - значит, конечно, не каждая ее точка, а, скажем, 5-10 равноотстоящих точек между началом и концом этой стороны включительно), и проверять каждую из них на попадание в другую фигуру...

Это я оставил на следующий этап - пока только одна точка sad.gif .

Понаблюдав чуть-чуть за шариками, можно увидеть ситуации, когда они "слипаются" и летят вмести - знак того, что алгоритм имеет недостатки и его надо менять sad.gif . Прочитал ссылку по столкновениям выпуклых объектов, в коде ни черта не разобрал, но идея проекций понравилась. good.gif Наверное так и надо...

Цитата
понимаешь, о чем я?

Ты имеешь ввиду, буду ли я делать вогнутые объекты? Не знаю, пока - нет... smile.gif

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


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #12


Гость






Цитата
Других объектов я не трогал..
Реализация TSupervisor осталась за кадром, ты не прикрепил основной файл, поэтому судить о том, насколько правильно ты реализовал предложенная мной алгоритм, я не могу...
 К началу страницы 
+ Ответить 
сообщение
Сообщение #13


Гуру
*****

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

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


Да я вроде все модули прикреплял blink.gif , вот TSupervisor отдельно:

Прикрепленный файл  SuperVisor.rar ( 1.08 килобайт ) Кол-во скачиваний: 467


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #14


Гость






Цитата(Bokul @ 24.12.2006 2:49)
Понаблюдав чуть-чуть за шариками, можно увидеть ситуации, когда они "слипаются" и летят вмести - знак того, что алгоритм имеет недостатки и его надо менять sad.gif .

no1.gif Понаблюдав за твоим кодом и сделав несколько экспериментов над ним, я могу тебе рассказать следующее:

во-первых, ты опять тратишь лишние вычислительные ресурсы... На этот раз - здесь:
  function TBall.distance(P:TPoint):real;
begin
distance:= sqrt(sqr(p.x-pos.x)+sqr(p.y-pos.y));
end;
Вот расскажи мне, зачем тебе нужен ТУТ корень? Что, недостаточно было возвращать целый квадрат расстояния и сравнивать его с квадратом радиуса? Это ж ускорение какое... Мало того, что операции над целыми выполняются гораздо быстрее, чем над вещественными числами, так еще и "дорогая" Sqrt не нужна, а квадрат радиуса можно вычислять и в конструкторе...

Во-вторых... Попробовав уменьшить число шариков до 2-х я с удивлением обнаружил, что проверка на выход за пределы Rect не всегда корректно срабатывает (даже, если шарики вообще не сталкиваются, а идут параллельными курсами blink.gif )... !smoke2.gif Попробуй, увидишь...

Теперь еще одно... Что ты делаешь... Ты проходишь по списку шариков, и для каждого из них запускаешь еще один проход по списку... Все прекрасно, НО... Ты не боишься, что при проверке
               if not(IsInfield(p^.data)) then
p^.data^.ChangeDirection;
ты вторично изменяешь направление движения какого-нибудь шарика? Вот тут и будет наблюдаться "слипание"...

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

Кстати, чуть не забыл... Абстрактность метода может задаваться директивой Abstract, тогда реализацию (эту самую "пустышку") вообще не надо писАть... yes2.gif

Теперь вроде все...
 К началу страницы 
+ Ответить 
сообщение
Сообщение #15


Гуру
*****

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

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


Цитата
Вот расскажи мне, зачем тебе нужен ТУТ корень? Что, недостаточно было возвращать целый квадрат расстояния и сравнивать его с квадратом радиуса? Это ж ускорение какое... Мало того, что операции над целыми выполняются гораздо быстрее, чем над вещественными числами, так еще и "дорогая" Sqrt не нужна, а квадрат радиуса можно вычислять и в конструкторе...

Да ты - прав, выигрыш видно сразу, особенно когда количество объектов достигает несколько сотен..
Цитата
Во-вторых... Попробовав уменьшить число шариков до 2-х я с удивлением обнаружил, что проверка на выход за пределы Rect не всегда корректно срабатывает (даже, если шарики вообще не сталкиваются, а идут параллельными курсами blink.gif )... !smoke2.gif Попробуй, увидишь...

Что именно? То, что они отбиваются не под правильным углом? Или может они улетают из поля?
PS не знаешь формулы расчета угла после удара? Я вывел свою, ну как видешь она не совсем корректно работает sad.gif ...


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #16


Гость






Цитата
Что именно?
А именно - вот что (эту картинку я наблюдал сам): представь себе 2 шарика, двигающихся параллельно друг другу из правого верхнего в левый нижний угол экрана... НО как только первый шар касается нижней границы и меняет направление (кстати, меняет он направление не по закону "угол падения равен углу отражения", а на противоположное - на 180 градусов), то же самое делает и второй шар (несмотря на то, что он-то еще не дошел до границы по крайней мере 5 своих диаметров)... unsure.gif А почему это происходит - я так и не понял, надо подольше поэкспериментировать...

Кстати, где-то на форуме я как-то выкладывал ссылку на физическую модель соударения упругих шаров (если она еще живая) - попробуй поискать, что-то связанное с упруг* / биллиа* / модель
 К началу страницы 
+ Ответить 
сообщение
Сообщение #17


Гуру
*****

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

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


Цитата
НО как только первый шар касается нижней границы и меняет направление, то же самое делает и второй шар

Не выходит спроектировать тоже самое (хотя, когда много шаров я видел ситуации, как шар менял направления не долетая до стенки), можешь дать данные для двух шаров, когда это происходит?
Цитата
кстати, меняет он направление не по закону "угол падения равен углу отражения", а на противоположное - на 180 градусов

no1.gif Вот процедура (переделанная под функцию для примера), отвечающая за это:
Код

uses math;
function ChangeDirection(l:real):real;
      function mydiv(a,b:real):longint;
      begin
        mydiv:=trunc(a/b);
      end;
      function mymod(a,b:real):real;
      begin
        mymod:=a-mydiv(a,b)*b;

      end;
const l90=pi/2;
begin

  ChangeDirection:=mydiv(l,l90)*l90 + (pi-mymod(l,l90));
end;

begin
  writeln(radtodeg(ChangeDirection(degtorad(120))):0:2);
  readln;
end.

Как видишь она не получает никаких внешних данных, но это не правильно. Почему? Вот Прикрепленное изображение .
Выходит нам надо передавать этой процедуре угол (T) под каким располагается поверхность об какую ударился шарик. И новый угол полёта = T+L где L - угол до соударения.

Но так как метод ChangeDirection один для всех (и для случаев столкновения с другими шарами), то нужно знать под каким углом(P) соударяются объекты, для шариков он будет таковым:
P:=arctan((y2-y1)/(x2-x1))+90
x1,y1- координаты центра (точка А на рисунке) первого шара
x2,y2- координаты центра (точка В на рисунке) второго шара
Прикрепленное изображение
Угол P должен высчитывать TSupervisor, но в зависимости от столкнувшихся объектов, он будет вычисляться по разному. Как предлагаешь TSupervisor-у без наследия определять этот угол для любых возможных наследников TGObject?

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


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #18


Гость






Цитата
Как предлагаешь TSupervisor-у без наследия определять этот угол для любых возможных наследников TGObject?
Ты не забыл, что у каждого объекта есть вектор скорости? И вычисление угла разлета можно производить именно по нему?
 К началу страницы 
+ Ответить 
сообщение
Сообщение #19


Гуру
*****

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

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


Цитата
Ты не забыл, что у каждого объекта есть вектор скорости? И вычисление угла разлета можно производить именно по нему?

yes2.gif
Ты предлагаешь передавать объекту точку столкновения, которая имеет скорость и координаты, а все остальное пусть вычисляет сам? С этим я согласен. Если не понятно, что я хочу сказать, скажи. yes2.gif


--------------------
Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее.
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #20


Гость






Что-то обсуждение зацикливается... Я же написал тебе выше - я бы сделал НЕметод класса CalcCollision который получает два объекта (любых), которые могут сообщить всю информацию о себе: где находится сейчас (координаты центра), куда летит, какой у него коэффициент упругости, скорость, и т.д.

Зная подобную информацию о двух объектах можно с легкостью вычислить параметры движения после столкновения, и передать их объектам... Или ты хочешь, чтобы квадрат с треугольником разлетались по другим законам, нежели два круга?

Кстати, о скорости... Я бы все-таки предпочел работать с
TVector = Record
X, Y: Double;
End;

(т.е. вертикальная/горизонтальная составляющая, а не угол относительно одной из осей...) Тогда, например, отскок от стенки будет тривиальным - смена знака одной из составляющих. Да и возможности FPC одним выражением складывать/умножать/вычитать/масштабировать вектора тоже нельзя не принимать во внимание - это может очень сильно облегчить работу... rolleyes.gif
 К началу страницы 
+ Ответить 

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

 





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