feat: implemented basic third person movement

This commit is contained in:
Sara 2025-07-04 16:01:56 +02:00
parent 062633c742
commit f7e07797c8
15 changed files with 278 additions and 764 deletions

View file

@ -7,6 +7,16 @@ void ActorBody::_bind_methods() {
BIND_HPROPERTY(Variant::FLOAT, movement_speed, PROPERTY_HINT_RANGE, "0.0,20.0");
}
void ActorBody::physics_process(double delta) {
if (this->teleport_on_process) {
set_global_position(this->teleport_target);
this->teleport_on_process = false;
force_update_transform();
}
set_velocity(get_movement_direction() * this->movement_speed);
move_and_slide();
}
void ActorBody::_notification(int what) {
if (Engine::get_singleton()->is_editor_hint()) {
return;
@ -14,20 +24,18 @@ void ActorBody::_notification(int what) {
switch (what) {
default:
return;
case NOTIFICATION_READY:
set_physics_process(true);
set_as_top_level(true);
case NOTIFICATION_PHYSICS_PROCESS:
physics_process(get_physics_process_delta_time());
return;
}
}
void ActorBody::physics_process(double delta) {
set_velocity(get_movement_direction() * this->movement_speed);
move_and_slide();
}
void ActorBody::set_movement_direction(Vector3 direction) {
this->mode = Direction;
this->movement_vector = { direction.x, 0.f, direction.y };
this->movement_vector = Vector3(direction.x, 0.f, direction.z).normalized();
}
Vector3 ActorBody::get_movement_direction() const {
@ -43,7 +51,7 @@ Vector3 ActorBody::get_movement_direction() const {
void ActorBody::set_movement_target(Vector3 location) {
this->mode = Position;
this->movement_vector = { location.x, 0.f, location.y };
this->movement_vector = { location.x, 0.f, location.z };
}
Vector3 ActorBody::get_movement_target() const {
@ -55,6 +63,11 @@ Vector3 ActorBody::get_movement_target() const {
}
}
void ActorBody::teleport(Vector3 target) {
this->teleport_target = target;
this->teleport_on_process = true;
}
void ActorBody::set_movement_speed(float speed) {
this->movement_speed = speed;
}

View file

@ -20,15 +20,18 @@ public:
Vector3 get_movement_direction() const;
void set_movement_target(Vector3 location);
Vector3 get_movement_target() const;
void teleport(Vector3 target);
void set_movement_speed(float speed);
float get_movement_speed() const;
MovementMode get_movement_mode() const;
private:
float movement_speed{ 1.f };
float movement_speed{ 3.f };
Vector3 movement_vector{ 0.f, 0.f, 0.f };
MovementMode mode{ Direction };
bool teleport_on_process{ false };
Vector3 teleport_target{};
};
#endif //! ACTOR_BODY_H

View file

@ -3,14 +3,7 @@
GameState *GameState::singleton_instance{ nullptr };
void GameState::_bind_methods() {
BIND_PROPERTY(Variant::STRING, locale_uid);
BIND_PROPERTY(Variant::STRING, locale_entrance_path);
{
String const party_member_data_hint{ vformat("%s/%s:PartyMemberData", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE) };
BIND_HPROPERTY(Variant::ARRAY, player_party, PROPERTY_HINT_ARRAY_TYPE, party_member_data_hint);
}
}
void GameState::_bind_methods() {}
GameState::GameState() {
self_type::singleton_instance = this;
@ -23,37 +16,3 @@ GameState::~GameState() {
GameState *GameState::get_singleton() {
return self_type::singleton_instance;
}
void GameState::set_locale_uid(String const &value) {
this->locale_uid = value;
}
String GameState::get_locale_uid() const {
return this->locale_uid;
}
void GameState::set_locale_entrance_path(String const &value) {
this->locale_uid = value;
}
String GameState::get_locale_entrance_path() const {
return this->locale_uid;
}
void GameState::set_player_party(Array array) {
this->player_party.clear();
for (int i{ 0 }; i < array.size(); ++i) {
Ref<PartyMemberData> data{ array[i] };
if (data.is_valid()) {
this->player_party.push_back(data);
}
}
}
Array GameState::get_player_party() const {
Array a{};
for (Ref<PartyMemberData> const &data : this->player_party) {
a.push_back(data);
}
return a;
}

View file

@ -1,7 +1,7 @@
#ifndef GAME_STATE_H
#define GAME_STATE_H
#include "authority/party_member_data.h"
#include "core/object/class_db.h"
#include "core/object/object.h"
#include "core/templates/vector.h"
@ -14,17 +14,6 @@ public:
GameState();
virtual ~GameState();
static GameState *get_singleton();
void set_locale_uid(String const &value);
String get_locale_uid() const;
void set_locale_entrance_path(String const &value);
String get_locale_entrance_path() const;
void set_player_party(Array array);
Array get_player_party() const;
private:
String locale_uid{ "" };
String locale_entrance_path{ "" };
Vector<Ref<PartyMemberData>> player_party{};
};
#endif // !GAME_STATE_H

View file

@ -1,5 +1,7 @@
#include "player_actor.h"
#include "authority/game_state.h"
#include "authority/player_input.h"
#include "scene/main/viewport.h"
void PlayerActor::_bind_methods() {
}
@ -7,10 +9,20 @@ void PlayerActor::_bind_methods() {
void PlayerActor::ready() {
PlayerInput *input{ cast_to<PlayerInput>(get_node(NodePath("%PlayerInput"))) };
input->connect(PlayerInput::signal_movement, callable_mp(this, &self_type::input_move));
set_process(true);
}
void PlayerActor::process(double delta) {
Basis const basis{ this->get_viewport()->get_camera_3d()->get_global_basis() };
Vector3 x{ basis.get_column(0) };
x = Vector3{ x.x, 0.f, x.z }.normalized();
Vector3 z{ basis.get_column(2) };
z = Vector3{ z.x, 0.f, z.z }.normalized();
set_movement_direction(x * this->move_input.x + z * this->move_input.y);
}
void PlayerActor::input_move(Vector2 movement) {
set_movement_direction({ movement.x, 0.f, movement.y });
this->move_input = movement;
}
void PlayerActor::_notification(int what) {
@ -23,5 +35,8 @@ void PlayerActor::_notification(int what) {
case NOTIFICATION_READY:
ready();
return;
case NOTIFICATION_PROCESS:
process(get_process_delta_time());
return;
}
}

View file

@ -7,10 +7,14 @@ class PlayerActor : public ActorBody {
GDCLASS(PlayerActor, ActorBody);
static void _bind_methods();
void ready();
void process(double delta);
void input_move(Vector2 movement);
public:
void _notification(int what);
private:
Vector2 move_input{ 0.f, 0.f };
};
#endif // !PLAYER_ACTOR_H

View file

@ -4,13 +4,21 @@
#include "macros.h"
String const PlayerInput::signal_movement{ "movement" };
String const PlayerInput::signal_look{ "look" };
String const PlayerInput::signal_mouselook{ "mouselook" };
void PlayerInput::_bind_methods() {
BIND_PROPERTY(Variant::STRING, action_move_left);
BIND_PROPERTY(Variant::STRING, action_move_right);
BIND_PROPERTY(Variant::STRING, action_move_forward);
BIND_PROPERTY(Variant::STRING, action_move_back);
BIND_PROPERTY(Variant::STRING, action_look_left);
BIND_PROPERTY(Variant::STRING, action_look_right);
BIND_PROPERTY(Variant::STRING, action_look_up);
BIND_PROPERTY(Variant::STRING, action_look_down);
ADD_SIGNAL(MethodInfo(self_type::signal_movement, PropertyInfo(Variant::VECTOR2, "movement_directions")));
ADD_SIGNAL(MethodInfo(self_type::signal_look, PropertyInfo(Variant::VECTOR2, "look_delta")));
ADD_SIGNAL(MethodInfo(self_type::signal_mouselook, PropertyInfo(Variant::VECTOR2, "mouse_delta")));
}
void PlayerInput::_notification(int what) {
@ -21,22 +29,56 @@ void PlayerInput::_notification(int what) {
default:
return;
case NOTIFICATION_READY:
this->set_process_unhandled_input(true);
set_process_unhandled_input(true);
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
return;
}
}
void PlayerInput::unhandled_input(Ref<InputEvent> const &event) {
Input *input{ Input::get_singleton() };
bool is_movement_action{ event->is_action(self_type::action_move_left) };
is_movement_action |= event->is_action(self_type::action_move_right);
is_movement_action |= event->is_action(self_type::action_move_forward);
is_movement_action |= event->is_action(self_type::action_move_back);
if (is_movement_action) {
float const h_axis{ input->get_axis(self_type::action_move_right, self_type::action_move_left) };
float const v_axis{ input->get_axis(self_type::action_move_back, self_type::action_move_forward) };
this->emit_signal(self_type::signal_movement, Vector2{ h_axis, v_axis });
float const h_axis{ input->get_axis(self_type::action_move_left, self_type::action_move_right) };
float const v_axis{ input->get_axis(self_type::action_move_forward, self_type::action_move_back) };
emit_signal(self_type::signal_movement, Vector2{ h_axis, v_axis });
}
bool is_look_action{ event->is_action(self_type::action_look_left) };
is_look_action |= event->is_action(self_type::action_look_right);
is_look_action |= event->is_action(self_type::action_look_up);
is_look_action |= event->is_action(self_type::action_look_down);
if (is_look_action) {
float const h_axis{ input->get_axis(self_type::action_look_right, self_type::action_look_left) };
float const v_axis{ input->get_axis(self_type::action_look_down, self_type::action_look_up) };
emit_signal(self_type::signal_look, Vector2{ h_axis, v_axis } * this->look_speed);
}
Ref<InputEventMouseMotion> mouse_motion_action{ event };
if (mouse_motion_action.is_valid()) {
Vector2 const velocity{ mouse_motion_action->get_relative() };
emit_signal(self_type::signal_mouselook, velocity * this->mouselook_speed);
}
}
void PlayerInput::set_mouselook_speed(Vector2 speed) {
this->mouselook_speed = speed;
}
Vector2 PlayerInput::get_mouselook_speed() const {
return this->mouselook_speed;
}
void PlayerInput::set_look_speed(Vector2 speed) {
this->look_speed = speed;
}
Vector2 PlayerInput::get_look_speed() const {
return this->look_speed;
}
void PlayerInput::set_action_move_left(String action) {
@ -70,3 +112,35 @@ void PlayerInput::set_action_move_back(String action) {
String PlayerInput::get_action_move_back() const {
return this->action_move_back;
}
void PlayerInput::set_action_look_left(String action) {
this->action_look_left = action;
}
String PlayerInput::get_action_look_left() const {
return this->action_look_left;
}
void PlayerInput::set_action_look_right(String action) {
this->action_look_right = action;
}
String PlayerInput::get_action_look_right() const {
return this->action_look_right;
}
void PlayerInput::set_action_look_up(String action) {
this->action_look_up = action;
}
String PlayerInput::get_action_look_up() const {
return this->action_look_up;
}
void PlayerInput::set_action_look_down(String action) {
this->action_look_down = action;
}
String PlayerInput::get_action_look_down() const {
return this->action_look_down;
}

View file

@ -11,6 +11,12 @@ class PlayerInput : public Node {
public:
void _notification(int what);
virtual void unhandled_input(Ref<InputEvent> const &event) override;
void set_mouselook_speed(Vector2 speed);
Vector2 get_mouselook_speed() const;
void set_look_speed(Vector2 speed);
Vector2 get_look_speed() const;
void set_action_move_left(String action);
String get_action_move_left() const;
void set_action_move_right(String action);
@ -20,14 +26,33 @@ public:
void set_action_move_back(String action);
String get_action_move_back() const;
void set_action_look_left(String action);
String get_action_look_left() const;
void set_action_look_right(String action);
String get_action_look_right() const;
void set_action_look_up(String action);
String get_action_look_up() const;
void set_action_look_down(String action);
String get_action_look_down() const;
public:
static String const signal_movement;
static String const signal_look;
static String const signal_mouselook;
private:
Vector2 mouselook_speed{ 0.001f, 0.001f };
Vector2 look_speed{ 1.75f, 1.75f };
String action_move_left{ "move_left" };
String action_move_right{ "move_right" };
String action_move_forward{ "move_forward" };
String action_move_back{ "move_back" };
String action_look_left{ "look_left" };
String action_look_right{ "look_right" };
String action_look_up{ "look_up" };
String action_look_down{ "look_down" };
};
#endif // !PLAYER_INPUT_H

View file

@ -2,10 +2,9 @@
#include "authority/actor_body.h"
#include "authority/game_state.h"
#include "authority/locale_marker.h"
#include "authority/party_member_data.h"
#include "authority/player_actor.h"
#include "authority/player_input.h"
#include "authority/third_person_camera.h"
#include "core/config/engine.h"
#include "core/object/class_db.h"
@ -16,11 +15,10 @@ void initialize_authority_module(ModuleInitializationLevel p_level) {
return;
}
GDREGISTER_CLASS(GameState);
GDREGISTER_RUNTIME_CLASS(LocaleMarker);
GDREGISTER_RUNTIME_CLASS(PartyMemberData);
GDREGISTER_CLASS(ActorBody);
GDREGISTER_CLASS(PlayerActor);
GDREGISTER_CLASS(PlayerInput);
GDREGISTER_CLASS(ThirdPersonCamera);
game_state = memnew(GameState);
Engine::get_singleton()->add_singleton(Engine::Singleton("GameState", GameState::get_singleton()));

View file

@ -0,0 +1,54 @@
#include "third_person_camera.h"
#include "player_input.h"
void ThirdPersonCamera::_bind_methods() {
}
void ThirdPersonCamera::ready() {
set_process(true);
if (PlayerInput * player_input{ cast_to<PlayerInput>(get_node(NodePath("%PlayerInput"))) }) {
player_input->connect(PlayerInput::signal_look, callable_mp(this, &self_type::on_look_input));
player_input->connect(PlayerInput::signal_mouselook, callable_mp(this, &self_type::on_mouselook_input));
}
}
void ThirdPersonCamera::process(double delta) {
this->look_rotation += look_rotation_motion * delta;
this->look_rotation.y = CLAMP(this->look_rotation.y, -Math_PI / 2.5, Math_PI / 2.5);
Vector3 pivot{ get_parent_node_3d()->get_global_position() };
pivot.y += this->height;
Vector3 offset{ 0.f, 0.f, this->distance };
offset.rotate({ 1.f, 0.f, 0.f }, this->look_rotation.y);
offset.rotate({ 0.f, 1.f, 0.f }, this->look_rotation.x);
look_at_from_position(pivot + offset, pivot);
}
void ThirdPersonCamera::on_look_input(Vector2 input) {
this->look_rotation_motion = input;
}
void ThirdPersonCamera::on_mouselook_input(Vector2 input) {
this->look_rotation -= input;
}
void ThirdPersonCamera::_notification(int what) {
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
switch (what) {
case NOTIFICATION_READY:
ready();
break;
case NOTIFICATION_PROCESS:
process(get_process_delta_time());
break;
}
}
void ThirdPersonCamera::set_base_rotation_speed(Vector2 value) {
this->base_rotation_speed = value;
}
Vector2 ThirdPersonCamera::get_base_rotation_speed() const {
return this->base_rotation_speed;
}

View file

@ -0,0 +1,29 @@
#ifndef THIRD_PERSON_CAMERA_H
#define THIRD_PERSON_CAMERA_H
#include "scene/3d/camera_3d.h"
class ThirdPersonCamera : public Camera3D {
GDCLASS(ThirdPersonCamera, Camera3D);
static void _bind_methods();
void ready();
void process(double delta);
void on_look_input(Vector2 input);
void on_mouselook_input(Vector2 input);
public:
void _notification(int what);
void set_base_rotation_speed(Vector2 value);
Vector2 get_base_rotation_speed() const;
private:
Vector2 look_rotation_motion{ 0.f, 0.f };
Vector2 look_rotation{ 0.f, 0.f };
Vector2 base_rotation_speed{ 0.1f, 0.1f };
float distance{ 3.f };
float height{ 2.2f };
};
#endif // !THIRD_PERSON_CAMERA_H