![]() |
![]() |
Bokul |
![]()
Сообщение
#1
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Тема зародилась Задачник по ООП, а это ее продолжения.
Вот структура того, что я написал (измененная)
Исходники в виде модулей для FPC - ![]() Возникли затруднения в написания модуля TSupervisor, а именно с главным циклом и наследием этого объекта. Вот, что я написал
-------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
![]() ![]() |
volvo |
![]()
Сообщение
#2
|
Гость ![]() |
Так... Ну, у меня после прочтения программы тоже шарики... В глазах бегают только...
![]() Погоди до завтра, надо на свежую голову посмотреть... Что сразу в глаза бросилось - это реализация List-а... Я помню, что ты просил выложить мою версию, но теперь - ![]() |
Bokul |
![]()
Сообщение
#3
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Цитата Так... Ну, у меня после прочтения программы тоже шарики... В глазах бегают только... ![]() ![]() ![]() Цитата Погоди до завтра, надо на свежую голову посмотреть... Что сразу в глаза бросилось - это реализация List-а... Я помню, что ты просил выложить мою версию, но теперь - no1.gif Будем твою дорабатывать, пока она станет функционально одинаковой с моей... Спасибо! Буду ждать... -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#4
|
Гость ![]() |
![]() С такой реализацией списка тоже можно жить... Неудобно, правда, но ничего - это FPC, в конце концов - перегрузишь операторы - будет смотреться получше... Теперь о том, какие огрехи и в каком порядке у тебя были замечены:
uses |
Bokul |
![]()
Сообщение
#5
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Спасибо!
![]() Учел все замечания, исправил, извиняюсь что сам не отладил некоторые. ![]() Отказ от ненужных переменных oldx,oldy избавил от надобности переопределять метод moveto в объекте TBall. Также поставил в основной цикл отрисовку границ поля - без этого они затираются шариками. И чуточку изменил деструктор TSuperVisor-а - вместо ClearDevice используем такой способ:
Измененные модули вмести с SuperVisor - ![]() Цитата С такой реализацией списка тоже можно жить... Неудобно, правда, но ничего - это FPC, в конце концов - перегрузишь операторы - будет смотреться получше... Что ты имеешь ввиду? Может вместо списков использовать коллекции? Цитата несоответствие типов, копаться, чтобы исправить это я не стал, просто закомментировал вызов процедуры Да, надо изменить наfunction TSuperVisor.DeleteGObject(p:TPItem):boolean; Цитата т.к. не совсем понятно, почему при добавлении к списку функция AddItem получает указатель типа TPGObject, а при удалении DeleteItem требует указатель типа TPItem... Для инициализации звена списка, объект UnitItem должен знать свою информационную часть, и по-этому мы передаем ему переменную типа TPGObject, но потом, при работе с UnitList-ом для удаления своего элемента все, что он должен - это указатель на звено, а не информационную часть (ведь таких звеньев может быть несколько). Вот почему в DeleteGObject как передаваемый параметр я использую TPItem. Цитата /// Это - пока пустые функции, похоже, Calculation здесь вообще не нужна - // вычислениями будет заниматься конкретный объект, а не Наблюдатель Я тоже согласен оставить эти методы абстрактными в TSuperVisor и переопределять их в наследниках. По-поводу Calculation не совсем представляю как это будет. Ты хочешь передавать в TGObject точку, где он столкнулся, а все остальное пусть высчитывает сам? -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#6
|
Гость ![]() |
Цитата Я тоже согласен оставить эти методы абстрактными в TSuperVisor и переопределять их в наследниках. А вот с этого места - поподробнее... Что это будут за наследники от TSuperVisor? СуперНаблюдатели? НаблюдателиСПравомВмешатьсяИПредотвратитьСтолкновение? Что ты вкладываешь в смысл "унаследоваться от объекта" в этом случае? Какие это тебе даст преимущества? Шарики будут так же кататься? Сталкиваться? Отскакивать друг от друга? Это все будет работать и с этим же Наблюдателем, без наследников...Понимаешь в чем дело, ведь просто так наследование - оно не панацея... Его надо применять тогда, когда это необходимо, а не потому, что оно есть в языке... Вот я и прошу тебя объяснить, ЧЕМ программа с ПЕРЕопределенным Наблюдателем будет отличаться от программы, где методы TSuperVisor будут доработаны? Цитата Может вместо списков использовать коллекции? Аналогично, какие ты в этом видишь преимущества? Что есть такое в коллекциях, чего нет (и нельзя сделать, заметь!!!) в списке? Только то, что там ЭТО уже готово, а для TList придется сделать - это для меня не аргумент, тем более в целях обучения... Я, конечно, против изобретения велосипедов, но вот тут как раз - не тот случай... Смотри: есть у меня коллекция. Что я могу с ней сделать (на примере TCollection)? Добавить элемент в коллекцию, найти его в коллекции, удалить оттуда, правда? А со списком все гораздо интереснее... Я же могу переопределить TList на TPriorityList, скажем, и добавлять в список объекты, согласно их приоритету... Например, для того, чтобы обработка квадратов была предпочтительнее обработки треугольников в 3 раза. И, заметь, я это полностью буду контролировать, ибо ЭТО - написано мной, а не кем-то, возможно, даже, лучше меня программирующим, НО не знающим специфики моего приложения... Мне, например, может быть критична скорость операции добавления в список, а по какому критерию оптимизированы коллекции, ты можешь мне гарантировать? ![]() Цитата Ты хочешь передавать в TGObject точку, где он столкнулся, а все остальное пусть высчитывает сам? Да я бы вообще ничего не передавал... Чего, объект своего положения не знает? Зачем еще ему что-то передавать? Надо просто вызвать процедуру обработки столкновения двух объектов (причем она совершенно не обязательно будет методом TSuperVisor, я бы как раз ее сделал посторонней процедурой), а там уже пускай в зависимости от формы и свойств столкнувшихся объектов она разруливает, какой процент энергии будет потерян, и какой объект получит импульс под каким углом (и как именно она это будет делать - тоже никоим образом Наблюдателя не касается - это не его проблема, он Наблюдатель!!! Зафиксировал факт столкновения - сообщил, куда следует. Все, спасибо, продолжай наблюдать)... А дальше - прямая обязанность объекта - пересчитать свои координаты с учетом новых данных... |
Bokul |
![]()
Сообщение
#7
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Цитата А вот с этого места - поподробнее... Что это будут за наследники от TSuperVisor? СуперНаблюдатели? Вот я и прошу тебя объяснить, ЧЕМ программа с ПЕРЕопределенным Наблюдателем будет отличаться от программы, где методы TSuperVisor будут доработаны? ![]() Думал наследием воспользоваться как инструментом поддержки новых типов наследников TGObject... но по твоему способу организации нужда в нем действительно отпадает. Цитата Если тебя прельстил метод ForEach, то это тоже преодолимо, как ты помнишь... ![]() Все, забыл, я даже такого слова никогда не слышал... ![]() Цитата Надо просто вызвать процедуру обработки столкновения двух объектов (причем она совершенно не обязательно будет методом TSuperVisor, я бы как раз ее сделал посторонней процедурой), а там уже пускай в зависимости от формы и свойств столкнувшихся объектов она разруливает, какой процент энергии будет потерян, и какой объект получит импульс под каким углом (и как именно она это будет делать - тоже никоим образом Наблюдателя не касается - это не его проблема, он Наблюдатель!!! З ![]() С появлением все новых наследников TGObject-а TSuperVisor должен запускать все новые и новые процедуры (именно о них я писал в теме "Задачник по ООП", только хотел их всунуть в TSuperVisor), а для этого нам надо как-то дать знать TSuperVisor-у какие процедуры запускать в зависимости от столкнувшихся объектов. Может и бред, но мне на ум приходит сделать еще один список в TSuperVisor , который будет хранить записи типа
и при появлении новых фигур просто добавлять в этот список новые процедуры. -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#8
|
Гость ![]() |
Цитата С появлением все новых наследников TGObject-а TSuperVisor должен запускать все новые и новые процедуры ![]() Procedure ComputeCollision(Var first_obj, second_obj: TGObject);? Значится, так: при столкновении считаем, что произошло столкновение двух точечных объектов (точка столкновения, допустим, известна; векторы скоростей обоих объектов - тоже; возможно - даже коэффициенты упругости - если это неприменимо к понятию "точечный объект", заменим его понятием "абстрактный объект" и их массы; что еще надо?) Ты УЖЕ можешь по тем данным, которые я перечислил пересчитать траектории движения, так? Пересчитал, получил новый вектор скорости для first_obj и second_obj, и ... поменял на него старый - один дополнительный метод в классе TGObject... Пока вижу только одну проблему - само обнаружение столкновения, ибо ту ссылку я так и не могу найти, а все, что находится - очень сложно, и, я боюсь, крайне медленно... Хотя для начала - вполне пойдет и вот такой простой алгоритм: ты в каждый момент времени знаешь, КУДА движется объект, так? Найти ту его точку, которая, скажем так, "движется впереди всех остальных" - то есть, первой вступит в соприкосновение с другим объектом/стенкой (я не говорю сейчас о случае, когда в данный объект врезалИСЬ, рассматривается только случай, что данный объект врезалСЯ в кого-то) сможешь? А проверить, находится ли точка с известными тебе координатами внутри фигуры (т.е., реализовать виртуальную функцию Function IsInside(P: TPoint): Boolean)? Понимаешь, куда я клоню? Точка "движущаяся первой" - есть, проверяешь, не находится ли она случайно внутри другого объекта, и все, если находится (внутри или на границе) - то есть столкновение!!! Конечно, можно подобрать форму объекта, для которого такой вариант не будет работать, но в большинстве случаев достаточно простых фигур (не забывай, со сложнвми есть проблемы еще и при отрисовке, так что очень усложнять тоже нежелательно), мне кажется, проблем быть не должно... По крайней мере, попробуй это реализовать, даже если это ошибочно, ничего плохого не будет, если ты заставишь этот алгоритм работать хотя бы только на кругах/квадратах... |
Bokul |
![]()
Сообщение
#9
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Цитата Ты УЖЕ можешь по тем данным, которые я перечислил пересчитать траектории движения, так? Пересчет идет где? В TGObject? Цитата Найти ту его точку, которая, скажем так, "движется впереди всех остальных" Для квадрата уже будет целая сторона в некоторых случаях.. -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#10
|
Гость ![]() |
Цитата Пересчет идет где? В TGObject? Зачем? Прямо в ComputeCollision - у тебя же "абстрактный объект"Цитата Для квадрата уже будет целая сторона в некоторых случаях.. Значит, придется запоминать массив/список "впереди-идущих" точек (если сторона - значит, конечно, не каждая ее точка, а, скажем, 5-10 равноотстоящих точек между началом и концом этой стороны включительно), и проверять каждую из них на попадание в другую фигуру... ![]() Собственно, полезные ссылки: Определение столкновений выпуклых объектов движущихся с постоянными скоростями Collision Detection and Impulse Dynamics in Real time Applications (это больше для 3D, но все равно интересно) ![]() А вообще - у меня к тебе вопрос: КАКИЕ фигуры ты хочешь вводить в иерархию движущихся объектов? Перечисли ВСЕ фигуры, которые ты себе представляешь... (Лучше всего при ответе на этот вопрос не просто перечислить их, а, хотя бы, привести для каждой фигуры конструктор и методы Show/Hide, понимаешь, о чем я?) |
Bokul |
![]()
Сообщение
#11
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Спасибо!
![]() Извиняюсь, что заставил себя ждать, зато отладил некоторые модули и сделал реализацию твоего алгоритма - теперь шарики умеют отталкиваться. ![]() Изменения в структуре объектов:
ChangeDirection, Calculation, WriteResults - абстрактные до этого времени методы я удалил. Других объектов я не трогал.. Очередная поставка модулей - ![]() Если администрация разрешает - Exe-шник : ![]() Цитата Значит, придется запоминать массив/список "впереди-идущих" точек (если сторона - значит, конечно, не каждая ее точка, а, скажем, 5-10 равноотстоящих точек между началом и концом этой стороны включительно), и проверять каждую из них на попадание в другую фигуру... Это я оставил на следующий этап - пока только одна точка ![]() Понаблюдав чуть-чуть за шариками, можно увидеть ситуации, когда они "слипаются" и летят вмести - знак того, что алгоритм имеет недостатки и его надо менять ![]() ![]() Цитата понимаешь, о чем я? Ты имеешь ввиду, буду ли я делать вогнутые объекты? Не знаю, пока - нет... ![]() Сообщение отредактировано: volvo - 3.01.2007 12:11 -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#12
|
Гость ![]() |
Цитата Других объектов я не трогал.. Реализация TSupervisor осталась за кадром, ты не прикрепил основной файл, поэтому судить о том, насколько правильно ты реализовал предложенная мной алгоритм, я не могу... |
Bokul |
![]()
Сообщение
#13
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Да я вроде все модули прикреплял
![]() ![]() -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#14
|
Гость ![]() |
Цитата(Bokul @ 24.12.2006 2:49) Понаблюдав чуть-чуть за шариками, можно увидеть ситуации, когда они "слипаются" и летят вмести - знак того, что алгоритм имеет недостатки и его надо менять ![]() ![]() во-первых, ты опять тратишь лишние вычислительные ресурсы... На этот раз - здесь: function TBall.distance(P:TPoint):real;Вот расскажи мне, зачем тебе нужен ТУТ корень? Что, недостаточно было возвращать целый квадрат расстояния и сравнивать его с квадратом радиуса? Это ж ускорение какое... Мало того, что операции над целыми выполняются гораздо быстрее, чем над вещественными числами, так еще и "дорогая" Sqrt не нужна, а квадрат радиуса можно вычислять и в конструкторе... Во-вторых... Попробовав уменьшить число шариков до 2-х я с удивлением обнаружил, что проверка на выход за пределы Rect не всегда корректно срабатывает (даже, если шарики вообще не сталкиваются, а идут параллельными курсами ![]() ![]() Теперь еще одно... Что ты делаешь... Ты проходишь по списку шариков, и для каждого из них запускаешь еще один проход по списку... Все прекрасно, НО... Ты не боишься, что при проверке if not(IsInfield(p^.data)) thenты вторично изменяешь направление движения какого-нибудь шарика? Вот тут и будет наблюдаться "слипание"... Итого, программа тебе на сегодня: проверить и отладить IsInField, и придумать такой механизм, который запрещал бы вторично менять направление движения одному и тому же шарику... Кстати, чуть не забыл... Абстрактность метода может задаваться директивой Abstract, тогда реализацию (эту самую "пустышку") вообще не надо писАть... ![]() Теперь вроде все... |
Bokul |
![]()
Сообщение
#15
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Цитата Вот расскажи мне, зачем тебе нужен ТУТ корень? Что, недостаточно было возвращать целый квадрат расстояния и сравнивать его с квадратом радиуса? Это ж ускорение какое... Мало того, что операции над целыми выполняются гораздо быстрее, чем над вещественными числами, так еще и "дорогая" Sqrt не нужна, а квадрат радиуса можно вычислять и в конструкторе... Да ты - прав, выигрыш видно сразу, особенно когда количество объектов достигает несколько сотен.. Цитата Во-вторых... Попробовав уменьшить число шариков до 2-х я с удивлением обнаружил, что проверка на выход за пределы Rect не всегда корректно срабатывает (даже, если шарики вообще не сталкиваются, а идут параллельными курсами blink.gif )... !smoke2.gif Попробуй, увидишь... Что именно? То, что они отбиваются не под правильным углом? Или может они улетают из поля? PS не знаешь формулы расчета угла после удара? Я вывел свою, ну как видешь она не совсем корректно работает ![]() -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#16
|
Гость ![]() |
Цитата Что именно? А именно - вот что (эту картинку я наблюдал сам): представь себе 2 шарика, двигающихся параллельно друг другу из правого верхнего в левый нижний угол экрана... НО как только первый шар касается нижней границы и меняет направление (кстати, меняет он направление не по закону "угол падения равен углу отражения", а на противоположное - на 180 градусов), то же самое делает и второй шар (несмотря на то, что он-то еще не дошел до границы по крайней мере 5 своих диаметров)... ![]() Кстати, где-то на форуме я как-то выкладывал ссылку на физическую модель соударения упругих шаров (если она еще живая) - попробуй поискать, что-то связанное с упруг* / биллиа* / модель |
Bokul |
![]()
Сообщение
#17
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Цитата НО как только первый шар касается нижней границы и меняет направление, то же самое делает и второй шар Не выходит спроектировать тоже самое (хотя, когда много шаров я видел ситуации, как шар менял направления не долетая до стенки), можешь дать данные для двух шаров, когда это происходит? Цитата кстати, меняет он направление не по закону "угол падения равен углу отражения", а на противоположное - на 180 градусов ![]() Код 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 - 25.12.2006 0:13 -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#18
|
Гость ![]() |
Цитата Как предлагаешь TSupervisor-у без наследия определять этот угол для любых возможных наследников TGObject? Ты не забыл, что у каждого объекта есть вектор скорости? И вычисление угла разлета можно производить именно по нему? |
Bokul |
![]()
Сообщение
#19
|
![]() Гуру ![]() ![]() ![]() ![]() ![]() Группа: Пользователи Сообщений: 1 117 Пол: Мужской Реальное имя: Богдан Репутация: ![]() ![]() ![]() |
Цитата Ты не забыл, что у каждого объекта есть вектор скорости? И вычисление угла разлета можно производить именно по нему? ![]() Ты предлагаешь передавать объекту точку столкновения, которая имеет скорость и координаты, а все остальное пусть вычисляет сам? С этим я согласен. Если не понятно, что я хочу сказать, скажи. ![]() -------------------- Лао-Цзы :
Знать много и не выставлять себя знающим есть нравственная высота. Знать мало и выставлять себя знающим есть болезнь. Только понимая эту болезнь, мы можем избавиться от нее. |
volvo |
![]()
Сообщение
#20
|
Гость ![]() |
Что-то обсуждение зацикливается... Я же написал тебе выше - я бы сделал НЕметод класса CalcCollision который получает два объекта (любых), которые могут сообщить всю информацию о себе: где находится сейчас (координаты центра), куда летит, какой у него коэффициент упругости, скорость, и т.д.
Зная подобную информацию о двух объектах можно с легкостью вычислить параметры движения после столкновения, и передать их объектам... Или ты хочешь, чтобы квадрат с треугольником разлетались по другим законам, нежели два круга? Кстати, о скорости... Я бы все-таки предпочел работать с TVector = Record (т.е. вертикальная/горизонтальная составляющая, а не угол относительно одной из осей...) Тогда, например, отскок от стенки будет тривиальным - смена знака одной из составляющих. Да и возможности FPC одним выражением складывать/умножать/вычитать/масштабировать вектора тоже нельзя не принимать во внимание - это может очень сильно облегчить работу... ![]() |
![]() ![]() |
![]() |
Текстовая версия | 19.06.2025 5:00 |