实现《英雄联盟》 PLAY 按键
控件名称:RiotPlayButton
作者:WPFDevelopersOrg - Vicky&James
源码链接[1]:https://github.com/vickyqu115/riotplaybutton
教学视频[2](【小李趣味多】https://bit.ly/3xI9DNh)
这篇文章是对 WPF RiotPlayButton 教程视频的技术回顾。
摘要本文详细介绍并分析了使用纯 WPF 技术开发受《英雄联盟》游戏启发的 PLAY 按钮的过程。本文强调了利用 WPF 功能创建各种用户界面组件的过程,并为开源开发提供了新的视角。同时,探索了动画和触发器等高级 WPF 功能,以提升用户交互体验。
介绍用户界面组件在提升用户体验方面非常重要。在游戏中,反应迅速且视觉上有吸引力的 PLAY 按钮是通往娱乐世界的门户。本文展示了使用 WPF 这一构建丰富桌面应用程序的强大框架创建 PLAY 按钮的过程。
项目背景本文讨论的项目尽可能全面地展示了 WPF 技术的能力。几年前发布这个项目后,获得了积极的反馈,这激励我继续为开源开发做贡献。随着 .NET 技术的发展,我不断更新和改进共享在 GitHub 上的代码。考虑到整个项目中包含的内容丰富,我决定详细分析每个部分的组成和技术重点,希望能为更多喜欢 WPF 技术的人提供帮助。
按钮构成通过分析器可以看到,这个 PLAY 按钮继承了 WPF ToggleButton 的属性。左边是《英雄联盟》游戏的标志,右边包含边界、图像、文本等多种设计元素。此外,还添加了鼠标悬停和检查触发效果。
主要内容分析1. 创建不规则形状第一个和第二个图形可以通过使用 Border 控件轻松编码。然而,第三个尖角和弧形的图形不能简单地使用 Border 编码。因此,尽管最初可以使用多边形和坐标来绘制,但多边形属性不提供绘制弧形的功能。因此,必须使用 Path 控件来编码。
详细分析<Style TargetType="{x:Type Path}" x:Key="Arrow">
<Setter Property="Fill" Value="#1E2328"/>
<Setter Property="Stroke" Value="{StaticResource ArrowStroke}"/>
<Setter Property="StrokeThickness" Value="2"/>
<Setter Property="Data" Value="M 0,0 L 103,0 L 118,14 L 103,28 L 0,28 C 10,14 0,0 0,0 Z"/>
<Setter Property="Margin" Value="40 5 4 -5"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="5" ShadowDepth="2"/>
</Setter.Value>
</Setter>
</Style>
在 WPF 中,Path 控件是用于绘制各种形状和轮廓的强大工具。Path 控件通过路径数据定义形状,路径数据是一系列指定如何绘制形状的命令和坐标。
Path 控件的主要属性如下:
Data 属性:Data 属性是 Path 控件的重要属性,用于指定描述形状轮廓的路径数据。路径数据格式包括 MoveTo (M)、LineTo (L)、CurveTo (C)、ClosePath (Z) 等命令与坐标的结合。Fill 属性:Fill 属性用于指定形状内部的填充颜色,可以使用颜色、渐变、图案或透明度填充形状内部。Stroke 属性:Stroke 属性用于指定形状轮廓的颜色,可以使用各种颜色定义轮廓颜色。StrokeThickness 属性:StrokeThickness 属性用于指定轮廓的厚度,决定轮廓的宽度。命令和坐标:路径数据是命令和坐标的连续体,这些命令指示 WPF 从一个点到另一个点如何绘制形状。常见的路径命令包括:M (MoveTo):将绘制点移动到指定坐标。
L (LineTo):绘制到指定坐标的直线。
C (CurveTo):使用控制点绘制贝塞尔曲线。
Z (ClosePath):关闭路径,将当前点连接到起点形成闭合形状。
Data 属性是 Path 控件的关键属性,包含定义形状轮廓的路径数据。这些路径数据由一系列命令和坐标组成,描述了路径的轮廓。在这个项目中,路径数据的命令和坐标的详细描述如下:
可以将其简单地解释为 X/Y 坐标轴。将此形状的长度设置为 118,宽度设置为 28:
M 0,0:这是 “MoveTo” 命令,将绘制点移动到坐标 (0, 0) 作为起点。
L 103,0:这是 “LineTo” 命令,从当前点 (0, 0) 绘制到坐标 (103, 0) 的直线。接着绘制到 (118, 14)、(103, 28)、(0, 28) 的线段。
由于这是对称形状,第二条线的 Y 坐标是形状总高度的一半,即 14。
接下来是绘制曲线的部分:C 10,14 0,0 0,0 z:这是 “贝塞尔曲线” 命令,定义了前一个点为控制点,后一个点为终点的贝塞尔曲线。这个命令定义了控制点为 (10, 14),终点为 (0, 0) 的贝塞尔曲线,并使用 ‘z’ 命令关闭路径,将其连接回起点 (0, 0)。
2. 创建渐变颜色<LinearGradientBrush x:Key="ArrowStroke" StartPoint="0.5,0" EndPoint="0.5,1" >
<GradientStop Color="#CC3FE7EE" Offset="0"/>
<GradientStop Color="#CC006D7D" Offset="0.5"/>
<GradientStop Color="#CC0493A7" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ArrowStrokeOver" StartPoint="0.5,0" EndPoint="0.5,1" >
<GradientStop Color="#FFAFF5FF" Offset="0"/>
<GradientStop Color="#FF46E6FF" Offset="0.5"/>
<GradientStop Color="#FF00ADD4" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ArrowFillOver" StartPoint="0.5,0" EndPoint="0.5,1" >
<GradientStop Color="#FF1D3B4A" Offset="0"/>
<GradientStop Color="#FF082734" Offset="1"/>
</LinearGradientBrush>
在游戏的这个部分,描边部分并不是简单的纯色,而是由多种色调组成的渐变颜色。为了实现这个效果,我们可以使用 LinearGradientBrush 来自定义颜色。
LinearGradientBrush 的主要属性及使用方法StartPoint 和 EndPoint:StartPoint 指定渐变的起点,通常使用相对坐标表示。这里 (0, 0) 是左上角,(1, 1) 是右下角。EndPoint 使用相对坐标指定渐变的终点。GradientStops:GradientStops 是 GradientStop 对象的集合,每个对象定义颜色和相对位置(Offset)。GradientStop 的 Color 属性定义指定位置的颜色,Offset 属性定义渐变中的颜色位置,通常在 0 到 1 之间。渐变方向:渐变的方向由 StartPoint 和 EndPoint 决定。例如,StartPoint 为 (0, 0),EndPoint 为 (1, 1) 时,渐变从左上角到右下角。渐变类型:LinearGradientBrush 默认设置为线性渐变,颜色沿直线渐变。通过调整 StartPoint 和 EndPoint,可以改变渐变的方向和起点,生成各种渐变效果。在这个项目中,我们希望创建一个从形状中央开始向下移动的垂直渐变。因此,将 StartPoint 设置为 (0.5, 0) 指定渐变的起点为顶部中央,将 EndPoint 设置为 (0.5, 1) 指定渐变的终点为底部中央。
然后,GradientStops 集合包含三个 GradientStop 对象,每个对象定义了不同的颜色和相对位置:
第一个 GradientStop:Color 设置为 #CC3FE7EE。Offset 设置为 0,表示该颜色位于渐变的起点。
第二个 GradientStop:Color 设置为 #CC006D7D。Offset 设置为 0.5,表示该
颜色位于渐变的中间。
第三个 GradientStop:Color 设置为 #CC0493A7。Offset 设置为 1,表示该颜色位于渐变的终点。处理 Path 和 Border 厚度在 Border 控件中:Border 控件的边框线包含在 Border 内。边框线的厚度由 BorderThickness 属性控制,以设备独立像素(DIPs)指定边框线的宽度。
在 Path 控件中:Path 控件的边框线以 StrokeThickness 属性的中心位置为基准绘制。StrokeThickness 控制边框线的厚度,表示边框线从中心延伸的距离。
在这个固定大小的图形中,将 Border 和 Path 的厚度都设置为 2,边距设置为 4 4 4 4。但在这种设置下,可以看到 Path 的上边框超出了 Border。因此,需要根据 StrokeThickness 调整 Path 的边距。左边距已经设置为 40,可以覆盖 GreenLine,所以没有问题。上边距需要增加 1 像素,设置为 5 像素,右边距和下边距不需要更改。由于 Path 的尺寸固定为 118x28,只需调整左边和上边的边距。
此外,上边距增加 5 像素后,下边看起来可能会被裁剪。为避免这种情况,可以将下边距设置为 -5 像素,这样上边增加的 5 像素会被去掉,布局会保持平衡。另一种方法是保持下边距为 0 像素。这两种方法都可以通过增加上边距来防止下边被裁剪。
4. 使用 Jamesnet.WPF Nuget 生成动画在 WPF 中,可以生成各种动态动画来使用户界面更加有趣。在这个项目中,使用厚度动画为 TextBlock 的文本部分添加有趣的动画效果。
<Application x:Class="VickyPlayButton.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:james="https://jamesnet.dev/xaml/presentation"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Height" Value="38"/>
<Setter Property="Width" Value="165"/>
<Setter Property="Foreground" Value="#FFFFFF"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<ControlTemplate.Resources>
<Storyboard x:Key="Checked">
<james:ThickItem Mode="CubicEaseInOut" TargetName="play" Property="Margin" Duration="0:0:0:0.5" To="30 100 0 0"/>
<james:ThickItem Mode="CubicEaseInOut" TargetName="stop" Property="Margin" Duration="0:0:0:0.5" To="30 0 0 0"/>
</Storyboard>
<Storyboard x:Key="UnChecked">
<james:ThickItem Mode="CubicEaseInOut" TargetName="play" Property="Margin" Duration="0:0:0:0.5" To="30 0 0 0"/>
<james:ThickItem Mode="CubicEaseInOut" TargetName="stop" Property="Margin" Duration="0:0:0:0.5" To="30 0 0 100"/>
</Storyboard>
</ControlTemplate.Resources>
<Grid Background="{TemplateBinding Background}">
<Border />
<Image />
<Border />
<Path x:Name="path" />
<Grid>
<Grid.Clip>
<RectangleGeometry Rect="0,5,165,28"/>
</Grid.Clip>
<TextBlock x:Name="play" />
<TextBlock x:Name="stop" />
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="path" Property="Fill" Value="{StaticResource ArrowFillOver}"/>
<Setter TargetName="path" Property="Stroke" Value="{StaticResource ArrowStrokeOver}"/>
<Setter Property="Foreground" Value="#FFFCF1DC"/>
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="path" Property="Fill" Value="#1E2328"/>
<Setter TargetName="path" Property="Stroke" Value="#5C5B57"/>
<Setter Property="Foreground" Value="#3C3C41"/>
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource Checked}"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource UnChecked}"/>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
动画可以通过 ControlTemplate.Resources 定义,定义了 “Checked” 和 “UnChecked” 两个动画资源。在 “Checked” 状态下,“Play” 文本消失,“Stop” 文本出现;在 “UnChecked” 状态下,“Stop” 文本消失,“Play” 文本出现。这就创建了一个翻转效果的动画。
为了更容易地创建和使用动画,我将各种 WPF 动画编译成了 Jamesnet.WPF Nuget 包。只需添加这个包,就可以轻松地使用和编写动画。
5. 使用 Grid.Clip 属性<Grid Background="{TemplateBinding Background}">
<Border />
<Image />
<Border />
<Path x:Name="path" />
<Grid>
<Grid.Clip>
<RectangleGeometry Rect="0,5,165,28"/>
</Grid.Clip>
<TextBlock x:Name="play" />
<TextBlock x:Name="stop" />
</Grid>
</Grid>
由于 Grid 内的元素相互重叠,在创建文本上下滚动动画时,可能会出现文本超出边框的视觉问题。为了解决这个问题,使用 <Grid.Clip> 属性。
<Grid.Clip> 是一个 XAML 元素,用于定义子元素的可见区域。剪辑区域通常是一个矩形,只有剪辑区域内的内容才会显示,超出部分将被隐藏。
在这个项目中,<Grid.Clip> 区域设置在 Path 的尺寸范围内:Rect="0,5,165,28"。这样,文本只会在这个区域内显示,从而在 Path 内实现上下滚动的效果。
* 视频内容纠正最近有一位观众指出了我们视频中的一个错误,我在此做出更正和说明。
11:34 贝塞尔曲线里C不是弧线的中点吧,控制点是在线外边的
“贝塞尔曲线中,C点不是曲线的中点。控制点是在曲线外的。”
在11:34的视频里,会看到以下内容(图片与视频说明一致)。
经过重新审视,我发现确实在解释贝塞尔曲线时出现了误解。感谢这位观众的指正,现在我来进行修正。
首先,视频中使用的路径如下:
M 0,0 L 103,0 L 118,14 L 103,28 L 0,28 C 10,14 0,0 0,0 Z
Y轴的0,0基准反转对理解没有太大影响,请大家谅解。
通过生成的图表可以看到,C (10, 14) 控制点的位置实际上在曲线的外部,而不是中点。这正是观众指出的问题。
接下来,我们详细了解一下三次贝塞尔曲线的机制。
三次贝塞尔曲线需要4个点:P0, P1, P2, P3。根据当前的几何路径数据,分别映射如下:
P0: 0,28 (起点)
P1: 10,14 (控制点1)
P2: 0,0 (控制点2)
P3: 0,0 (终点)
此外,我们绘制了曲线随时间变化的过程。
蓝色线段的起点和终点随着控制点的移动而绘制出曲线。具体的可视化效果请参见这里[3],该博客中有很好的动画说明。
在这种情况下,使用二次贝塞尔曲线比三次贝塞尔曲线更合适,因为二次贝塞尔曲线需要的控制点更少。
三次贝塞尔曲线 (Cubic Bezier)
M 0,0 L 103,0 L 118,14 L 103,28 L 0,28 C 10,14 0,0 0,0 Z
二次贝塞尔曲线(Quadratic Bezier)
M 0,0 L 103,0 L 118,14 L 103,28 L 0,28 Q 10,14 0,0 Z
综上所述,在这种情况下使用二次贝塞尔曲线更为合适。
沟通与支持我们随时保持沟通渠道开放。大家可以通过以下方式与我们互动:
GitHub[4]: 关注、Fork、StarsBiliBili[5]: 一键三连邮箱: james@jamesnet.dev参考资料[1]源码链接: https://github.com/vickyqu115/riotplaybutton
[2]教学视频: https://bit.ly/3xI9DNh
[3]这里: https://blog.naver.com/kyuniitale/40022945907
[4]GitHub: https://github.com/vickyqu115/smartdate
[5]BiliBili: https://bit.ly/3xI9DNh
相关资讯
英雄联盟,简称“LOL”,是由美国拳头公司(Riot Game)开发、中国大陆地区腾讯游戏代理运营的英雄对战MOBA竞技网游,游戏里拥有数百个个性英雄,并拥有排位系统、符文系统等个...
2024-01-30 18:32
TOC8第八届《英雄联盟:云顶之弈》公开赛(Teamfight Tactics Open Championships,简称TOC)是CN赛区官方在SET11画中灵赛季举办的奖金池最...
2024-04-11 17:19
杭州亚运会英雄联盟1/4决赛,中国队与强敌展开了一场激烈的对决。比赛进行到最后,中国队展现出了他们的绝世逆袭能力,将全球网友纷纷惊呼不已!双方比赛一开始,就展现出了惊人的实力。中国...
2023-12-20 08:18
早在英雄联盟12周年庆之际的“策划面对面”节目中,官方曾透露过跨区匹配功能正在开发中,不知道大家还记得这个饼吗?今天英雄联盟终于宣布,玩家们心心念念的跨区匹配功能即将在12月内开启...
2023-12-07 18:07
“跨区匹配”正式上线,这无疑是英雄联盟14年来最重要的一次更新。曾几何时,跨服组团的梦想远在天边;如今技术进步,这一梦想终于成真。这14年里,英雄联盟经历了太多太多。从最初的嘈杂对...
2023-12-11 14:47
英雄联盟PBE美测服启动异常/启动失败怎么办?解决办法来了召唤师技能是英雄联盟的一大特色,每个玩家将有两个独立安放召唤师技能的技能栏。召唤师技能独立计算CD,不需要任何消耗手段。很...
2024-03-02 05:13
英雄联盟,作为一款自2009年问世以来就在全球范围内收获了巨大成功的竞技游戏,它不仅引领了MOBA(多人在线战斗竞技场)类型的潮流,更是塑造了一个独特的电子竞技文化现象。这款游...
2024-03-16 12:49
【来源:中国新闻网】中新网南京9月3日电(记者 王昊)9月2日晚,2024英雄联盟全球总决赛LPL赛区资格赛在南京落下帷幕。在决定全球总决赛最后一张门票的比赛中,WBG以3:2击败...
2024-09-03 02:21
有这么一群电竞选手,他们在赛场上的每一个操作都能牵动粉丝们的心。就像Faker,那可是英雄联盟界的传奇人物啊。可现在呢,局势变得有些奇怪。昨天T1和TES的那场比赛,就像是一颗投入...
2024-10-20 22:24
各位LPL的观众和英雄联盟召唤师大家好,这里是天下游戏汇。LPL春季赛在昨天进行了三场比赛的较量,在早些时候进行的是联盟第一BLG与联盟垫底队伍RA之间的比赛,这场比赛双方实力差距...
2024-03-11 17:37
热门资讯
MORE+热门游戏
MORE+34.63MB
40.44MB
70.78MB
60.26M
27.16M
308.49MB
120.76MB
47.57MB
72.78MB
68.13MB