简介
本文以Make Pong Game in Godot | gameidea为蓝本撰写。如果你对rust、godot等内容比较生疏或感到疑惑,可以先阅读godot-rust入门文档。
本文搭建在godot-rust(gdext)创建项目的基础之上,如果你对该部分内容感到生疏,可以先阅读这部分内容,为接下来的操作做好准备。出于连贯性考虑,本文将省略一些基础性操作,这些操作可以在godot-rust(gdext)你的第一个2D游戏这个系列中看到,可以从这个系列中了解相关内容。
注意,本文使用的godot版本为4.6.2,godot-rust(gdext)版本为0.5.1,相关的版本变化可能会导致一些内容失效。
本文所有操作均在Windows11环境下进行。
本文工程资源和相关素材可以在这里(pong-lab)可以获取。
正文
创建UI
// ui.rs
use std::collections::HashMap;
use godot::{
classes::{Button, CanvasLayer, ICanvasLayer, Label},
prelude::*,
};
use crate::state::{State, Stateful};
#[derive(GodotClass)]
#[class(init, base=CanvasLayer)]
pub struct UI {
base: Base<CanvasLayer>,
state: UIState,
#[init(val = Self::contents())]
contents: HashMap<UIState, Content>,
#[init(node = "Label")]
label: OnReady<Gd<Label>>,
#[init(node = "Button")]
button: OnReady<Gd<Button>>,
}
#[godot_api]
impl ICanvasLayer for UI {
fn ready(&mut self) {
self.button
.signals()
.button_up()
.connect_other(&*self, Self::on_button_up);
self.on_state_enter();
}
}
#[godot_api]
impl UI {
#[signal]
pub fn start_game();
#[signal]
pub fn resume_game();
fn contents() -> HashMap<UIState, Content> {
// 使用Model思路将内容部分和展示部分解耦
let mut map = HashMap::new();
map.insert(UIState::Title, ("Pong", "Play").into());
map.insert(UIState::Paused, ("Paused", "Resume").into());
map.insert(UIState::Victory, ("Win", "Replay").into());
map.insert(UIState::GameOver, ("Game Over", "Replay").into());
map
}
fn on_button_up(&mut self) {
match self.state { // 根据不同状态复用按钮,提供不同信号发射
UIState::Paused => self.signals().resume_game().emit(),
_ => self.signals().start_game().emit(),
}
}
}
struct Content {
label: &'static str,
button: &'static str,
}
impl From<(&'static str, &'static str)> for Content {
fn from(value: (&'static str, &'static str)) -> Self {
Self {
label: value.0,
button: value.1,
}
}
}
#[derive(PartialEq, Eq, Default, Clone, Copy, Hash)]
pub enum UIState {
#[default]
Title,
Paused,
Victory,
GameOver,
}
impl State for UIState {}
impl Stateful for UI {
type S = UIState;
fn on_state_enter(&mut self) {
if let Some(content) = self.contents.get(&self.state) {
// 进入状态即加载其应展示数据
self.label.set_text(content.label);
self.button.set_text(content.button);
}
}
fn on_state_exit(&mut self) {}
fn set_state(&mut self, new_state: Self::S) {
self.state = new_state;
}
fn state(&self) -> Self::S {
self.state
}
}
UI只提供UI功能和UI界面信号发送,我们不把控制逻辑放入UI。
UI只有两个元素,一个Lable,一个Button,编译rust后,我们在godot创建UI场景。
选中Label
- Text属性中可以填写一些文字来用于编辑展示,这里填写的时“Label”,这些文字在运行中将会被替换掉。
- 将Label顶部居中后,稍微再往下调整
20
- 检查器 > Control > Theme Override > Font Sizes > Font Sizes :
80 - 检查器 > Control > Theme Override > Styles > Normal :
StyleBoxFlat - 选中刚创建的
StyleBoxFlat,为其设置一个背景颜色 : Hex :000000b4,即半透明的黑色背景
与Label类似,选中Button,Text属性可填示意内容,这里填写的是“Button”,与上述相同的位置设置Font Size为80,锚点设置为居中。