概述

本文试图通过经典的游戏-俄罗斯方块,来演示WPF强大的图形界面编程功能。

涉及的图形方面有这几个方面:

1、不规则界面的设置

2、布局系统的使用

3、2D图形的应用

4、输入事件的响应

5、风格样式的使用

6、跨线程的调用

7、自定义控件

我们先截两张成品的图片,获取一点感性的认识。截图如下:

图1

图2

以上两张图中,整个程序界面已经和市场上的游戏比较接近。7种形状分别以不同的颜色进行区分,可以快速下落,一键下落,开始、暂停、算分,基本上功能全了。

实现

那这个程序究竟是怎么实现的呢?实际上,我在逻辑的层面,把它成分了两个层次,一个是图形展示层,一个是游戏算法实现层。游戏算法尽量地与图形界面分离,这样同的算法可以比较轻松地移植到别的平台上,比如用H5实现,或是用D3D实现等等,你只要关注不同平台的图形显示部分即可。

界面

界面上,除了基础的元素如分数、级别、启停按钮等,还对整体做了个性化的定制。比如使用了椭圆的程序界面,游戏主体部分使用了阴影显示,用一个动画显示欢迎信息等。

下边,我们一步一步把程序拆分开来。

1、主窗体部分

主窗体部分主要由Xaml来实现,包括其中的动画部分。

1)椭圆的实现

WPF中设置图形界面是十分的方便,整个应用程序就是一个大画布,想怎么画就怎么画。就算根窗体,一个Clip属性就可以改变它的形状。这里用的是.Net Framework里自带的

EllipseGeometry 类,只要设置两个半径和中心点即可。这里有别于Winform下边的GDI实现,不需要显式的调用图形API去画图。具体代码如下:
    <Window.Clip>
<EllipseGeometry RadiusX="470" RadiusY="340" Center="512,384"/>
</Window.Clip>

2)动画的实现

动画的实现,可以自己手动写代码。不过,可以通过VS Blend更方便的实现动画。这里将左边的“俄罗斯广场欢迎你!”几个字做一个随意的动画。为了提高开发效率,这里使用了Blend来制作的动画。下边的Blend的操作界面:

图3

以下是通过编辑器生成的代码:

-定义触发器

 <Window.Triggers>

        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource OnLoaded1}"/>
</EventTrigger> </Window.Triggers>

-定义资源

<Window.Resources>
<Storyboard x:Key="OnLoaded1" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="label">
<EasingDoubleKeyFrame KeyTime="0" Value="-24"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="69"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="-9"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="-115.5"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="-90"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="label">
<EasingDoubleKeyFrame KeyTime="0" Value="-21"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="3"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="100.5"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="90"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="-21"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="label">
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="-169.33"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="150.377"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="-3.442"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>

注:WPF任何属性定义都可以当作资源来重用。

3)花式按钮

这个比较简单,也就是设置了一下本身的样式。

编辑器窗口:

代码如下:

<Button Content="X" ToolTip="关闭" Focusable="False" HorizontalAlignment="Left" Margin="828,320,0,0" VerticalAlignment="Top" Width="75" Height="79" FontSize="36" Foreground="#FFA62121" Click="Button_Click">
<Button.Background>
<RadialGradientBrush>
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FF6FDABD" Offset="1"/>
</RadialGradientBrush>
</Button.Background>
</Button>

4)Grid布局

界面元素的摆放,可以通过绝对坐标的方式,但这种方式通常需要手工编码的方式去计算元素的位置,所以在复杂的应用系统中是不建议使用的。那么为了简化界面元素的排列,于是便有了布局系统。一般比较细粒度的一点的控制,多采用Grid,这里也采用了Grid。Grid主要是将界面分成若干行和列,然后将元素放入其中,类似Html中的Table,这里不赘述。

下边是一个一行两列的Grid,Grid本身铺满父容器。

 <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="300*"/>
</Grid.ColumnDefinitions>
...
</Grid>

5)容器的拖拽

这里也很简单,写一段响应MouseDown事件的代码:

 private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}

6)自定义控件

网格线的实现,及预置方块。代码如下:

private void UserControl_Initialized(object sender, EventArgs e)
{
for (var col = ; col <= ; col++)
{
Line line = new Line();
line.X1 = col * ;
line.X2 = col * ;
line.Y1 = ;
line.Y2 = ;
line.Stroke = new SolidColorBrush(Color.FromArgb(0xFF, , , ));
grid.Children.Add(line);
} for (var row = ; row <= ; row++)
{
Line line = new Line();
line.X1 = ;
line.X2 = ;
line.Y1 = row * ;
line.Y2 = row * ;
line.Stroke = new SolidColorBrush(Color.FromArgb(0xFF, , , ));
grid.Children.Add(line);
} for (int row = ; row < ; row++)
{
for (int col = ; col < ; col++)
{
bool show = datas[row, col];
Cube cube = new Cube();
cube.Width = ;
cube.Height = ;
cube.HorizontalAlignment = HorizontalAlignment.Left;
cube.VerticalAlignment = VerticalAlignment.Top;
if (show)
{
cube.Visibility = Visibility.Visible;
}
else
{
cube.Visibility = Visibility.Hidden;
}
var m = cube.Margin;
cube.Margin = new Thickness(m.Left + col * + , m.Top + row * + , m.Right, m.Bottom);
cubes[row, col] = cube;
grid.Children.Add(cube);
}
}
}

算法

本程序的算法,主要是通过两个二维数组来实现的。

具体算法略。

源码

WPF实战俄罗斯方块的更多相关文章

  1. 如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

    如何从40亿整数中找到不存在的一个 前言 给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数.(在文件中至少确实一个这样的数-为什么?).在具有足够内存的情况 ...

  2. WPF实战之一 桌面消息框(右下角消息弹出框)

    此版本是根据别人的项目改造的,记录下笔记 原文:https://blog.csdn.net/catshitone/article/details/75089069 一.即时弹出 1.创建弹出框 新建一 ...

  3. WPF实战案例-打印

    在前段时间做了一下打印,因为需要支持的格式比较多,所以wpf能打印的有限分享一下几种格式的打印(.xls .xlsx .doc .docx .png .jpg .bmp .pdf) 首先为了保证exc ...

  4. WPF实战案例-MVVM模式下在Xaml中弹出窗体

    相信很多学习和开发wpf项目的同学都了解过mvvm模式,同样,在mvvm模式下会有一个不可忽视的问题,就是怎么在xaml中弹出窗体,而不破坏MVVM本身的结构. 关于弹出窗体的方式还是很多的,本文先讲 ...

  5. WPF实战案例-数据代理

    在我们wpf开发中,很多人会有mvvm模式去做wpf的项目. 是否有人遇到这样一个场景:在一个界面上,有个tabcontrol上面有4个页签,每个页签里面都有一个datagrid,里面显示的列基本一样 ...

  6. WPF实战案例-在线程内同步集合数据到UI线程

    有这样一个场景,在vm中,我们为了ui的体验,会异步访问后端接口,获取数据集合,如果这个集合绑定到界面,并且在线程内,怎么处理? 有人讲:this.Dispatcher.Invoke,如果在vm内呢? ...

  7. WPF实战案例-MVVM模式下用附加属性在Xaml中弹出窗体

    嗯..最近回家去了,2个月没写过代码了,面试只能吹牛,基础都忘了,今天回顾一下,分享一篇通过附加属性去处理窗体弹出的情况. 或许老司机已经想到了,通过设置附加属性值,值变更的回调函数去处理窗体弹出,是 ...

  8. WPF 实战 - 翻页控件

    1. 先上效果 <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> ...

  9. 转载 wpf使用经验

    转载自 胡庆访[ http://zgynhqf.cnblogs.com/ ] WPF 是一个界面层框架技术,要对 WPF 技术达到熟练运用的程度,需要同时拥有开发和设计两方面的知识.而我作为一名开发人 ...

随机推荐

  1. [JAVA IDEA]在使用maven项目中,无法读取resources文件夹中的配置文件的一种解决方案

    1.在通过配置文件来连接数据库时,在resouces文件中放入了db.properties配置文件,但无法正常读取到 读取配置文件信息的代码: InputStream input=JdbcUtil.c ...

  2. 工作经验:Java 系统记录调用日志,并且记录错误堆栈

    前言:现在有一个系统,主要是为了给其他系统提供数据查询接口的,这个系统上线不会轻易更新,更不会跟随业务系统的更新而更新(这也是有一个数据查询接口系统的原因,解耦).这时,这个系统就需要有一定的方便的线 ...

  3. spss C# 二次开发 学习笔记(一)——配置数据源

    由于项目的需要,使用Spss进行数据统计分析. Spss对于数据统计分析的功能有多强主要是客户关注的事情,我所主要关注的是,Spss的二次开发有多复杂. 学习的基本思路是: (1)首先了解统计基本知识 ...

  4. Python Djan 路由对应的名称

    路由关系命名 对URL路由关系进行命名,以后可以根据此名称生成自己想要的URL 1. url(r'fdsafdsaeeeee',views.index, name='hello') #给这个url后面 ...

  5. 初学Node.js

    下载Node.js,官方网址:https://nodejs.org/en/download/ 可根据根据自己的电脑配置来下载相当于的Node.js 下载完成后使用Windows键+R 输入cmd 输入 ...

  6. VScode基础设置

    安装依赖包: • One Monokai • Aglia • One Dark Pro • Material Icon   漂亮的主题: Themes Quokka 是一个调试工具插件,能够根据你正在 ...

  7. JSON转换和序列化的区别

    序列化是将对象状态转换为可保持或可传输的格式的过程.与序列化相对的是反序列化,它将流转换为对象.这两个过程结合起来,可以轻松地存储和传输数据.将对象的状态信息转换为可以存储或传输的窗体的过程. 在序列 ...

  8. 中专生自学Android到找到工作的前前后后

    我是一名中专生,在学校里读的是计算机专业,但是由于学校不好大部分同学都不爱学习来这里几乎大部分都是在混日子的,虽然我中考的成绩不差,但是因为家里穷考虑到以后没钱读大学我毅然来到这里,虽然是中专,但是我 ...

  9. volley7--NetworkDispatcher从网络中获取数据

    源码: /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, V ...

  10. Android8.0适配那点事(二)

    小伙伴们,咱们今天咱继续对Android8.0的适配进行分解,今天将针对启动页,版本适配和系统限制等进行“啃食” 猛戳这里查看Android8.0适配那点事(一): 1.启动页适配 近日,我无意中发现 ...