feat: fundamental movement and separation of concerns for camera and character motion
This commit is contained in:
		
							parent
							
								
									121bb0b7d6
								
							
						
					
					
						commit
						709b972f91
					
				| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								godot/player_character.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								godot/player_character.tscn
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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")
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										61
									
								
								src/player_character.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/player_character.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
#include "player_character.hpp"
 | 
			
		||||
#include <godot_cpp/classes/navigation_agent3d.hpp>
 | 
			
		||||
#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>("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};
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								src/player_character.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/player_character.hpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
#ifndef PLAYER_CHARACTER_HPP
 | 
			
		||||
#define PLAYER_CHARACTER_HPP
 | 
			
		||||
 | 
			
		||||
#include <godot_cpp/classes/character_body3d.hpp>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			@ -3,12 +3,13 @@
 | 
			
		|||
#include <godot_cpp/core/class_db.hpp>
 | 
			
		||||
#include <godot_cpp/core/defs.hpp>
 | 
			
		||||
#include <godot_cpp/godot.hpp>
 | 
			
		||||
#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<GameRoot>();
 | 
			
		||||
    ClassDB::register_abstract_class<GameRoot>();
 | 
			
		||||
    ClassDB::register_class<GameRoot3D>();
 | 
			
		||||
    ClassDB::register_class<SpawnPoint3D>();
 | 
			
		||||
    ClassDB::register_class<PlayerInput>();
 | 
			
		||||
| 
						 | 
				
			
			@ -28,6 +29,7 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level)
 | 
			
		|||
    ClassDB::register_class<Level3D>();
 | 
			
		||||
    ClassDB::register_class<TunnelsGameMode>();
 | 
			
		||||
    ClassDB::register_class<TunnelsPlayer>();
 | 
			
		||||
    ClassDB::register_class<PlayerCharacter>();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 <godot_cpp/classes/input_event.hpp>
 | 
			
		||||
#include <godot_cpp/classes/scene_state.hpp>
 | 
			
		||||
#include <godot_cpp/classes/viewport.hpp>
 | 
			
		||||
 | 
			
		||||
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<InputEvent> event, float value) {
 | 
			
		|||
    this->move_input.y = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TunnelsPlayer::initialize_character() {
 | 
			
		||||
    Ref<PackedScene> 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<PlayerCharacter>(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();
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,13 +8,22 @@
 | 
			
		|||
#include <godot_cpp/classes/input_event.hpp>
 | 
			
		||||
 | 
			
		||||
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<InputEvent> event, float value);
 | 
			
		||||
    void vertical_move_input(Ref<InputEvent> 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;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue