«STAR WARS REIMAGINED: african totem C3PO» © 2016, Евгений 
CGtalk.by CG Award 2016: «STAR WARS REIMAGINED: african totem C3PO», Евгений
   
Главная Форумы Галерея Теория Конкурсы Вакансии
Назад   CGtalk.by
Новые сообщения Зарегистрироваться

View Single Post
Старый 15.06.2007, 22:03   #1
black zorro
Участник
 
Сообщений: 12
Flash 8 & Физика. Столкновение точки со стеной

Сегодня мы продолжаем начатый в прошлый раз рассказ о методах интеграции физики во flash.
В прошлый раз мы рассмотрели основы векторной математики.

Мы узнали, что такое вектор, что такое длина вектора, и нормаль к нему, и что такое нормализованный вектор.

Поняли, как векторы преобразуются в углы, и какой смысл несет скалярное произведение двух векторов.

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


Мы узнали, что вектор задает собой направление движения чего-то. Если вектор состоит из двух компонент (x,y) - то это движение на плоскости. Из школьной формулы:

расстояние = время * скорость

Ясно, что координаты объекта зависят от времени. Во flash есть два стандартных механизма позволяющих изменять координаты объекта во времени.

Прежде всего, если flash основан на понятии временной шкалы на которой находится множество кадров сменяющих друг за другом, то простейший способ это ввести код который срабатывает на событие начала каждого нового кадра. Важно знать сколько времени занимает один кадр, по умолчанию, частота равна 12 кадрам в секунду.

Скорость смены кадров определяется настройками документа flash в меню Modify -> Document.

Для примера создайте любой символ в библиотеке нового документа (я использовал круг, но какой либо разницы это не имеет).

Дайте этому символу имя smb_circle, затем поместите два объект этого символа на слой Layer1 и дайте им имена obj_circle_1 и obj_circle_2.

Введите следующий actionscript код в первом кадре документа.

Код:
  var v_coords = {start: {x: 10, y: 20}, ort: {x: 0.31, y: 0.94} };
_root.obj_circle_1._x = v_coords.start.x;
_root.obj_circle_1._y = v_coords.start.y;
 
var count_frames = 0;
// счетчик количества пройденных кадров
_root.onEnterFrame  = function (){
count_frames ++;
_root.obj_circle_1._x += v_coords.ort.x * 10;
_root.obj_circle_1._y += v_coords.ort.y * 10;    
// или так 
_root.obj_circle_2._x = 200+ v_coords.start.x + v_coords.ort.x * 10 * count_frames;
_root.obj_circle_2._y = v_coords.start.y + v_coords.ort.y * 10 * count_frames;
}
Как видите при запуске ролика, падение двух шариков абсолютно идентично, просто второй падает на 200px правее.

Я создал вектор v_coords задающий начальные координаты шарика. А затем назначил функцию обработчик события onEnterFrame (при наступлении каждого очередного кадра, т.е. каждую 12 долю секунды) который изменяет значения координат объекта obj_circle на величину произведения соответствующей компоненты нормализованного вектора и числа десяти.

Почему я использовал произведение 10 и именно нормализованного вектора?
Да просто это смотрится неплохо, шарик медленно ползет сверху вниз, не более того. На самом деле, нужно понимать, что в то время как на экране мы оперируем пикселями, объекты окружающего нас мира задаются в других единицах измерения и все что вам нужно, так это задать правила соотношения этих величин. Это достаточно нетривиальная задача, иногда в целях геймплея приходится нарушать эти пропорции.

Часть игр, если их делать действительно похожими на реальную жизнь, будут невообразимо скучны или, наоборот, требовать от игрока просто нечеловеческой реакции или усидчивости. В общем играйтесь коэффициентами, но оставьте базовые законы математики и физики в покое.

А теперь отложим в сторону философию и рассмотрим, чисто технические сложности подхода основанного на onEnterFrame. Flash построен на концепции кадров сменяющихся друг за дружкой. Если вы указываете в свойства документа некоторую частоту FPS, то это значит что flash просто будет пытаться поддерживать именно такую скорость выполнения.

Но это не означает, что он действительно может выполнить вашу просьбу. На это влияют такие факторы как загрузка процессора иными задачами, браузером, антивирусом. Возможно, что объем вычислений будет разниться в разных кадрах, а, следовательно, заявленные 12 кадров в секунду могут упасть до меньших значений. Давайте попробуем другой подход основанный на таймере. Мы можем попросить flash извещать нас каждые, скажем, 100 миллисекунд, и выполнять действия по расписанию.

Для этого нам следует воспользоваться функцией setInterval. В простейшем случае в качестве параметров этой функции следует указать имя функции, которая будет вызываться по расписанию, затем значение интервала в миллисекундах и, наконец, можно передать произвольное количество параметров которые будут в свою очередь переданы функции вызываемой из таймера. Для демонстрации некоторых неприятных моментов связанных с реализацией setInterval давайте модифицируйте пример выше.

Код:
  var v_coords = {start: {x: 10, y: 20}, ort: {x: 0.31, y: 0.94} };
_root.obj_circle_1._x = v_coords.start.x;
_root.obj_circle_1._y = v_coords.start.y;
 
_root.obj_circle_2._x = 200+ v_coords.start.x;
_root.obj_circle_2._y = v_coords.start.y;
 
var initialTime_1 = getTimer();
var initialTime_2 = getTimer();
_root.onEnterFrame  = function (){
  var nowTime = getTimer();
  _root.obj_circle_1._x += v_coords.ort.x * 1;
  _root.obj_circle_1._y += v_coords.ort.y * 1;    
  trace ("-- onEnterFrame "+ (nowTime - initialTime_1));    
  initialTime_1 = getTimer();    
}
// функция выполняющая изменение координат мяча,
// параметры _fio, _age, _sex не используются и служат только
// для демонстрации
function fooUpdateBall (_fio, _age, _sex){
  var nowTime = getTimer();
  _root.obj_circle_2._x += v_coords.ort.x * 1;
  _root.obj_circle_2._y += v_coords.ort.y * 1;    
  trace ("-- setInteral "+ (nowTime - initialTime_2));    
  initialTime_2 = getTimer();    
  updateAfterEvent ();
}
// запускаем функцию срабатывающую каждые 50 мс
setInterval (fooUpdateBall, 50, "Bill", 12, "male");
Для оценки времени я использовал функцию getTimer которая возвращает величину количества времени прошедшего от момента запуска ролика. Для удобства я изменил значение частоты кадров до 20 кадров в секунду, или, что тоже самое, 50 миллисекунд между отдельными кадрами (именно это число указано вторым параметром функции setInterval). Предположительно два шарика должны двигаться синхронно.

Давайте проверим и запустим пример...

А вот и нет! Шарики движутся хоть и близко, но не с одинаковой скоростью. Более того, обратите внимание на значения, выводимые функций trace – разность времени скачет от 45 миллисекунд до 55, и иногда в вызовах setInterval разницы подскакивают до 100 миллисекунд. Теперь изменим параметр – время задержки с 50 до 60 миллисекунд и соберем статистику (время задержки подскочило до 100 и не опускается ниже), теперь уменьшим время до 10 и снова соберем статистику (здесь числа примерно равны 15).

Проще говоря, если вы просите flash вызвать некоторую функцию через определенный интервал то, будьте готовы к погрешности примерно равной величине времени для одного кадра. С другой стороны уменьшать время кадра до величин, скажем, 60 кадров в секунду также нельзя, чтобы не привести к катастрофической потере производительности.

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

S=V*T

Примерно, как в примере ниже, хотя там также возникают погрешности, но это лучшее что можно предложить:

Код:
  var initialTime_2= getTimer();
function fooUpdateBall (_fio, _age, _sex){
  var nowTime = getTimer();
  var time: Number = int(( nowTime - initialTime_2) / 60) ;
  _root.obj_circle_2._x = 200+ v_coords.start.x + v_coords.ort.x * time;
  _root.obj_circle_2._y = v_coords.start.y + v_coords.ort.y * time;    
  updateAfterEvent ();
}
// обратите внимание на число 60 здесь и на то, что внутри функции fooUpdateBall  разницу во времени я делю на него же
setInterval (fooUpdateBall, 60, "Bill", 12, "male");
Да еще, напишите письмо adobe, чтобы они наконец-то озаботились поддержкой в flash player многопоточных вычислений, синхронизации, и заодно многоядерных машин. А ведь еще есть проблема, связанная с лимитом времени выполнения одного кадра в 15 секунд, и если ваш код сложнее, то крутитесь, как хотите.
black zorro офлайн   Ответить с цитированием
 
Polygon.by. Учебно-практический центр компьютерной графики.

Онлайн журнал о магии компьютерной графики и цифрового искусства «Magic CG»
Пакет инструментов для обеспечения качества 3D-контента для игр
CG-EVENT

Текущее время 10:23 (GMT +4)


Powered by vBadvanced CMPS v3.0 RC1
Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Хостинг и облачные решения — Active Technologies

Все права защищены © 2006 — 2014, CGtalk.by