
Angle To...
По скольку писать в блоги я не умею (несколько раз пробовал — такая фигня получается), то попробую как-то более технически рассказать про свой игровой фреймворк. Ну и может иногда всякие программистские штуки рассказывать, с которыми приходится сталкиваться. Кто-то эти штуки может знать, а кому-то вполне пригодится.
Например определение угла между двумя точками. В своё время дёрнул метод из какого-то класса и забыл про него. А сегодня столкнулся с одной проблемой, которая заставила меня присмотреться к нему и в итоге переписать. Оказалось всё гораздо проще, чем было, буквально в одну строчку. Сейчас мой метод выглядит вот так:
Пояснение: Vec2 — это своя реализация вектора.
Например определение угла между двумя точками. В своё время дёрнул метод из какого-то класса и забыл про него. А сегодня столкнулся с одной проблемой, которая заставила меня присмотреться к нему и в итоге переписать. Оказалось всё гораздо проще, чем было, буквально в одну строчку. Сейчас мой метод выглядит вот так:
public function angleTo(v:Vec2):Number {
return Math.atan2(v.y - y, v.x - x) * 180 / Math.PI;
}
Пояснение: Vec2 — это своя реализация вектора.
- +2
- elmortem
Комментарии (52)
public static const RAD_TO_DEG: Number = 57.29577951;
…
return Math.atan2(v.y — y, v.x — x) * RAD_TO_DEG;
Основная причина вызов из класса (Math.PI) и деление.
А вообще всегда можно протестировать :)
1) от 1 миллиона итераций примерно для inline
2) от 200 тысяч итераций примерно для вызова своей функции где используется PI
Так что оптимизации похоже есть, но константа все равно быстрее :)
-_____-
Для деления на уровне чипа используются битовые сдвиги и маски (при беззнаковом) и вычитания при знаковом.
При этом есть проверки результатов операции на больше, меньше 0.
При 0 в качестве делителя, на выходе будет мусор, т.к. первый же сдвиг/вычитание выдаст неверный ответ.
На самом деле при деление как таковом никакой ошибки не происходит, но на выходе будет мусор или тоже самое число, а это не правильно.
Чтобы число было тоже самое, делить нужно на 1.
Вот чтобы избежать такой неоднозначности и сделали деление на ноль ошибкой, которая выдается сразу, еще до деления.
При операциях с плавающей точкой все немного сложнее, но суть таже.
Раньше подобные операции выполнялись программно, позже это перенесли на уровень чипов, но алгоритмы работы не изменились :)
гдето пример был похожий:
3*5 = 3*5 // =15. сократим на 5
3*5/5 = 3*5/5
3 = 3 //всё верно =) теперь с нулем
3*0 = 5*0 // =0. сократим равенство на 0, попытаемся поделить по правилам =)
3*0/0 = 5*0/0
3 = 5
3*0/0 = 3*0/0
3 = 3
Всё работает (:
2 darkvam. Так что не советую яблоки на 0 делить ))))
х/1 = х
х/0 = х
Значит 1 = 0, значит произведем равноценным обмен, я вам 0 миллионов, а вы мне 1.
Можно логически проследить куда стремится число при уменьшении знаменателя:
х/1 = х
х/0,5 = 2х
…
х/0,1 = 10х
х/0,01 = 100х
Первый пример оч правелен. Но это недостаток чисел. А если расуждать не числами?) А на банальном бытовом уровне.) Хочу ещё потроллить)
8/1 = 8
8/0 = число стремящееся к бесконечности, тоесть — бесконечно.
Деление проверяется умножением.
бесконечность * 0 = 8.
Круто.
Ну вопще тут прослеживается недостаток математики либо не оговоренность некоторого рода)
Другое дело, что предел число/переменная приближается к бесконечности, когда переменная приближается к 0.
Так что всё плачевно)
На банальном уровне 8 яблок можно разделить между Х людьми, но если никого нет, то между никем их и нельзя разделить — они остнутся сами по себе, а никто останется без яблок ;)
Отнюдь, вы можете взять ни одной палочки много-много раз
Другое дело если оба аргумента равны нулю 0о — это абсурд. Угол поворота точки мы не проходили.
И проверку этой ситуации внутри angleTo() для вывода какого-то другого результата я бы не стал делать — нерациональное замедление работы ради нелепого случая, которого можно избегать уровнем выше.
Это абсурд, но такая ситуация возможна — две точки находятся в одном месте, или вы считаете, что проверка нахождения точек в одном месте будет быстрее?
И я не говорю, что эту проверку не следует делать. Я лишь говорю, что не стал бы вносить ее в тело angleTo («избегал бы уровнем выше»). Ведь я могу обходить заведомо ненулевые вектора быстрее, чем если бы это сделал, скажем, angleTo2, спотыкающийся об if.
Думаю, что проверка равенства двух чисел нулю все равно намного быстрее вычисления арктангенса.
Но при большом количестве точек я бы не жертвовал в фонд дураков своими миллисекундами )
И кстати — это только у меня Math.atan2(0, 0) выдает просто 0 без каких либо ошибок?
atan2(0, 0) = 0
а это вполне соответствует… действительности?
Но не совсем ничем, в данном случае вы были правы — проверка должна проводиться уровнем выше, т.к. в контексте результат может быть некорректен.
Поясню: предположим есть пушка, которая следит за мышью. Если мышь навести точно на пушку — то вычисление даст угол 0, но в данном случае корректно было бы оставить предыдущее значение — потом что при наведении отведении угол будет скакать между действительным углом и нулем.