Add simple main menu

This commit is contained in:
Sofia 2026-07-23 18:47:21 +03:00
parent 1ac19b9816
commit d2da80f64c
5 changed files with 359 additions and 14 deletions

View File

@ -1,24 +1,185 @@
[gd_scene format=3 uid="uid://lahirbqfppvw"]
[sub_resource type="BoxMesh" id="BoxMesh_m77kh"]
[ext_resource type="PackedScene" uid="uid://bbmc2gmy7g8ag" path="res://scenes/misc/menu_player.tscn" id="1_o5qli"]
[node name="node_3d" type="Node3D" unique_id=591926555]
[node name="menu" type="MainMenu" unique_id=2036763876 node_paths=PackedStringArray("camera", "main_position", "host_position", "main_panel", "host_panel", "join_panel", "host_port", "join_addr")]
camera = NodePath("camera_3d")
main_position = NodePath("main_pos")
host_position = NodePath("host_pos")
main_panel = NodePath("main_panel")
host_panel = NodePath("host_panel")
join_panel = NodePath("join_panel")
host_port = NodePath("host_panel/v_box_container/host_port")
join_addr = NodePath("join_panel/v_box_container/address_text")
[node name="camera_3d" type="Camera3D" parent="." unique_id=1513321931]
transform = Transform3D(0.85754585, -0.09722054, 0.50513697, 0, 0.98197806, 0.18899518, -0.5144076, -0.16207202, 0.8420912, 1.0974057, 0.86883736, 1.7596759)
[node name="mesh_instance_3d" type="MeshInstance3D" parent="." unique_id=1466304853]
mesh = SubResource("BoxMesh_m77kh")
transform = Transform3D(0.98629165, -0.031186381, 0.16203775, -1.4054481e-08, 0.981978, 0.1889952, -0.16501158, -0.18640438, 0.9685167, 1.0974052, 1.7166122, 1.4909106)
current = true
[node name="directional_light_3d" type="DirectionalLight3D" parent="." unique_id=75522463]
transform = Transform3D(0.61237246, -0.5, 0.61237246, 0.3535534, 0.8660254, 0.3535534, -0.7071068, 0, 0.7071068, 0, 1, 2)
[node name="netstats" type="NetStatsLabel" parent="." unique_id=1366169487]
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -144.0
[node name="menu_player" parent="." unique_id=928718358 instance=ExtResource("1_o5qli")]
transform = Transform3D(0.66809446, 0, -0.74407655, 0, 1, 0, 0.74407655, 0, 0.66809446, 2.270261, -2.3841858e-07, -1.3596804)
[node name="menu_player2" parent="." unique_id=1735741962 instance=ExtResource("1_o5qli")]
transform = Transform3D(0.9823497, 0, 0.18705386, 0, 1, 0, -0.18705386, 0, 0.9823497, -3.3954296, 1.1920929e-06, 0.47203088)
[node name="menu_player3" parent="." unique_id=1638403481 instance=ExtResource("1_o5qli")]
transform = Transform3D(-0.26696783, 0, 0.9637054, 0, 1, 0, -0.9637054, 0, -0.26696783, -3.5550833, -2.3841858e-07, 2.7302406)
[node name="main_panel" type="Panel" parent="." unique_id=2078435804]
offset_left = 139.0
offset_top = 167.0
offset_right = 336.0
offset_bottom = 474.0
[node name="v_box_container" type="VBoxContainer" parent="main_panel" unique_id=287461844]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -47.0
offset_top = -95.0
offset_right = 47.0
offset_bottom = 95.0
grow_horizontal = 2
grow_vertical = 0
vertical_alignment = 2
grow_vertical = 2
alignment = 1
[node name="label" type="Label" parent="main_panel/v_box_container" unique_id=140542377]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
text = "QuakeBall"
horizontal_alignment = 1
metadata/_edit_lock_ = true
[node name="host" type="Button" parent="main_panel/v_box_container" unique_id=1962091117]
layout_mode = 2
text = "Host Game"
metadata/_edit_lock_ = true
[node name="join" type="Button" parent="main_panel/v_box_container" unique_id=1364459397]
layout_mode = 2
text = "Join Game"
metadata/_edit_lock_ = true
[node name="settings" type="Button" parent="main_panel/v_box_container" unique_id=1153899756]
layout_mode = 2
text = "Settings"
metadata/_edit_lock_ = true
[node name="quit" type="Button" parent="main_panel/v_box_container" unique_id=1508139096]
layout_mode = 2
text = "Quit"
metadata/_edit_lock_ = true
[node name="host_panel" type="Panel" parent="." unique_id=1633876079]
visible = false
offset_left = 139.0
offset_top = 167.0
offset_right = 336.0
offset_bottom = 474.0
[node name="v_box_container" type="VBoxContainer" parent="host_panel" unique_id=1651169053]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -47.0
offset_top = -95.0
offset_right = 47.0
offset_bottom = 95.0
grow_horizontal = 2
grow_vertical = 2
alignment = 1
[node name="title" type="Label" parent="host_panel/v_box_container" unique_id=1539421918]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
text = "Host Game"
horizontal_alignment = 1
metadata/_edit_lock_ = true
[node name="port_label" type="Label" parent="host_panel/v_box_container" unique_id=1618746525]
layout_mode = 2
text = "Port"
horizontal_alignment = 1
[node name="host_port" type="SpinBox" parent="host_panel/v_box_container" unique_id=1116593197]
layout_mode = 2
max_value = 16000.0
value = 8080.0
alignment = 1
[node name="host" type="Button" parent="host_panel/v_box_container" unique_id=312403378]
layout_mode = 2
text = "Host"
[node name="back" type="Button" parent="host_panel/v_box_container" unique_id=1658922684]
layout_mode = 2
text = "Back"
[node name="join_panel" type="Panel" parent="." unique_id=1630672771]
visible = false
offset_left = 139.0
offset_top = 167.0
offset_right = 336.0
offset_bottom = 474.0
[node name="v_box_container" type="VBoxContainer" parent="join_panel" unique_id=2029384993]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -47.0
offset_top = -95.0
offset_right = 47.0
offset_bottom = 95.0
grow_horizontal = 2
grow_vertical = 2
alignment = 1
[node name="title" type="Label" parent="join_panel/v_box_container" unique_id=268455606]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
text = "Join Game"
horizontal_alignment = 1
metadata/_edit_lock_ = true
[node name="address_label" type="Label" parent="join_panel/v_box_container" unique_id=425811384]
layout_mode = 2
text = "Address"
horizontal_alignment = 1
[node name="address_text" type="TextEdit" parent="join_panel/v_box_container" unique_id=584304528]
custom_minimum_size = Vector2(150, 30)
layout_mode = 2
[node name="join" type="Button" parent="join_panel/v_box_container" unique_id=1953386108]
layout_mode = 2
text = "Join"
[node name="back" type="Button" parent="join_panel/v_box_container" unique_id=759190425]
layout_mode = 2
text = "Back"
[node name="main_pos" type="Node3D" parent="." unique_id=98241074]
transform = Transform3D(0.98629165, -0.031186387, 0.16203767, -1.4054484e-08, 0.981978, 0.1889952, -0.16501155, -0.18640435, 0.9685167, 1.0974054, 1.7166122, 1.4909104)
[node name="host_pos" type="Node3D" parent="." unique_id=663794107]
transform = Transform3D(0.3960592, -0.17354009, 0.90167665, -1.9851193e-08, 0.981978, 0.18899521, -0.9182249, -0.0748533, 0.38892144, -1.0602233, 1.7166122, 3.759781)
[connection signal="pressed" from="main_panel/v_box_container/host" to="." method="go_to_host_game"]
[connection signal="pressed" from="main_panel/v_box_container/join" to="." method="go_to_join_game"]
[connection signal="pressed" from="main_panel/v_box_container/quit" to="." method="quit_game"]
[connection signal="pressed" from="host_panel/v_box_container/host" to="." method="host_game"]
[connection signal="pressed" from="host_panel/v_box_container/back" to="." method="go_to_main_menu"]
[connection signal="pressed" from="join_panel/v_box_container/join" to="." method="join_game"]
[connection signal="pressed" from="join_panel/v_box_container/back" to="." method="go_to_main_menu"]

View File

@ -0,0 +1,9 @@
[gd_scene format=3 uid="uid://bbmc2gmy7g8ag"]
[ext_resource type="PackedScene" uid="uid://bod6b3befmyt5" path="res://scenes/player/soldier_m.tscn" id="1_pgypt"]
[node name="menu_player" type="MenuPlayer" unique_id=928718358 node_paths=PackedStringArray("mesh")]
mesh = NodePath("soldier_m")
[node name="soldier_m" parent="." unique_id=281995630 instance=ExtResource("1_pgypt")]
transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0)

152
rust/src/ui/main_menu.rs Normal file
View File

@ -0,0 +1,152 @@
use godot::{
classes::{Camera3D, Panel, SpinBox, TextEdit},
prelude::*,
register::property::SimpleVar,
};
use crate::net::network_manager::NetworkManager;
#[derive(Debug, Clone, GodotConvert, Default, Export)]
#[godot(via = u8)]
pub enum Menu {
#[default]
MainMenu,
HostMenu,
JoinMenu,
}
impl SimpleVar for Menu {}
#[derive(GodotClass)]
#[class(base=Node3D, init, tool)]
pub struct MainMenu {
#[export]
camera: Option<Gd<Camera3D>>,
#[export]
main_position: Option<Gd<Node3D>>,
#[export]
host_position: Option<Gd<Node3D>>,
#[export]
main_panel: Option<Gd<Panel>>,
#[export]
host_panel: Option<Gd<Panel>>,
#[export]
join_panel: Option<Gd<Panel>>,
#[export]
host_port: Option<Gd<SpinBox>>,
#[export]
join_addr: Option<Gd<TextEdit>>,
#[export]
current_menu: Menu,
prev_panel: Option<Gd<Panel>>,
base: Base<Node3D>,
}
#[godot_api]
impl INode3D for MainMenu {
fn process(&mut self, _delta: f64) {
if let Some(camera) = &mut self.camera {
let target = match self.current_menu {
Menu::MainMenu => {
if let Some(pos) = &self.main_position {
Some((
pos.get_global_position(),
pos.get_global_rotation(),
self.main_panel.clone(),
))
} else {
None
}
}
Menu::HostMenu => {
if let Some(pos) = &self.host_position {
Some((
pos.get_global_position(),
pos.get_global_rotation(),
self.host_panel.clone(),
))
} else {
None
}
}
Menu::JoinMenu => {
if let Some(pos) = &self.host_position {
Some((
pos.get_global_position(),
pos.get_global_rotation(),
self.join_panel.clone(),
))
} else {
None
}
}
};
if let Some((target_pos, target_rot, target_panel)) = target {
let prev_pos = camera.get_global_position();
let prev_rot = camera.get_global_rotation();
let new_pos = prev_pos.lerp(target_pos, 0.2);
let new_rot = prev_rot.lerp(target_rot, 0.2);
camera.set_global_position(new_pos);
camera.set_global_rotation(new_rot);
if let Some(mut panel) = target_panel {
if let Some(prev_panel) = &mut self.prev_panel {
if prev_panel.instance_id() != panel.instance_id() {
prev_panel.set_visible(false);
}
}
self.prev_panel = Some(panel.clone());
panel.set_visible(true);
}
}
}
}
}
#[godot_api]
impl MainMenu {
#[func]
pub fn host_game(&mut self) {
if let Some(host_port) = &self.host_port {
NetworkManager::singleton()
.bind_mut()
.host(host_port.get_value() as u16);
}
}
#[func]
pub fn join_game(&mut self) {
if let Some(addr) = &self.join_addr {
if let Ok(addr) = addr.get_text().to_string().parse() {
NetworkManager::singleton().bind_mut().join(addr);
}
}
}
#[func]
pub fn go_to_main_menu(&mut self) {
self.current_menu = Menu::MainMenu;
}
#[func]
pub fn go_to_host_game(&mut self) {
self.current_menu = Menu::HostMenu;
}
#[func]
pub fn go_to_join_game(&mut self) {
self.current_menu = Menu::JoinMenu;
}
#[func]
pub fn quit_game(&mut self) {
self.base().get_tree().quit();
}
}

View File

@ -0,0 +1,21 @@
use godot::prelude::*;
use crate::soldier_mesh::SoldierMesh;
#[derive(GodotClass)]
#[class(base=Node3D, init)]
pub struct MenuPlayer {
#[export]
mesh: Option<Gd<SoldierMesh>>,
base: Base<Node3D>,
}
#[godot_api]
impl INode3D for MenuPlayer {
fn ready(&mut self) {
if let Some(mesh) = &mut self.mesh {
mesh.bind_mut().spawn_raygun();
}
}
}

View File

@ -2,6 +2,8 @@ pub mod cli;
pub mod game_finished_screen;
pub mod lobby;
pub mod lobby_player_listing;
pub mod main_menu;
pub mod menu_player;
pub mod net_stats;
pub mod player_listing;
pub mod simple_score_view;