Glade 提供了視覺化介面設計,簡化 GTK 開發流程,讓開發者專注於應用程式邏輯而非介面細節。透過 gtk::Builder 可以輕鬆載入 Glade 檔案,並以程式碼操控介面元素。然而,在回呼函式中使用介面元件時,需要注意變數生命週期問題。使用 move 關鍵字搭配 GTK 物件的低成本 clone 特性,可以有效解決此問題,確保回呼函式正常運作。除了 GTK,Rust 生態還有 Relm、Qt、flutter-rs 等 GUI 框架可供選擇,滿足不同開發需求。更進一步,Rust 的高效能和安全性也使其成為遊戲開發的理想選擇,Amethyst 遊戲引擎的 ECS 架構提供了一種組織遊戲邏輯的有效方法。

使用 Glade 建立 GTK 圖形使用者介面

在開發圖形使用者介面(GUI)應用程式時,使用 Glade 可以簡化介面設計的過程。Glade 是一個使用者介面設計工具,允許開發者以視覺化的方式設計 GTK 應用程式的介面。本章節將介紹如何使用 Glade 建立 GTK GUI,並載入 Glade 檔案至 Rust 程式中。

建立 Glade 檔案

首先,需要建立一個 Glade 檔案來定義 GUI 的佈局。Glade 檔案是一種 XML 格式的檔案,用於描述 GTK 介面的結構。在 Glade 中,可以拖曳不同的元件(如按鈕、標籤、圖片等)來建立介面。

以下是一個簡單的 Glade 檔案範例(layout.glade):

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- 其他內容 -->
  <object class="GtkApplicationWindow" id="applicationwindow1">
    <!-- 視窗內容 -->
    <child>
      <object class="GtkBox" id="output_box">
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkLabel" id="message_output"/>
        </child>
        <child>
          <object class="GtkImage" id="image_output">
            <property name="pixbuf">cat.png</property>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>

載入 Glade 檔案至 Rust 程式

要將 Glade 檔案載入至 Rust 程式,需要使用 gtk::Builder。首先,建立一個新的 Rust 專案,並將 layout.glade 檔案複製到 src 資料夾中。然後,在 main.rs 中填入以下程式碼:

extern crate gio;
extern crate gtk;

use gio::prelude::*;
use gtk::prelude::*;
use std::env::args;

fn build_ui(app: &gtk::Application) {
    let glade_src = include_str!("layout.glade");
    let builder = gtk::Builder::new_from_string(glade_src);
    let window: gtk::Window = builder.get_object("applicationwindow1").unwrap();
    window.set_application(app);
    window.show_all();
}

fn main() {
    let application = gtk::Application::new(
        "com.shinglyu.catsay-gui-glade",
        Default::default()
    ).expect("Failed to initialize GTK");

    application.connect_activate(|app| {
        build_ui(app);
    });

    application.run(&args().collect::<Vec<_>>());
}

內容解密:

  1. include_str! 巨集用於將 layout.glade 檔案內容載入為字串。
  2. gtk::Builder::new_from_string 方法用於從 Glade 檔案的字串表示建立 GTK 介面。
  3. builder.get_object 方法用於取得 Glade 檔案中定義的元件。
  4. window.set_application 方法用於將視窗與 GTK 應用程式關聯。

新增互動功能

要新增互動功能,需要為按鈕點選事件新增回呼函式。以下是一個範例(build_ui 函式):

fn build_ui(app: &gtk::Application) {
    // ...
    let message_input: gtk::Entry = builder.get_object("message_input").unwrap();
    let button: gtk::Button = builder.get_object("generate_btn").unwrap();
    let message_output: gtk::Label = builder.get_object("message_output").unwrap();
    let image_output: gtk::Image = builder.get_object("image_output").unwrap();

    button.connect_clicked(move |_| {
        message_output.set_text(&format!("{}\n \\\n \\", message_input.get_text().unwrap().as_str()));
        image_output.show();
    });

    window.show_all();
    image_output.hide();
}

內容解密:

  1. builder.get_object 方法用於取得輸入框、按鈕、輸出標籤和圖片元件。
  2. button.connect_clicked 方法用於為按鈕點選事件新增回呼函式。
  3. 在回呼函式中,讀取輸入框的文字,並設定輸出標籤的文字。
  4. 顯示圖片元件。

圖形使用者介面(GUI)開發中的變數生命週期問題與解決方案

在開發圖形使用者介面(GUI)應用程式時,開發者經常會遇到變數生命週期相關的問題。這些問題通常源於回呼函式(callback function)與其捕捉的變數之間的生命週期不匹配。本文將探討這類別問題,並提供具體的解決方案。

問題的根源

當在 Rust 中使用 GTK(GIMP Toolkit)或其他 GUI 框架時,開發者可能會遇到一個常見的錯誤:回呼函式可能比當前函式存活得更久,但它借用了某些變數。以下是一個典型的錯誤範例:

button.connect_clicked(|_| {
    // 使用了 image_output
    image_output.show();
});

編譯器會報錯,指出 image_output 可能在回呼函式執行時已經超出範圍。這是因為 button.connect_clicked 設定後,回呼函式可能會在應用程式的生命週期內被觸發多次,而 build_ui 函式可能早已執行完畢,導致 image_output 變數超出範圍。

解決方案:使用 moveclone

為瞭解決這個問題,可以使用 move 關鍵字將變數的所有權轉移到回呼函式中。但是,這樣做會導致原始變數無法再被使用。幸運的是,GTK-rs 對 GTK 物件的處理方式允許進行低成本的 clone 操作,只複製指標而不進行深複製。

let image_output: gtk::Image = builder.get_object("image_output").unwrap();
let image_output_clone = image_output.clone(); // 低成本的 clone

button.connect_clicked(move |_| {
    // 在回呼函式中使用 image_output_clone
    image_output_clone.show();
});

// 原始的 image_output 仍然可用
image_output.hide();

程式碼解密:

  1. let image_output_clone = image_output.clone();:對 image_output 進行低成本的 clone,建立一個新的指標指向同一個 GTK 物件。
  2. button.connect_clicked(move |_| { ... });:使用 moveimage_output_clone 的所有權轉移到回呼函式中,確保它在回呼函式執行時仍然有效。
  3. image_output_clone.show();:在回呼函式中顯示圖片。
  4. image_output.hide();:原始的 image_output 仍然有效,可以用來隱藏圖片。

處理其他元件

同樣地,如果需要在回呼函式中使用其他元件,如 gtk::Switch,可以按照類別似的方式處理。

let is_dead_switch: gtk::Switch = builder.get_object("is_dead_switch").unwrap();
let image_output_clone = image_output.clone();

button.connect_clicked(move |_| {
    let is_dead = is_dead_switch.get_active();
    if is_dead {
        image_output_clone.set_from_file("./images/cat_dead.png");
    } else {
        image_output_clone.set_from_file("./images/cat.png");
    }
    image_output_clone.show();
});

程式碼解密:

  1. let is_dead = is_dead_switch.get_active();:讀取 is_dead_switch 的狀態。
  2. image_output_clone.set_from_file("./images/cat_dead.png");:根據 is_dead 的值設定不同的圖片。
  3. image_output_clone.show();:顯示設定的圖片。

其他 GUI 函式庫的選擇

除了 GTK-rs,還有多種其他 GUI 函式庫可供選擇,如 Relm、Qt(透過 rust-qt 或 qmlrs)、conrod(Piston 遊戲引擎的一部分)、flutter-rs(Flutter 的 Rust 繫結)等。每個函式庫都有其特點和適用場景,開發者可以根據具體需求選擇最合適的工具。

未來發展趨勢

隨著 Rust 語言和其生態系統的不斷發展,GUI 程式設計領域也在不斷進步。新的函式庫和框架不斷湧現,為開發者提供了更多的選擇和可能性。未來,我們可以期待看到更多高效、安全、易用的 GUI 解決方案在 Rust 社群中出現。

用 Rust 開發遊戲:從基礎到實作

電子遊戲自早期以來已經有了長足的進步。以紅白機上的《超級瑪利歐兄弟》為例,它執行在 8 位元 CPU 上,時脈僅 1.79MHz,遊戲本體大小約為 31KB。如今,我們可以輕易獲得擁有 8 核心 CPU、每核心執行在 3.5GHz 的遊戲 PC,以及大小達到 50-70GB 的遊戲。這代表著計算能力的數千倍提升和儲存空間的數百萬倍增長。隨著遊戲變得越來越複雜,遊戲程式設計師的工作也變得更加艱鉅。

Rust 因其低階記憶體安全保證和卓越的效能,成為開發遊戲的理想選擇。Rust 的高效能使得它非常適合用於建立強壯且高效的遊戲引擎和遊戲。同時,其高階語法允許以乾淨和模組化的方式編寫遊戲邏輯。

建立 Amethyst 專案

在開始撰寫任何 Amethyst 程式碼之前,我們需要安裝一些 Amethyst 所需的系統函式庫。Amethyst 依賴於一些系統函式庫來處理聲音、字型渲染、XML 解析和密碼學等任務。因此,我們需要先安裝這些系統依賴項。以 Ubuntu 為例,可以執行以下命令來安裝所有必要的依賴項:

sudo apt install gcc pkg-config openssl libasound2-dev cmake build-essential python3 libfreetype6-dev libexpat1-dev libxcb-composite0-dev libssl-dev
sudo apt-get install libvulkan-dev mesa-vulkan-drivers vulkan-utils

內容解密:

  1. 安裝編譯器和構建工具gccbuild-essential 是用於編譯和構建程式的基礎工具。
  2. 安裝依賴函式庫libasound2-devlibfreetype6-devlibexpat1-devlibssl-dev 等函式庫分別用於處理聲音、字型渲染、XML 解析和密碼學。
  3. Vulkan 後端支援:為了使用 Vulkan 後端,需要安裝 libvulkan-devmesa-vulkan-driversvulkan-utils

實作「皮卡丘排球」遊戲

本章將帶領讀者重現經典的 Flash 遊戲「皮卡丘排球」。遊戲中,兩隻皮卡丘(寶可夢角色)在海灘上打排球。玩家可以使用鍵盤與電腦對戰或與其他玩家競爭。

遊戲功能

  • 2D 遊戲,一名玩家(貓)在左側,另一名玩家在右側。
  • 使用 WSAD 鍵控制左側玩家的移動,使用方向鍵控制右側玩家。
  • 球從中間發出,每位玩家必須使用頭部和身體將球擊回對手。
  • 球會因重力而彈跳和下落。
  • 當球觸及對手一側的地面時,玩家得分。
  • 包含音樂和音效。

Amethyst 與實體-元件-系統(ECS)模式

Amethyst 是一款根據實體-元件-系統(ECS)模式的遊戲引擎。ECS 是遊戲引擎設計中的一種架構模式,其核心思想是促進組合而非繼承。

ECS 架構

  1. 實體(Entities):遊戲中的物件,如玩家、怪物和樹木。
  2. 元件(Components):附加在實體上的屬性,如攻擊力、位置、碰撞偵測和生命值。
    • 例如:AttackTransformCollisionHealth 元件。
  3. 系統(Systems):更新元件的邏輯單元,如移動系統、輸入系統和碰撞偵測系統。

內容解密:

  • ECS 模式透過將實體的不同方面分離成元件,並使用系統來更新這些元件,從而使程式碼更加乾淨和結構化。
  • 這種架構有助於建立非常複雜的遊戲,因為它允許開發者輕鬆地管理和更新遊戲的各個方面。