Блог Александра Божко
Архивы
Рубрики
Поделись с другими!
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Перед тем как почесть этот пост я советую посмотреть ролик Всеволода Леонова, посвященный анимации.

Собственно, существует, как минимум два способа реализовать движение объектов в FireMonkey.

Первый из них – банально менять координаты объекта по таймеру.

Второй – использовать компонент TFloatAnimation.

На видео проиллюстрирована реализация движения тела, брошенного под углом к горизонту обеими способами.

И здесь можно увидеть, что траектории полета мячей немного отличаются. Почему? Давайте попробуем разобраться.

Левый мяч “летит” с помощью таймера. Код примерно такой:

procedure TMy3DForm.Button3Click(Sender: TObject);
begin
ct:= 0;
v:= 20;
g:= 9.8;
x0:=0;
angleYG:=30;
BallRadius:= Ball.Width/2;
Timer1.Enabled:= True;
end;
procedure TMy3DForm.Timer1Timer(Sender: TObject);
var
x, y: real;

function CheckGround: boolean;
begin
  Result:= False;

 if Ball.Position.Y>-BallRadius then
 begin
 ct:=0;
 Timer1.Enabled:= False;
 Ball.Position.Y:=-BallRadius;
 x0:= Ball.Position.Z;
 end;

end;

begin

  AngleYR:= DegToRad(AngleYG);
     ct:= ct+(Timer1.Interval/1000);
     x :=  x0+v*ct*Cos(AngleYR);
     Ball.Position.Z:=x;
     y:= (BallRadius+
     (v*ct*sin(AngleYR))-(ct*ct*g/2));
     Ball.Position.Y:= -1*y;
     CheckGround;

end;

Здесь просто изменяется позиция мяча с учетом его радиуса. В конце каждой итерации проверяется, не достиг ли мяч земли (процедура CheckGround).  И здесь очень важно, какой задан интервал таймера. Для того, что бы получить более точную имитацию движения объекта интервал должен быть минимальным. С другой стороны, значительное уменьшение интервала может привести к подтормаживанию. С точки зрения визуального восприятия лучше задать значение 40 (25 кадров в секунду), что я и сделал.

Для второго мяча (правого) я использовал два компонента TFloatAnimation. Примерно так, как это сделал Всеволод Леонов. Из кода наглядно видны настройки обоих компонентов.

procedure TMy3DForm.Button4Click(Sender: TObject);
begin
Ball.Position.Y:= -0.5;
Ball2.Position.Y:= -0.5;
Ball.Position.Z:= 0;
Ball2.Position.Z:= 0;

ct:= 0;
v:= 20;
g:= 9.8;
x0:=0;
angleYG:=30;

BallRadius:= Ball.Width/2;

CulcParams;
AnimationX.Duration:= t;
AnimationX.StopValue:= L;

AnimationY.AnimationType:= TAnimationType.atOut;
AnimationY.StartFromCurrent:= True;
AnimationY.Duration:= t/2;
PosYMax:= -(yMax+BallRadius);
AnimationY.StopValue:= PosYMax;
AnimationY.Interpolation:= TInterpolationType.itQuadratic;
AnimationY.AutoReverse:= True;

{ CodeSite.Send('Z - '+FloatToStr(Ball2.Position.Z)+'Y - '+FloatToStr(Ball2.Position.Y)+
'  L - '+FloatToStr(L)+ ' t - '+ FloatToStr(t)+' yMax - '+FloatToStr(yMax));}
AnimationX.Enabled:= True;
AnimationY.Enabled:= True;
AnimationY.Start;
AnimationX.Start;
end;

procedure TMy3DForm.AnimationYProcess(Sender: TObject);
begin
if Ball2.Position.y = -BallRadius then
begin
AnimationX.Stop;
AnimationY.Stop;
CodeSite.Send(FloatToStr(PosYMax));
v:= v/1.5;
CulcParams;

AnimationY.AnimationType:= TAnimationType.atOut;
AnimationY.StartFromCurrent:= False;
AnimationY.StartValue:= -BallRadius;
AnimationY.Duration:= t/2;
AnimationY.StopValue:= -(yMax+BallRadius);
AnimationY.Loop:= True;

AnimationX.StartValue:=Ball2.Position.Z;
AnimationX.Duration:= t;
AnimationX.StopValue:= Ball2.Position.Z+L;
 CodeSite.Send('Z - '+FloatToStr(Ball2.Position.Z)+' Y - '+FloatToStr(Ball2.Position.Y)+
 ' L - '+FloatToStr(L)+ ' t - '+ FloatToStr(t)+ ' V - '+FloatToStr(V)+' yMax - '+FloatToStr(yMax));
AnimationX.Start;
AnimationY.Start;

end;
end;

Расчет дальности и времени полета, а так же максимальной высоты, на которую поднимается мяч  производится по формулам в процедуре CulcParams:

procedure TMy3DForm.CulcParams();
begin
AngleYR:= DegToRad(AngleYG);
yMax:=  Sqr(v)*Sqr(Sin(AngleYR))/(2*g);
L:= Sqr(v)*Sin(2*AngleYR)/g;
t:= L/(v*Cos(AngleYR));
end;

Здесь же я реализовал эффект отскока мяча. Чуть позже я прокомментирую этот код, а пока вернемся к исходному вопросу. Почему первый мяч пролетает чуть дальше второго?

Чуть чуть поправим код процедуры CheckGround. Включим лог и реализуем отскок.

function CheckGround: boolean;
begin
  Result:= False;

 if Ball.Position.Y>-BallRadius then
 begin
 ct:=0;
 Timer1.Enabled:= False;
 // Пишем реальное значение в лог
 CodeSite.Send(FloatToStr(Ball.Position.Z)+' - '+FloatToStr(Ball.Position.Y));
 Ball.Position.Y:=-BallRadius;
 x0:= Ball.Position.Z;

 v:= v/1.5;  // После удара о землю скорость падает
 if v<1 then
 begin
   // check stop

   Result:=True;
   Timer1.Enabled:= False;
   Exit;
 end;
 // Отскок. Запускаем таймер заново
 Timer1.Enabled:= True;
 end;

end;

для логирования я воспользовался инструментом Code Site, входящим в состав RAD Studio. Он действительно очень эффективно позволяет организовать логирование в процессе работы программы. Подробнее прочесть о Code Site вы можете в блоге WebDelphi.ru. Я же просто приведу сам лог.

Info    36.0266571044922 – -0.100639998912811
Info    52.1924629211426 – -0.229333326220512
Info    59.2746276855469 – -0.441528886556625
Info    62.5591087341309 – -0.389256298542023
Info    64.0644989013672 – -0.420495808124542
Info    64.7031478881836 – -0.484564274549484
Info    65.0072631835938 – -0.479582995176315
Info    65.1289138793945 – -0.499673187732697

Мы видим, что из-за дискретности таймера мяч немного “проваливается”, а не останавливается на значении 0,5 (радиус мяча) по оси Y. Соответственно, и по оси X он пролетает чуть дальше. Именно для того, что бы погрешность не накладывалась я вставил в код строчку:

Ball.Position.Y:=-BallRadius;

А вот при анимации с помощью TFloatAnimation, я изначально рассчитал экстремальные точки и компоненты сделали интерполяцию. И как видно из ролика – достаточно точно.

Теперь еще пару комментариев по коду.

По оси Y я поднимаю мяч до рассчитаной заранее точки и возвращаю его обратно по тойже траектории:

AnimationY.AutoReverse:= True;

По оси X осуществляется линейное движение.

TFloatAnimation имеет события OnProcees и OnFinish. Но дело в том, что если движение зациклено (Loop = True), то OnFinish срабатывать не будет. Как и в предыдущем случае, я при ударе мяча об землю гашу скорость в полтора раза и переопределяю значение свойства Duration (длительность анимации) и начальное и конечное положение объекта по обеим осям. Что бы остановить и возобновить движение я использую методы Stop и Start. При этом если мы вызвали Stop, то изменение значения свойства Enabled уже не влияет на поведение объекта.

Траектория движения подбирается значениями свойств AnimationType и Interpolation.

Конечно, сложные траектории будет довольно сложно смоделировать с помощью TFloatAnimation , но для простых – это хороший вариант. Возможно, если уравнение перемещения объекта задается одной формулой, то его можно реализовать с помощью собственного компонента, расширив список типов интерполяции. Я уверен, что разумное решение можно найти для любой, даже такой невероятной траектории:

pre style=’color:#000000;background:#ffffff;’><span style=’color:#800000; font-weight:bold; ‘>procedure</span> TMy3DForm<span style=’color:#808030; ‘>.</span>CulcParams<span style=’color:#808030; ‘>(</span><span style=’color:#808030; ‘>)</span><span style=’color:#800080; ‘>;</span>
<span style=’color:#800000; font-weight:bold; ‘>begin</span>
AngleYR<span style=’color:#808030; ‘>:</span><span style=’color:#808030; ‘>=</span> DegToRad<span style=’color:#808030; ‘>(</span>AngleYG<span style=’color:#808030; ‘>)</span><span style=’color:#800080; ‘>;</span>
yMax<span style=’color:#808030; ‘>:</span><span style=’color:#808030; ‘>=</span>  Sqr<span style=’color:#808030; ‘>(</span>v<span style=’color:#808030; ‘>)</span><span style=’color:#808030; ‘>*</span>Sqr<span style=’color:#808030; ‘>(</span>Sin<span style=’color:#808030; ‘>(</span>AngleYR<span style=’color:#808030; ‘>)</span><span style=’color:#808030; ‘>)</span><span style=’color:#808030; ‘>/</span><span style=’color:#808030; ‘>(</span><span style=’color:#008c00; ‘>2</span><span style=’color:#808030; ‘>*</span>g<span style=’color:#808030; ‘>)</span><span style=’color:#800080; ‘>;</span>
L<span style=’color:#808030; ‘>:</span><span style=’color:#808030; ‘>=</span> Sqr<span style=’color:#808030; ‘>(</span>v<span style=’color:#808030; ‘>)</span><span style=’color:#808030; ‘>*</span>Sin<span style=’color:#808030; ‘>(</span><span style=’color:#008c00; ‘>2</span><span style=’color:#808030; ‘>*</span>AngleYR<span style=’color:#808030; ‘>)</span><span style=’color:#808030; ‘>/</span>g<span style=’color:#800080; ‘>;</span>
t<span style=’color:#808030; ‘>:</span><span style=’color:#808030; ‘>=</span> L<span style=’color:#808030; ‘>/</span><span style=’color:#808030; ‘>(</span>v<span style=’color:#808030; ‘>*</span>Cos<span style=’color:#808030; ‘>(</span>AngleYR<span style=’color:#808030; ‘>)</span><span style=’color:#808030; ‘>)</span><span style=’color:#800080; ‘>;</span>
<span style=’color:#800000; font-weight:bold; ‘>end</span><span style=’color:#800080; ‘>;</span>
</pre>

Другие статьи серии:

Firemonkey на практике #0
Firemonkey на практике #1
Firemonkey на практике #2. Освещение и материал поверхности 3D объектов
Firemonkey на практике #3. Использование 3D моделей
Firemonkey на практике #4. Ты попал!
Firemonkey на практике #5.


Поделись с другими!
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Продукты DevArt
Купить онлайн:



Читай русскоязычные Delphi блоги
Каталог блогов Blogdir.ru
Яндекс.Метрика