高级动画:炸弹掉落游戏实现与优化
立即解锁
发布时间: 2025-08-26 01:40:49 阅读量: 3 订阅数: 14 

### 高级动画:炸弹掉落游戏实现与优化
#### 1. 代码实现动画概述
在某些情况下,需要通过代码以编程方式创建动画的每一个细节。比如处理多个动画,且事先不知道动画数量和配置方式;或者想在不同页面使用相同动画,将动画相关细节与标记分离以方便复用。创建、配置和启动动画并不难,只需创建动画和故事板对象,将动画添加到故事板,然后启动故事板,还可在动画结束后通过响应 `Storyboard.Completed` 事件进行清理工作。
下面以一个炸弹掉落游戏为例,游戏中一系列炸弹以不断增加的速度掉落,玩家需点击每个炸弹进行拆除,当达到设定的掉落炸弹数量上限(默认 5 个)时,游戏结束。
#### 2. 游戏主要页面布局
游戏主页面包含一个两列网格。
- **左侧**:是一个 `Border` 元素,其中包含代表游戏表面的 `Canvas`:
```xml
<Border Grid.Column="0" BorderBrush="SteelBlue" BorderThickness="1" Margin="5">
<Grid>
<Canvas x:Name="canvasBackground" SizeChanged="canvasBackground_SizeChanged" MinWidth="50">
<Canvas.Background>
<RadialGradientBrush>
<GradientStop Color="AliceBlue" Offset="0"></GradientStop>
<GradientStop Color="White" Offset="0.7"></GradientStop>
</RadialGradientBrush>
</Canvas.Background>
</Canvas>
</Grid>
</Border>
```
当 `Canvas` 首次调整大小或用户改变浏览器窗口大小时,会运行以下代码设置裁剪区域:
```csharp
private void canvasBackground_SizeChanged(object sender, SizeChangedEventArgs e)
{
// Set the clipping region to match the current display region of the Canvas.
RectangleGeometry rect = new RectangleGeometry();
rect.Rect = new Rect(0, 0, canvasBackground.ActualWidth, canvasBackground.ActualHeight);
canvasBackground.Clip = rect;
}
```
这是为了防止 `Canvas` 绘制超出其显示区域的子元素,避免炸弹飞出 `Canvas` 范围。
- **右侧**:是一个面板,用于显示游戏统计信息、当前掉落和拆除的炸弹数量,以及一个开始游戏的按钮:
```xml
<Border Grid.Column="1" BorderBrush="SteelBlue" BorderThickness="1" Margin="5">
<Border.Background>
<RadialGradientBrush GradientOrigin="1,0.7" Center="1,0.7" RadiusX="1" RadiusY="1">
<GradientStop Color="Orange" Offset="0"></GradientStop>
<GradientStop Color="White" Offset="1"></GradientStop>
</RadialGradientBrush>
</Border.Background>
<StackPanel Margin="15" VerticalAlignment="Center" HorizontalAlignment="Center">
<bomb:Title></bomb:Title>
<TextBlock x:Name="lblRate" Margin="0,30,0,0" TextWrapping="Wrap" FontFamily="Georgia" FontSize="14"></TextBlock>
<TextBlock x:Name="lblSpeed" Margin="0,30" TextWrapping="Wrap" FontFamily="Georgia" FontSize="14"></TextBlock>
<TextBlock x:Name="lblStatus" TextWrapping="Wrap" FontFamily="Georgia" FontSize="20">No bombs have dropped.</TextBlock>
<Button x:Name="cmdStart" Padding="5" Margin="0,30" Width="80" Content="Start Game" Click="cmdStart_Click"></Button>
</StackPanel>
</Border>
```
其中 `bomb:Title` 是一个自定义用户控件,显示游戏标题。要使用该自定义用户控件,需将项目命名空间映射到 XML 命名空间:
```xml
<UserControl x:Class="BombDropper.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:bomb="clr-namespace:BombDropper;assembly=BombDropper">
```
然后就可以使用 `bomb` 前缀插入自定义控件:
```xml
<bomb:Title></bomb:Title>
```
#### 3. 炸弹用户控件
炸弹的图形可以使用静态图像,但使用 Silverlight 形状更灵活,能在不产生失真的情况下调整大小,还可对绘图的各个部分进行动画处理或修改。示例中的炸弹图形来自 Microsoft Word 的在线剪贴画集,转换为 XAML 后进行了简化,插入到名为 `Bomb` 的新用户控件中。
`Bomb` 用户控件代码如下:
```csharp
public partial class Bomb: UserControl
{
public Bomb()
{
InitializeComponent();
}
public bool IsFalling
{
get;
set;
}
}
```
其标记中包含一个 `RotateTransform`,用于在炸弹下落时产生摆动效果:
```xml
<UserControl x:Class="BombDrop.Bomb"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<UserControl.RenderTransform>
<TransformGroup>
<RotateTransform Angle="20" CenterX="50" CenterY="50"></RotateTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"></ScaleTransform>
</TransformGroup>
</UserControl.RenderTransform>
<Canvas>
<!-- The Path elements that draw the bomb graphic are defined here. -->
</Canvas>
</UserControl>
```
#### 4. 炸弹掉落实现
游戏使用 `DispatcherTimer` 来控制炸弹掉落,它会在用户界面线程上触发事件,避免多线程编程的复杂性。
相关代码如下:
```csharp
private DispatcherTimer bombTimer = new DispatcherTimer();
public Page()
{
InitializeComponent();
bombTimer.Tick += bombTimer_Tick;
}
// Keep track of how many bombs are dropped and stopped.
private int droppedCount = 0;
private int savedCount = 0;
// Initially, bombs fall every 1.3 seconds, and hit the ground after 3.5 seconds.
private double initialSecondsBetweenBombs = 1.3;
private double initialSecondsToFall = 3.5;
private double secondsBetweenBombs;
private double secondsToFall;
private void cmdStart_Click(object sender, RoutedEventArgs e)
{
cmdStart.IsEnabled = false;
// Reset the game.
droppedCount = 0;
savedCount = 0;
secondsBetweenBombs = initialSecondsBetweenBombs;
secondsToFall = initialSecondsToFall;
// Start the bomb-dropping timer.
bombTimer.Interval = TimeSpan.FromSeconds(secondsBetweenBombs);
bombTimer.Start();
}
private void bombTimer_Tick(object sender, EventArgs e)
{
// Create the bomb.
Bomb bomb = new Bomb();
bomb.IsFalling = true;
// Position the bomb.
Random random = new Random();
bomb.SetValue(Canvas.LeftProperty, (double)(random.Next(0, (int)(canvasBackground.ActualWidth - 50))));
bomb.SetValue(Canvas.TopProperty, -100.0);
// Add the bomb to the Canvas.
canvasBackground.Children.Add(bomb);
// Attach mouse click event (for defusing the bomb).
bomb.MouseLeftButtonDown += bomb_MouseLeftButtonDown;
// Create the animation for the falling bomb.
Storyboard storyboard = new Storyboard();
DoubleAnimation fallAnimation = new DoubleAnimation();
fallAnimation.To = canvasBackground.ActualHeight;
fallAnimation.Duration = TimeSpan.FromSeconds(secondsToFall);
Storyboard.SetTarget(fallAnimation, bomb);
Storyboard.SetTargetProperty(fallAnimation, new PropertyPath("(Canvas.Top)"));
storyboard.Children.Add(fallAnimation);
// Create the animation for the bomb "wiggle."
DoubleAnimation w
```
0
0
复制全文
相关推荐









