Implement videoplayer feature for replay
This commit is contained in:
parent
c5880c4b1a
commit
620ed980a2
18
godot/mesh/replay_telegrenade.tscn
Normal file
18
godot/mesh/replay_telegrenade.tscn
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[gd_scene format=3 uid="uid://cnqxjag3wwtno"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bjnojwqmmv74y" path="res://assets/Telegrenade.fbx" id="1_o7yty"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_sugp2"]
|
||||||
|
albedo_color = Color(0, 0, 1, 1)
|
||||||
|
emission_enabled = true
|
||||||
|
emission = Color(0, 0, 0.96862745, 1)
|
||||||
|
emission_energy_multiplier = 5.0
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_jyhfs"]
|
||||||
|
albedo_color = Color(0.30692267, 0.3069227, 0.3069227, 1)
|
||||||
|
|
||||||
|
[node name="Telegrenade" unique_id=1761178296 instance=ExtResource("1_o7yty")]
|
||||||
|
|
||||||
|
[node name="Cube" parent="." index="0" unique_id=218178446]
|
||||||
|
surface_material_override/0 = SubResource("StandardMaterial3D_sugp2")
|
||||||
|
surface_material_override/1 = SubResource("StandardMaterial3D_jyhfs")
|
||||||
Binary file not shown.
Binary file not shown.
@ -3,8 +3,10 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://d3377ecff6nyn" path="res://scenes/replay_finished.tscn" id="1_q5ujf"]
|
[ext_resource type="PackedScene" uid="uid://d3377ecff6nyn" path="res://scenes/replay_finished.tscn" id="1_q5ujf"]
|
||||||
[ext_resource type="PackedScene" uid="uid://c1gs6cqek1qlq" path="res://scenes/ui/hud.tscn" id="2_a451s"]
|
[ext_resource type="PackedScene" uid="uid://c1gs6cqek1qlq" path="res://scenes/ui/hud.tscn" id="2_a451s"]
|
||||||
[ext_resource type="PackedScene" uid="uid://lahirbqfppvw" path="res://scenes/main.tscn" id="2_r7g00"]
|
[ext_resource type="PackedScene" uid="uid://lahirbqfppvw" path="res://scenes/main.tscn" id="2_r7g00"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://coqf6cn4hntqi" path="res://scenes/ui/replay_hud.tscn" id="4_v7405"]
|
||||||
|
|
||||||
[node name="standalone_replay_global" type="StandaloneReplayManager" unique_id=730657724]
|
[node name="standalone_replay_global" type="StandaloneReplayManager" unique_id=730657724]
|
||||||
replay_finished_screen = ExtResource("1_q5ujf")
|
replay_finished_screen = ExtResource("1_q5ujf")
|
||||||
main_menu = ExtResource("2_r7g00")
|
main_menu = ExtResource("2_r7g00")
|
||||||
hud_scene = ExtResource("2_a451s")
|
hud_scene = ExtResource("2_a451s")
|
||||||
|
replay_hud_scene = ExtResource("4_v7405")
|
||||||
|
|||||||
61
godot/scenes/ui/replay_hud.tscn
Normal file
61
godot/scenes/ui/replay_hud.tscn
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
[gd_scene format=3 uid="uid://coqf6cn4hntqi"]
|
||||||
|
|
||||||
|
[node name="replay_hud" type="ReplayHud" unique_id=899758035 node_paths=PackedStringArray("current_time", "end_time", "slider")]
|
||||||
|
current_time = NodePath("panel/h_box_container/label")
|
||||||
|
end_time = NodePath("panel/h_box_container/label2")
|
||||||
|
slider = NodePath("panel/h_box_container/h_slider")
|
||||||
|
anchors_preset = 7
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
|
||||||
|
[node name="panel" type="Panel" parent="." unique_id=389733281]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 7
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -279.0
|
||||||
|
offset_top = -152.0
|
||||||
|
offset_right = 281.0
|
||||||
|
offset_bottom = -49.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
|
||||||
|
[node name="h_box_container" type="HBoxContainer" parent="panel" unique_id=343157918]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
alignment = 1
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|
||||||
|
[node name="label" type="Label" parent="panel/h_box_container" unique_id=1140720179]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
custom_maximum_size = Vector2(50, -1)
|
||||||
|
clip_contents = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "0:00"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|
||||||
|
[node name="h_slider" type="HSlider" parent="panel/h_box_container" unique_id=605407748]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 4
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
|
|
||||||
|
[node name="label2" type="Label" parent="panel/h_box_container" unique_id=1489275389]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
custom_maximum_size = Vector2(50, -1)
|
||||||
|
clip_contents = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "0:00"
|
||||||
|
horizontal_alignment = 1
|
||||||
|
metadata/_edit_lock_ = true
|
||||||
@ -18,7 +18,7 @@ use crate::{
|
|||||||
DamageSource, IPlayer, NetTransform, ball::Ball, remote_player::RemotePlayer,
|
DamageSource, IPlayer, NetTransform, ball::Ball, remote_player::RemotePlayer,
|
||||||
telegrenade::Telegrenade, weapon::WeaponType,
|
telegrenade::Telegrenade, weapon::WeaponType,
|
||||||
},
|
},
|
||||||
ui::hud::Hud,
|
ui::{hud::Hud, replay_hud::ReplayHud},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const STANDALONE_REPLAY_GLOBAL: &str = "StandaloneReplayGlobal";
|
pub const STANDALONE_REPLAY_GLOBAL: &str = "StandaloneReplayGlobal";
|
||||||
@ -32,16 +32,36 @@ pub struct StandaloneReplayManager {
|
|||||||
main_menu: Option<Gd<PackedScene>>,
|
main_menu: Option<Gd<PackedScene>>,
|
||||||
#[export]
|
#[export]
|
||||||
hud_scene: Option<Gd<PackedScene>>,
|
hud_scene: Option<Gd<PackedScene>>,
|
||||||
|
#[export]
|
||||||
|
replay_hud_scene: Option<Gd<PackedScene>>,
|
||||||
|
|
||||||
|
end_time: f64,
|
||||||
loading_replay: bool,
|
loading_replay: bool,
|
||||||
map: Option<Gd<Map>>,
|
map: Option<Gd<Map>>,
|
||||||
pub playback: Option<Gd<ReplayPlayback>>,
|
pub playback: Option<Gd<ReplayPlayback>>,
|
||||||
replay: Option<Replay>,
|
replay: Option<Replay>,
|
||||||
hud: Option<Gd<Hud>>,
|
hud: Option<Gd<Hud>>,
|
||||||
|
replay_hud: Option<Gd<ReplayHud>>,
|
||||||
|
since_last_update: f64,
|
||||||
|
|
||||||
base: Base<Node>,
|
base: Base<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl INode for StandaloneReplayManager {
|
||||||
|
fn process(&mut self, delta: f64) {
|
||||||
|
self.since_last_update += delta;
|
||||||
|
if self.since_last_update >= 1.
|
||||||
|
&& let Some(hud) = &mut self.replay_hud
|
||||||
|
&& let Some(playback) = &self.playback
|
||||||
|
{
|
||||||
|
self.since_last_update = 0.;
|
||||||
|
hud.bind_mut()
|
||||||
|
.update_times(playback.bind().current_time, self.end_time, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[godot_api]
|
#[godot_api]
|
||||||
impl StandaloneReplayManager {
|
impl StandaloneReplayManager {
|
||||||
#[signal]
|
#[signal]
|
||||||
@ -87,6 +107,7 @@ impl StandaloneReplayManager {
|
|||||||
.on_finished()
|
.on_finished()
|
||||||
.connect_other(self, Self::on_done);
|
.connect_other(self, Self::on_done);
|
||||||
|
|
||||||
|
self.end_time = replay.find_ending();
|
||||||
playback.bind_mut().replay = replay;
|
playback.bind_mut().replay = replay;
|
||||||
playback.bind_mut().current_map = Some(map.clone());
|
playback.bind_mut().current_map = Some(map.clone());
|
||||||
playback.bind_mut().current_time = 0.;
|
playback.bind_mut().current_time = 0.;
|
||||||
@ -96,7 +117,7 @@ impl StandaloneReplayManager {
|
|||||||
self.base_mut().add_child(&playback);
|
self.base_mut().add_child(&playback);
|
||||||
|
|
||||||
if let Some(scene) = &self.hud_scene {
|
if let Some(scene) = &self.hud_scene {
|
||||||
let hud = scene.instantiate_as::<Hud>();
|
let mut hud = scene.instantiate_as::<Hud>();
|
||||||
|
|
||||||
hud.signals().on_exit_to_lobby().connect_other(self, |s| {
|
hud.signals().on_exit_to_lobby().connect_other(self, |s| {
|
||||||
s.on_done();
|
s.on_done();
|
||||||
@ -109,6 +130,20 @@ impl StandaloneReplayManager {
|
|||||||
|
|
||||||
self.hud = Some(hud.clone());
|
self.hud = Some(hud.clone());
|
||||||
playback.add_child(&hud);
|
playback.add_child(&hud);
|
||||||
|
|
||||||
|
if let Some(scene) = &self.replay_hud_scene {
|
||||||
|
let mut replay_hud = scene.instantiate_as::<ReplayHud>();
|
||||||
|
|
||||||
|
replay_hud.bind_mut().update_times(0., self.end_time, true);
|
||||||
|
|
||||||
|
replay_hud
|
||||||
|
.signals()
|
||||||
|
.on_time_changed()
|
||||||
|
.connect_other(self, |s, new_time| s.fast_forward_to(new_time));
|
||||||
|
|
||||||
|
self.replay_hud = Some(replay_hud.clone());
|
||||||
|
hud.add_child(&replay_hud);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,6 +162,9 @@ impl StandaloneReplayManager {
|
|||||||
if let Some(mut hud) = self.hud.take() {
|
if let Some(mut hud) = self.hud.take() {
|
||||||
hud.queue_free();
|
hud.queue_free();
|
||||||
}
|
}
|
||||||
|
if let Some(mut hud) = self.replay_hud.take() {
|
||||||
|
hud.queue_free();
|
||||||
|
}
|
||||||
if let Some(mut playback) = self.playback.take() {
|
if let Some(mut playback) = self.playback.take() {
|
||||||
playback.queue_free();
|
playback.queue_free();
|
||||||
}
|
}
|
||||||
@ -137,6 +175,14 @@ impl StandaloneReplayManager {
|
|||||||
self.base().get_tree().change_scene_to_packed(scene);
|
self.base().get_tree().change_scene_to_packed(scene);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fast_forward_to(&mut self, new_time: f64) {
|
||||||
|
if let Some(playback) = &mut self.playback {
|
||||||
|
playback.bind_mut().current_time = new_time;
|
||||||
|
playback.bind_mut().current_frame_idx = 0;
|
||||||
|
playback.bind_mut().fast_forwarding = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
@ -564,6 +610,14 @@ impl Replay {
|
|||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_ending(&self) -> f64 {
|
||||||
|
if let Some(frame) = self.frames.last() {
|
||||||
|
frame.time
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@ -11,6 +11,7 @@ pub mod net_stats;
|
|||||||
pub mod player_listing;
|
pub mod player_listing;
|
||||||
pub mod player_listings;
|
pub mod player_listings;
|
||||||
pub mod replay_finished_screen;
|
pub mod replay_finished_screen;
|
||||||
|
pub mod replay_hud;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod simple_score_view;
|
pub mod simple_score_view;
|
||||||
pub mod slider;
|
pub mod slider;
|
||||||
|
|||||||
70
rust/src/ui/replay_hud.rs
Normal file
70
rust/src/ui/replay_hud.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use godot::{
|
||||||
|
classes::{Control, IControl, Label, Slider},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(GodotClass)]
|
||||||
|
#[class(base=Control, init)]
|
||||||
|
pub struct ReplayHud {
|
||||||
|
#[export]
|
||||||
|
pub current_time: Option<Gd<Label>>,
|
||||||
|
#[export]
|
||||||
|
pub end_time: Option<Gd<Label>>,
|
||||||
|
#[export]
|
||||||
|
pub slider: Option<Gd<Slider>>,
|
||||||
|
|
||||||
|
dragging: bool,
|
||||||
|
|
||||||
|
base: Base<Control>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl IControl for ReplayHud {
|
||||||
|
fn ready(&mut self) {
|
||||||
|
if let Some(slider) = self.slider.clone() {
|
||||||
|
slider
|
||||||
|
.signals()
|
||||||
|
.drag_started()
|
||||||
|
.connect_other(self, |s| s.dragging = true);
|
||||||
|
slider
|
||||||
|
.signals()
|
||||||
|
.drag_ended()
|
||||||
|
.connect_other(self, |s, _| s.dragging = false);
|
||||||
|
slider
|
||||||
|
.signals()
|
||||||
|
.value_changed()
|
||||||
|
.connect_other(self, |s, new_value| s.on_change(new_value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[godot_api]
|
||||||
|
impl ReplayHud {
|
||||||
|
#[signal]
|
||||||
|
pub fn on_time_changed(new_time: f64);
|
||||||
|
|
||||||
|
fn on_change(&mut self, new_value: f64) {
|
||||||
|
if self.dragging {
|
||||||
|
if let Some(current_time_label) = &mut self.current_time {
|
||||||
|
current_time_label.set_text(&format!("{:.2}", new_value));
|
||||||
|
}
|
||||||
|
self.signals().on_time_changed().emit(new_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_times(&mut self, current_time: f64, end_time: f64, set_value: bool) {
|
||||||
|
if let Some(current_time_label) = &mut self.current_time {
|
||||||
|
current_time_label.set_text(&format!("{:.2}", current_time));
|
||||||
|
}
|
||||||
|
if let Some(end_time_label) = &mut self.end_time {
|
||||||
|
end_time_label.set_text(&format!("{:.2}", end_time));
|
||||||
|
}
|
||||||
|
if let Some(slider) = &mut self.slider {
|
||||||
|
slider.set_min(0.);
|
||||||
|
slider.set_max(end_time);
|
||||||
|
if set_value && !self.dragging {
|
||||||
|
slider.set_value(current_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user