Главная » Разработка для Windows Phone 7 » Проблема прыгающего мяча Windows Phone 7

0

Рассмотрим XAML, созданный для телефона с большим экраном и обеспечивающий перемещение шара вверх и вниз:

<Grid x:Name="ContentPanel" Grid.Row="1"> <Path Fill="Red"> <Path.Data>

<EllipseGeometry RadiusX="25" RadiusY="25" /> </Path.Data>

<Path.RenderTransform>

<TranslateTransform x:Name="translate" X="240" /> </Path.RenderTransform> </Path>

<Path Fill="{StaticResource PhoneAccentBrush}"

Data="M 100 625 L 380 625, 380 640, 100 640 Z" />

<Grid.Triggers>

<EventTrigger>

<BeginStoryboard>

<Storyboard RepeatBehavior="Forever">

<DoubleAnimation Storyboard.TargetName="translate" Storyboard.TargetProperty="Y" From="50" To="60 0" Duration="0:0:1"

AutoReverse="True" RepeatBehavior="Forever" />

</Storyboard> </BeginStoryboard> </EventTrigger> </Grid.Triggers> </Grid>

В нем используется два элемента Path. Первый описывает красный шар (будем называть его «мячом») второй – поверхность, по которой этот шар будет «прыгать».

К мячу применяется TranslateTransform: значение свойства X фиксировано, что обеспечивает горизонтальное центрирование мяча; к свойству Y применяется анимация в диапазоне значений от 50 до 600 и обратно. Но получаемое перемещение не выглядит как реальное подпрыгивание мяча, потому что выполняется постоянно с одинаковой скоростью, т.е. не подчиняется законам физики. В реальности физические законы действуют по умолчанию, но в компьютерной графике для их реализации часто приходится потрудиться.

Получить более реалистичную модель подпрыгивания мяча поможет объект DoubleAnimationUsingKeyFrames с SplineDoubleKeyFrame, который ускоряет мяч при падении и замедляет при его движении вверх. Для создания эффекта свободного падения используются опорные точки сплайна:

<Storyboard RepeatBehavior="Forever">

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="translate"

Storyboard.TargetProperty="Y"> <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="50" /> <SplineDoubleKeyFrame KeyTime="0:0:1" Value="600"

KeySpline="0.25 0, 0.6 0.2" /> <SplineDoubleKeyFrame KeyTime="0:0:2" Value="50"

KeySpline="0.75 1, 0.4 0.8" /> </DoubleAnimationUsingKeyFrames> </Storyboard>

Вот теперь намного лучше, но по-прежнему не вполне правильно. Проблема в моделировании момента удара мяча о землю. Когда мяч ударяется о землю, он имеет максимальную скорость и сразу же с максимальной скоростью начинает движение в противоположном направлении.

В реальности все происходит не так. Когда мяч ударяется о землю, он замедляется и немного деформируется, сжимаясь. Обратная деформация приводит к тому, что мяч опять ускоряется. Можно ли это смоделировать? Почему нет?

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

<Storyboard RepeatBehavior="Forever">

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="translate"

Storyboard.TargetProperty="Y"> <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="50" /> <SplineDoubleKeyFrame KeyTime="0:0:1" Value="600"

KeySpline="0.25 0, 0.6 0.2" /> <DiscreteDoubleKeyFrame KeyTime="0:0:1.1" Value="600" /> <SplineDoubleKeyFrame KeyTime="0:0:2.1" Value="50"

KeySpline="0.75 1, 0.4 0.8" /> </DoubleAnimationUsingKeyFrames> </Storyboard>

Трансформация TranslateTransform начинается в нулевой момент времени со значения 50. В течение следующей секунды ее значение меняется до 600 (мяч ускоряется). Следующую десятую секунды значение остается равным 600 и затем возвращается к 50 в течение следующей секунды. Теперь анимация длится не 2, а 2,1 секунды.

Конечно, само по себе это выглядит даже еще хуже. Но давайте добавим в Path, описывающий мяч, один ScaleTransform:

<Path.RenderTransform> <TransformGroup>

<ScaleTransform x:Name="scale" CenterY="25" /> <TranslateTransform x:Name="translate" X="240" /> </TransformGroup> </Path.RenderTransform>

Не подверженный трансформациям центр мяча находится в точке (0, 0), радиус мяча равен 25 пикселам, поэтому нижняя точка мяча имеет координаты (0, 25). Это точка, которая касается пола, и она должна оставаться неподвижной во время ScaleTransform, поэтому мы и задали CenterY равным 25. CenterX равен 0 по умолчанию.

Вот еще две анимации для моментального распрямления мяча после деформации:

<Storyboard RepeatBehavior="Forever">

<DoubleAnimationUsingKeyFrames

Storyboard.TargetName="scale" Storyboard.TargetProperty="ScaleX">

<DiscreteDoubleKeyFrame KeyTime="0:0:1"           Value="1" />

<SplineDoubleKeyFrame KeyTime="0:0:1.05"          Value="1.5"

KeySpline="0.75 1,                                0.4 0.8" />

<SplineDoubleKeyFrame KeyTime="0:0:1.1"           Value="1"

KeySpline="0.25 0,                                0.6 0.2" /> </DoubleAnimationUsingKeyFrames>

<DoubleAnimationUsingKeyFrames

Storyboard.TargetName="scale" Storyboard.TargetProperty="ScaleY"> <DiscreteDoubleKeyFrame KeyTime="0:0:1" Value="1" /> <SplineDoubleKeyFrame KeyTime="0:0:1.05" Value="0.66"

KeySpline="0.75 1, 0.4 0.8" /> <SplineDoubleKeyFrame KeyTime="0:0:1.1" Value="1"

KeySpline="0.25 0, 0.6 0.2" /> </DoubleAnimationUsingKeyFrames> </Storyboard>

В промежутке времени между 1 секундой и 1,05 секунды ширина мяча увеличивается на 50% и высота уменьшается на треть. Ситуация меняется на обратную в течение следующих 0,05 секунд, после чего мяч приобретает нормальную форму и начинает движение вверх.

В окончательной версии приложения BouncingBall (Прыгающий мяч) к мячу также применяется RadialGradientBrush:

Проект Silverlight: BouncingBall Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1"> <Path>

<Path.Data>

<EllipseGeometry RadiusX="25" RadiusY="25" /> </Path.Data>

<Path.Fill>

<RadialGradientBrush GradientOrigin="0.35 0.35" Center="0.35 0.35"> <GradientStop Offset="0" Color="White" /> <GradientStop Offset="1" Color="Red" /> </RadialGradientBrush>

</Path.Fill>

<Path.RenderTransform> <TransformGroup>

<ScaleTransform x:Name="scale" CenterY="25" /> <TranslateTransform x:Name="translate" X="240" /> </TransformGroup> </Path.RenderTransform> </Path>

<Path Fill="{StaticResource PhoneAccentBrush}"

Data="M 100 625 L 380 625, 380 640, 100 640 Z" />

<Grid.Triggers>

<EventTrigger>

<BeginStoryboard>

<Storyboard RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames

Storyboard.TargetName="translate" Storyboard.TargetProperty="Y"> <DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="50" /> <SplineDoubleKeyFrame KeyTime="0:0:1" Value="600"

KeySpline="0.25 0, 0.6 0.2" /> <DiscreteDoubleKeyFrame KeyTime="0:0:1.1" Value="600" /> <SplineDoubleKeyFrame KeyTime="0:0:2.1" Value="50"

KeySpline="0.75 1, 0.4 0.8" /> </DoubleAnimationUsingKeyFrames>

<DoubleAnimationUsingKeyFrames

Storyboard.TargetName="scale" Storyboard.TargetProperty="ScaleX"> <DiscreteDoubleKeyFrame KeyTime="0:0:1" Value="1" /> <SplineDoubleKeyFrame KeyTime="0:0:1.05" Value="1.5"

KeySpline="0.75 1, 0.4 0.8" /> <SplineDoubleKeyFrame KeyTime="0:0:1.1" Value="1"

KeySpline="0.25 0, 0.6 0.2" /> </DoubleAnimationUsingKeyFrames>

<DoubleAnimationUsingKeyFrames

Storyboard.TargetName="scale" Storyboard.TargetProperty="ScaleY"> <DiscreteDoubleKeyFrame KeyTime="0:0:1" Value="1" /> <SplineDoubleKeyFrame KeyTime="0:0:1.05" Value="0.66"

KeySpline="0.75 1, 0.4 0.8" /> <SplineDoubleKeyFrame KeyTime="0:0:1.1" Value="1"

KeySpline="0.25 0, 0.6 0.2" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Grid.Triggers> </Grid>

И вот что мы получаем:

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии