當前位置: 妍妍網 > 碼農

實作【英雄聯盟】 PLAY 按鍵

2024-05-24碼農

實作【英雄聯盟】 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,表示該顏色位於漸變的終點。

    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 style="{StaticResource GoldLine}"/>
    <Image style="{StaticResource Emblem}"/>
    <Border style="{StaticResource GreenLine}"/>
    <Path x:Name="path" style="{StaticResource Arrow}"/>
    <Grid>
    <Grid.Clip>
    <RectangleGeometry Rect="0,5,165,28"/>
    </Grid.Clip>
    <TextBlock x:Name="play" style="{StaticResource Play}"/>
    <TextBlock x:Name="stop" style="{StaticResource 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 style="{StaticResource GoldLine}"/>
    <Image style="{StaticResource Emblem}"/>
    <Border style="{StaticResource GreenLine}"/>
    <Path x:Name="path" style="{StaticResource Arrow}"/>
    <Grid>
    <Grid.Clip>
    <RectangleGeometry Rect="0,5,165,28"/>
    </Grid.Clip>
    <TextBlock x:Name="play" style="{StaticResource Play}"/>
    <TextBlock x:Name="stop" style="{StaticResource 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、Stars

  • BiliBili [5] : 一鍵三連

  • 信箱: [email protected]

  • 參考資料

    [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