diff --git a/godot/player.tscn b/godot/player.tscn index 4a5e4a4..0f3c9fa 100644 --- a/godot/player.tscn +++ b/godot/player.tscn @@ -3,4 +3,4 @@ [node name="TunnelsPlayer" type="TunnelsPlayer"] [node name="Camera3D" type="Camera3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 0.971276, 0.237957, 0, -0.237957, 0.971276, 0, 1.20073, 1.85333) +transform = Transform3D(-1, -6.99417e-08, 5.24489e-08, 0, 0.599946, 0.800041, -8.74227e-08, 0.800041, -0.599946, -2.38419e-07, 7.13901, -2.79085) diff --git a/godot/player_character.tscn b/godot/player_character.tscn new file mode 100644 index 0000000..b47acbc --- /dev/null +++ b/godot/player_character.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=3 format=3 uid="uid://dpda341t6ipiv"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3g72p"] +height = 1.59321 + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_rwcvu"] +height = 1.605 + +[node name="PlayerCharacter" type="PlayerCharacter"] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.802835, 0) +shape = SubResource("CapsuleShape3D_3g72p") + +[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8121, 0) +mesh = SubResource("CapsuleMesh_rwcvu") diff --git a/godot/test_level.tscn b/godot/test_level.tscn index 3d761a3..a6e9eab 100644 --- a/godot/test_level.tscn +++ b/godot/test_level.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=6 format=3 uid="uid://m36guasmi3c1"] +[gd_scene load_steps=9 format=3 uid="uid://m36guasmi3c1"] [ext_resource type="PackedScene" uid="uid://cqkbxe758jr7p" path="res://player.tscn" id="1_hv5rj"] @@ -8,6 +8,15 @@ game_state = SubResource("GameState_k4j3x") player_scene = ExtResource("1_hv5rj") +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_s4k5k"] + +[sub_resource type="Sky" id="Sky_oquga"] +sky_material = SubResource("ProceduralSkyMaterial_s4k5k") + +[sub_resource type="Environment" id="Environment_mt2l0"] +background_mode = 2 +sky = SubResource("Sky_oquga") + [sub_resource type="BoxMesh" id="BoxMesh_5glbk"] size = Vector3(20, 0.25, 20) @@ -17,10 +26,17 @@ size = Vector3(20, 0.25, 20) [node name="Level3D" type="Level3D"] game_mode_prototype = SubResource("TunnelsGameMode_itn7y") -[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_mt2l0") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="WorldEnvironment"] mesh = SubResource("BoxMesh_5glbk") +skeleton = NodePath("../..") -[node name="StaticBody3D" type="StaticBody3D" parent="."] +[node name="StaticBody3D" type="StaticBody3D" parent="WorldEnvironment"] -[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="WorldEnvironment/StaticBody3D"] shape = SubResource("BoxShape3D_kacqg") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="WorldEnvironment"] +transform = Transform3D(0.581243, 0.357328, -0.731077, 0, 0.898426, 0.439124, 0.81373, -0.255238, 0.522204, 0, 2.44259, 0) diff --git a/src/player_character.cpp b/src/player_character.cpp new file mode 100644 index 0000000..b1c0798 --- /dev/null +++ b/src/player_character.cpp @@ -0,0 +1,61 @@ +#include "player_character.hpp" +#include +#include "godot_cpp/variant/utility_functions.hpp" +#include "utils/godot_macros.h" + +namespace godot { +void PlayerCharacter::_bind_methods() { +#define CLASSNAME PlayerCharacter +} + +void PlayerCharacter::_enter_tree() { GDGAMEONLY(); + this->nav_agent = this->get_node("NavigationAgent3D"); + this->target_rotation = this->get_global_transform().get_basis().get_quaternion(); +} + +void PlayerCharacter::_process(double delta_time) { GDGAMEONLY(); + if(!this->mode_manual) { + this->process_ai(delta_time); + } +} + +void PlayerCharacter::_physics_process(double delta_time) { GDGAMEONLY(); + Vector3 const new_velocity = this->get_velocity().move_toward(this->velocity_target, delta_time * PlayerCharacter::ACCELERATION); + UtilityFunctions::print("Velocity target: ", this->velocity_target); + UtilityFunctions::print("Velocity current: ", this->get_velocity()); + UtilityFunctions::print("Velocity next: ", new_velocity); + this->set_velocity(new_velocity); + this->move_and_slide(); +} + +void PlayerCharacter::move(Vector3 world_vector) { + this->velocity_target = world_vector * PlayerCharacter::SPEED; +} + +void PlayerCharacter::aim(Vector3 at) { + Vector3 const position{this->get_global_position()}; + Vector3 const forward{(Vector3{at.x, 0.f, at.z} - Vector3{position.x, 0.f, position.z}).normalized()}; + Vector3 const up{0.f, 1.f, 0.f}; + this->target_rotation = {forward.cross(up), up, forward}; +} + +void PlayerCharacter::set_manual_mode(bool value) { + this->mode_manual = value; +} + +void PlayerCharacter::process_ai(double delta_time) { + this->velocity_target = this->nav_agent->get_velocity(); +} + +void PlayerCharacter::process_rotation(double delta_time) { + Transform3D current_trans{this->get_global_transform()}; + Basis const current_basis{current_trans.get_basis()}; + real_t const angle{current_basis.get_quaternion().angle_to(this->target_rotation.get_quaternion())}; + current_trans.set_basis(current_basis.lerp(this->target_rotation, PlayerCharacter::ROTATION_SPEED / angle * delta_time)); + this->set_global_transform(current_trans); +} + +float const PlayerCharacter::ACCELERATION{100.f}; +float const PlayerCharacter::SPEED{3.f}; +float const PlayerCharacter::ROTATION_SPEED{1.f}; +} diff --git a/src/player_character.hpp b/src/player_character.hpp new file mode 100644 index 0000000..98507fc --- /dev/null +++ b/src/player_character.hpp @@ -0,0 +1,34 @@ +#ifndef PLAYER_CHARACTER_HPP +#define PLAYER_CHARACTER_HPP + +#include + +namespace godot { +class NavigationAgent3D; + +class PlayerCharacter : public CharacterBody3D { + GDCLASS(PlayerCharacter, CharacterBody3D); + static void _bind_methods(); +public: + virtual void _enter_tree() override; + virtual void _process(double delta_time) override; + virtual void _physics_process(double delta_time) override; + void move(Vector3 world_vector); + void aim(Vector3 at); + void set_manual_mode(bool value); +protected: + void process_ai(double delta_time); + void process_rotation(double delta_time); +private: + Vector3 velocity_target{0.f,0.f,0.f}; + Basis target_rotation{}; + NavigationAgent3D *nav_agent{nullptr}; + bool mode_manual{false}; + + static float const ACCELERATION; + static float const SPEED; + static float const ROTATION_SPEED; +}; +} + +#endif // !PLAYER_CHARACTER_HPP diff --git a/src/register_types.cpp b/src/register_types.cpp index 13ca436..ac91adf 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -3,12 +3,13 @@ #include #include #include -#include "utils/game_root.hpp" #include "utils/game_mode.hpp" +#include "utils/game_root.hpp" #include "utils/game_state.hpp" #include "utils/level.hpp" -#include "utils/spawn_point.hpp" #include "utils/player_input.hpp" +#include "utils/spawn_point.hpp" +#include "player_character.hpp" #include "tunnels_game_mode.hpp" #include "tunnels_player.hpp" @@ -19,7 +20,7 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level) if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { return; } - ClassDB::register_class(); + ClassDB::register_abstract_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -28,6 +29,7 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level) ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); } extern "C" diff --git a/src/tunnels_player.cpp b/src/tunnels_player.cpp index b729d60..9f5f70d 100644 --- a/src/tunnels_player.cpp +++ b/src/tunnels_player.cpp @@ -1,12 +1,14 @@ #include "tunnels_player.hpp" +#include "godot_cpp/classes/resource_loader.hpp" +#include "player_character.hpp" +#include "utils/game_root.hpp" #include "utils/godot_macros.h" #include "utils/player_input.hpp" #include +#include #include namespace godot { -float const TunnelsPlayer::MOVE_SPEED{0.f}; - void TunnelsPlayer::_bind_methods() { #define CLASSNAME TunnelsPlayer GDFUNCTION_ARGS(horizontal_move_input, "event", "value"); @@ -15,11 +17,26 @@ void TunnelsPlayer::_bind_methods() { void TunnelsPlayer::_ready() { GDGAMEONLY(); this->camera = this->get_viewport()->get_camera_3d(); + this->initialize_character(); } -void TunnelsPlayer::_physics_process(double delta_time) { GDGAMEONLY(); - this->set_velocity(this->get_world_move_input()); - this->move_and_slide(); +void TunnelsPlayer::_exit_tree() { GDGAMEONLY(); + GameRoot::get_singleton()->remove_player(this->get_player_id()); +} + +void TunnelsPlayer::_process(double delta_time) { GDGAMEONLY(); + switch(this->state) { + default: + case State::ManualControl: + this->character->move(this->get_world_move_input().normalized()); + this->set_global_position(this->character->get_global_position()); + break; + case State::Tactics: + break; + case State::Overview: + this->set_global_position(this->get_global_position() + this->get_world_move_input().normalized()); + break; + } } void TunnelsPlayer::setup_player_input(PlayerInput *input) { @@ -39,9 +56,26 @@ void TunnelsPlayer::vertical_move_input(Ref event, float value) { this->move_input.y = value; } +void TunnelsPlayer::initialize_character() { + Ref player_scene = ResourceLoader::get_singleton()->load("res://player_character.tscn"); + if(player_scene.is_null() || !player_scene.is_valid()) + return; + if(player_scene->get_state()->get_node_type(0) != StringName("PlayerCharacter")) + return; + this->character = Object::cast_to(player_scene->instantiate()); + this->get_parent()->add_child(this->character); + this->character->set_global_transform(this->get_global_transform()); + this->character->set_manual_mode(true); +} + Vector3 TunnelsPlayer::get_world_move_input() const { Basis const camera_basis = camera->get_global_transform().get_basis(); - return this->move_input.x * camera_basis.get_column(0) - + this->move_input.y * camera_basis.get_column(2); + // get the forward and left vectors, ensuring that they won't produce {0,0,0} when flattened + Vector3 x = camera_basis.get_column(0); + if(x.x == 0.f && x.z == 0.f) x = camera_basis.get_column(1); + Vector3 z = camera_basis.get_column(2); + if(z.x == 0.f && z.z == 0.f) z = camera_basis.get_column(1); + return this->move_input.x * Vector3{x.x, 0.f, x.z}.normalized() + + this->move_input.y * Vector3{z.x, 0.f, z.y}.normalized(); } } diff --git a/src/tunnels_player.hpp b/src/tunnels_player.hpp index 01516f9..cbe6c69 100644 --- a/src/tunnels_player.hpp +++ b/src/tunnels_player.hpp @@ -8,13 +8,22 @@ #include namespace godot { -class TunnelsPlayer : public CharacterBody3D, public IPlayer { - GDCLASS(TunnelsPlayer, CharacterBody3D); +class PlayerCharacter; + +class TunnelsPlayer : public Node3D, public IPlayer { + GDCLASS(TunnelsPlayer, Node3D); static void _bind_methods(); + enum State { + ManualControl = 0x0, + Tactics = 0x1, + Overview = 0x2, + }; + public: virtual void _ready() override; - virtual void _physics_process(double delta_time) override; + virtual void _exit_tree() override; + virtual void _process(double delta_time) override; virtual void setup_player_input(PlayerInput *input) override; virtual Node *to_node() override; @@ -22,13 +31,16 @@ public: void horizontal_move_input(Ref event, float value); void vertical_move_input(Ref event, float value); + void initialize_character(); + Vector3 get_world_move_input() const; private: Vector2 move_input{0,0}; + Vector2 mouse_location{0,0}; + PlayerCharacter *character{nullptr}; + TunnelsPlayer::State state{State::ManualControl}; Camera3D *camera{nullptr}; -public: - static float const MOVE_SPEED; }; }