feat: first full movement and enemy prototype
This commit is contained in:
parent
d72a037d5a
commit
eb8dba058d
42 changed files with 3844 additions and 0 deletions
182
modules/going/player_body.cpp
Normal file
182
modules/going/player_body.cpp
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
#include "player_body.h"
|
||||
#include "core/config/engine.h"
|
||||
#include "core/input/input.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "macros.h"
|
||||
|
||||
char *const PlayerBody::split_step_action{const_cast<char*>("split_step")};
|
||||
char *const PlayerBody::move_left_action{const_cast<char*>("move_left")};
|
||||
char *const PlayerBody::move_right_action{const_cast<char*>("move_right")};
|
||||
char *const PlayerBody::move_forward_action{const_cast<char*>("move_forward")};
|
||||
char *const PlayerBody::move_back_action{const_cast<char*>("move_back")};
|
||||
|
||||
void PlayerBody::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_desired_velocity"), &self_type::get_desired_velocity);
|
||||
ClassDB::bind_method(D_METHOD("get_desired_direction"), &self_type::get_desired_direction);
|
||||
|
||||
BIND_PROPERTY(Variant::FLOAT, stopping_deceleration);
|
||||
BIND_PROPERTY(Variant::FLOAT, start_speed);
|
||||
BIND_PROPERTY(Variant::FLOAT, step_boost);
|
||||
BIND_PROPERTY(Variant::FLOAT, min_step_speed);
|
||||
BIND_PROPERTY(Variant::FLOAT, acceleration);
|
||||
BIND_PROPERTY(Variant::FLOAT, target_speed);
|
||||
BIND_PROPERTY(Variant::FLOAT, split_step_time);
|
||||
BIND_PROPERTY(Variant::FLOAT, split_step_stop_time);
|
||||
BIND_PROPERTY(Variant::FLOAT, model_lean);
|
||||
BIND_PROPERTY(Variant::FLOAT, model_lean_speed);
|
||||
}
|
||||
|
||||
void PlayerBody::_notification(int what) {
|
||||
if(Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch(what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
this->enter_tree();
|
||||
return;
|
||||
case NOTIFICATION_PROCESS:
|
||||
this->process(this->get_process_delta_time());
|
||||
return;
|
||||
case NOTIFICATION_PHYSICS_PROCESS:
|
||||
this->physics_process(this->get_physics_process_delta_time());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerBody::enter_tree() {
|
||||
this->set_process(true);
|
||||
this->set_physics_process(true);
|
||||
this->model = Object::cast_to<Node3D>(this->get_node(NodePath("character")));
|
||||
this->anim = Object::cast_to<AnimationPlayer>(this->get_node(NodePath("character/AnimationPlayer")));
|
||||
this->camera = Object::cast_to<Camera3D>(this->get_node(NodePath("Camera3D")));
|
||||
this->camera->set_fov(this->min_fov);
|
||||
}
|
||||
|
||||
void PlayerBody::process(double delta) {
|
||||
Input *input{Input::get_singleton()};
|
||||
this->movement = Vector2{
|
||||
input->get_axis(self_type::move_right_action, self_type::move_left_action),
|
||||
input->get_axis(self_type::move_back_action, self_type::move_forward_action)
|
||||
}.normalized();
|
||||
float fov_target{Math::lerp(this->min_fov, this->max_speed_fov, Math::sqrt(this->get_velocity().length() / this->target_speed))};
|
||||
this->camera->set_fov(Math::move_toward(this->camera->get_fov(), fov_target, float(this->max_delta_fov * delta)));
|
||||
}
|
||||
|
||||
void PlayerBody::physics_process(double delta) {
|
||||
this->move_and_slide();
|
||||
}
|
||||
|
||||
Vector3 PlayerBody::get_desired_direction() const {
|
||||
Basis const global{this->camera->get_global_basis()};
|
||||
Vector3 forward{global.get_column(2)};
|
||||
Vector3 left{global.get_column(0)};
|
||||
return Vector3{
|
||||
Vector3{forward.x, 0.f, forward.z}.normalized() * -this->movement.y +
|
||||
Vector3{left.x, 0.f, left.z}.normalized() * -this->movement.x
|
||||
}.normalized();
|
||||
}
|
||||
|
||||
Vector3 PlayerBody::get_desired_velocity() const {
|
||||
return this->get_desired_direction() * this->target_speed;
|
||||
}
|
||||
|
||||
Vector2 PlayerBody::get_movement_input() const {
|
||||
return this->movement;
|
||||
}
|
||||
|
||||
AnimationPlayer *PlayerBody::get_anim() const {
|
||||
return this->anim;
|
||||
}
|
||||
|
||||
Camera3D *PlayerBody::get_camera() const {
|
||||
return this->camera;
|
||||
}
|
||||
|
||||
Node3D *PlayerBody::get_model() const {
|
||||
return this->model;
|
||||
}
|
||||
|
||||
void PlayerBody::set_stopping_deceleration(float value) {
|
||||
this->stopping_deceleration = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_stopping_deceleration() const {
|
||||
return this->stopping_deceleration;
|
||||
}
|
||||
|
||||
void PlayerBody::set_start_speed(float value) {
|
||||
this->start_speed = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_start_speed() const {
|
||||
return this->start_speed;
|
||||
}
|
||||
|
||||
void PlayerBody::set_step_boost(float value) {
|
||||
this->step_boost = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_step_boost() const {
|
||||
return this->step_boost;
|
||||
}
|
||||
|
||||
void PlayerBody::set_min_step_speed(float value) {
|
||||
this->min_step_speed = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_min_step_speed() const {
|
||||
return this->min_step_speed;
|
||||
}
|
||||
|
||||
void PlayerBody::set_acceleration(float value) {
|
||||
this->acceleration = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_acceleration() const {
|
||||
return this->acceleration;
|
||||
}
|
||||
|
||||
void PlayerBody::set_target_speed(float value) {
|
||||
this->target_speed = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_target_speed() const {
|
||||
return this->target_speed;
|
||||
}
|
||||
|
||||
void PlayerBody::set_split_step_time(double value) {
|
||||
this->split_step_time = value;
|
||||
}
|
||||
|
||||
double PlayerBody::get_split_step_time() const {
|
||||
return this->split_step_time;
|
||||
}
|
||||
|
||||
void PlayerBody::set_split_step_stop_time(double value) {
|
||||
this->split_step_stop_time = value;
|
||||
}
|
||||
|
||||
double PlayerBody::get_split_step_stop_time() const {
|
||||
return this->split_step_stop_time;
|
||||
}
|
||||
|
||||
void PlayerBody::set_model_lean(float value) {
|
||||
this->model_lean = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_model_lean() const {
|
||||
return this->model_lean;
|
||||
}
|
||||
|
||||
void PlayerBody::set_model_lean_speed(float value) {
|
||||
this->model_lean_speed = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_model_lean_speed() const {
|
||||
return this->model_lean_speed;
|
||||
}
|
||||
73
modules/going/player_body.h
Normal file
73
modules/going/player_body.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef PLAYER_BODY_H
|
||||
#define PLAYER_BODY_H
|
||||
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/3d/physics/character_body_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
|
||||
class PlayerBody : public CharacterBody3D {
|
||||
GDCLASS(PlayerBody, CharacterBody3D);
|
||||
static void _bind_methods();
|
||||
void _notification(int what);
|
||||
void enter_tree();
|
||||
void process(double delta);
|
||||
void physics_process(double delta);
|
||||
|
||||
public:
|
||||
Vector3 get_desired_direction() const;
|
||||
Vector3 get_desired_velocity() const;
|
||||
Vector2 get_movement_input() const;
|
||||
|
||||
AnimationPlayer *get_anim() const;
|
||||
Camera3D *get_camera() const;
|
||||
Node3D *get_model() const;
|
||||
|
||||
void set_stopping_deceleration(float value);
|
||||
float get_stopping_deceleration() const;
|
||||
void set_start_speed(float value);
|
||||
float get_start_speed() const;
|
||||
void set_step_boost(float value);
|
||||
float get_step_boost() const;
|
||||
void set_min_step_speed(float value);
|
||||
float get_min_step_speed() const;
|
||||
void set_acceleration(float value);
|
||||
float get_acceleration() const;
|
||||
void set_target_speed(float value);
|
||||
float get_target_speed() const;
|
||||
void set_split_step_time(double value);
|
||||
double get_split_step_time() const;
|
||||
void set_split_step_stop_time(double value);
|
||||
double get_split_step_stop_time() const;
|
||||
void set_model_lean(float value);
|
||||
float get_model_lean() const;
|
||||
void set_model_lean_speed(float value);
|
||||
float get_model_lean_speed() const;
|
||||
private:
|
||||
Vector2 movement{0.f, 0.f};
|
||||
|
||||
AnimationPlayer *anim{nullptr};
|
||||
Node3D *model{nullptr};
|
||||
Camera3D *camera{nullptr};
|
||||
|
||||
float stopping_deceleration{20.f};
|
||||
float start_speed{5.f};
|
||||
float step_boost{7.f};
|
||||
float min_step_speed{15.f};
|
||||
float acceleration{8.f};
|
||||
float target_speed{30.f};
|
||||
double split_step_time{0.5};
|
||||
double split_step_stop_time{0.5};
|
||||
float max_speed_fov{100.f};
|
||||
float min_fov{80.f};
|
||||
double max_delta_fov{100.f};
|
||||
float model_lean{0.25f};
|
||||
float model_lean_speed{0.25f};
|
||||
public:
|
||||
static char *const split_step_action;
|
||||
static char *const move_left_action;
|
||||
static char *const move_right_action;
|
||||
static char *const move_forward_action;
|
||||
static char *const move_back_action;
|
||||
};
|
||||
|
||||
#endif // !PLAYER_BODY_H
|
||||
BIN
modules/going/player_body.linuxbsd.editor.dev.x86_64.llvm.o
Normal file
BIN
modules/going/player_body.linuxbsd.editor.dev.x86_64.llvm.o
Normal file
Binary file not shown.
174
modules/going/player_states.cpp
Normal file
174
modules/going/player_states.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#include "player_states.h"
|
||||
#include "core/config/engine.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/typedefs.h"
|
||||
#include "going/player_body.h"
|
||||
|
||||
|
||||
PlayerBody *PlayerState::get_body() const {
|
||||
return this->body;
|
||||
}
|
||||
|
||||
PlayerState::StateID StandingState::get_next_state() const {
|
||||
if(!this->get_body()->is_on_floor()) {
|
||||
return FallingState::get_class_static();
|
||||
} else {
|
||||
return !this->get_body()->get_movement_input().is_zero_approx()
|
||||
? RunningState::get_class_static()
|
||||
: self_type::get_class_static();
|
||||
}
|
||||
}
|
||||
|
||||
void StandingState::state_entered() {
|
||||
this->get_body()->get_anim()->play("RESET", 0.1);
|
||||
}
|
||||
|
||||
void StandingState::physics_process(double delta) {
|
||||
Vector3 const current{this->get_body()->get_velocity()};
|
||||
float const speed_delta{float(this->get_body()->get_stopping_deceleration() * delta)};
|
||||
this->get_body()->set_velocity(current.move_toward(Vector3(), speed_delta));
|
||||
}
|
||||
|
||||
PlayerState::StateID RunningState::get_next_state() const {
|
||||
if(!this->get_body()->is_on_floor()) {
|
||||
return FallingState::get_class_static();
|
||||
} else if(this->get_body()->get_velocity().is_zero_approx()) {
|
||||
return StandingState::get_class_static();
|
||||
} else if(Input::get_singleton()->is_action_just_pressed(PlayerBody::split_step_action) && this->get_body()->get_velocity().length() > this->get_body()->get_min_step_speed()) {
|
||||
return SplitStepState::get_class_static();
|
||||
} else {
|
||||
return self_type::get_class_static();
|
||||
}
|
||||
}
|
||||
|
||||
void RunningState::state_entered() {
|
||||
this->get_body()->get_anim()->play("run", 0.1);
|
||||
}
|
||||
|
||||
void RunningState::process(double delta) {
|
||||
if(!this->get_body()->get_velocity().is_zero_approx()) {
|
||||
Vector3 const current{this->get_body()->get_velocity()};
|
||||
Vector3 const cross{Vector3{0.f, 1.f, 0.f}.cross(current).normalized()};
|
||||
Vector3 const desired_direction{this->get_body()->get_desired_direction()};
|
||||
this->lean_modifier = Math::move_toward(
|
||||
this->lean_modifier,
|
||||
cross.dot(desired_direction) * this->get_body()->get_model_lean(),
|
||||
float(this->get_body()->get_model_lean_speed() * delta)
|
||||
);
|
||||
Vector3 up{Vector3{0.0, 1.0, 0.0} + cross * this->lean_modifier};
|
||||
this->get_body()->get_model()->look_at(this->get_body()->get_global_position() - current, up);
|
||||
}
|
||||
}
|
||||
|
||||
void RunningState::physics_process(double delta) {
|
||||
Vector3 const current{this->get_body()->get_velocity()};
|
||||
Vector3 const desired{this->get_body()->get_desired_velocity()};
|
||||
float const dot{current.dot(desired)};
|
||||
float const speed_delta{desired.is_zero_approx() || dot < 0.f // if we're stopping or making a full turnaround
|
||||
? float(this->get_body()->get_stopping_deceleration() * delta) // deceleration
|
||||
: (current.is_zero_approx() // if we're starting from standstill
|
||||
? this->get_body()->get_start_speed() // startup boost
|
||||
: float(this->get_body()->get_acceleration() * delta) // in-motion velocity
|
||||
)
|
||||
};
|
||||
this->get_body()->set_velocity(current.move_toward(desired, speed_delta));
|
||||
}
|
||||
|
||||
void RunningState::state_exited() {
|
||||
this->get_body()->get_model()->look_at(this->get_body()->get_global_position() - this->get_body()->get_velocity());
|
||||
this->lean_modifier = 0.f;
|
||||
}
|
||||
|
||||
PlayerState::StateID SplitStepState::get_next_state() const {
|
||||
if(!this->get_body()->is_on_floor()) {
|
||||
return FallingState::get_class_static();
|
||||
} else {
|
||||
return this->timer <= 0.0
|
||||
? RunningState::get_class_static()
|
||||
: self_type::get_class_static();
|
||||
}
|
||||
}
|
||||
|
||||
void SplitStepState::state_entered() {
|
||||
this->last_velocity = this->get_body()->get_velocity();
|
||||
this->timer = this->get_body()->get_split_step_time();
|
||||
this->get_body()->set_velocity(last_velocity.normalized() * this->get_body()->get_target_speed() * 0.75f);
|
||||
this->get_body()->get_anim()->play("split-step");
|
||||
}
|
||||
|
||||
void SplitStepState::process(double delta) {
|
||||
this->timer -= delta;
|
||||
this->get_body()->set_velocity(this->get_body()->get_velocity()
|
||||
.move_toward(Vector3(), this->get_body()->get_target_speed() / this->get_body()->get_split_step_stop_time() * delta));
|
||||
}
|
||||
|
||||
void SplitStepState::state_exited() {
|
||||
if(this->get_body()->is_on_floor()) {
|
||||
Vector3 const desired_direction{this->get_body()->get_desired_direction()};
|
||||
float const dot{this->last_velocity.normalized().dot(desired_direction)};
|
||||
this->get_body()->set_velocity(dot > -0.8f
|
||||
? desired_direction * MAX(last_velocity.length(), this->get_body()->get_step_boost())
|
||||
: Vector3()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PlayerState::StateID FallingState::get_next_state() const {
|
||||
if(this->get_body()->is_on_floor()) {
|
||||
return RunningState::get_class_static();
|
||||
} else {
|
||||
return self_type::get_class_static();
|
||||
}
|
||||
}
|
||||
|
||||
void FallingState::state_entered() {
|
||||
this->get_body()->get_anim()->play("falling", 0.1);
|
||||
}
|
||||
|
||||
void FallingState::process(double delta) {
|
||||
Vector3 const current{this->get_body()->get_velocity()};
|
||||
Vector3 const flattened{current.x, 0.f, current.z};
|
||||
this->get_body()->set_velocity((flattened - (flattened * 0.025f)) + Vector3{0.f, current.y - float(9.8 * delta), 0.f});
|
||||
}
|
||||
|
||||
void PlayerStateMachine::_bind_methods() {
|
||||
}
|
||||
|
||||
void PlayerStateMachine::_notification(int what) {
|
||||
if(Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch(what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
this->ready();
|
||||
break;
|
||||
case NOTIFICATION_PROCESS:
|
||||
this->current_state->process(this->get_process_delta_time());
|
||||
this->try_transition();
|
||||
break;
|
||||
case NOTIFICATION_PHYSICS_PROCESS:
|
||||
this->current_state->physics_process(this->get_physics_process_delta_time());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerStateMachine::ready() {
|
||||
this->set_process(true);
|
||||
this->set_physics_process(true);
|
||||
this->body = Object::cast_to<PlayerBody>(this->get_parent());
|
||||
this->add_state<StandingState>();
|
||||
this->add_state<RunningState>();
|
||||
this->add_state<SplitStepState>();
|
||||
this->add_state<FallingState>();
|
||||
}
|
||||
|
||||
void PlayerStateMachine::try_transition() {
|
||||
PlayerState::StateID next{this->current_state->get_next_state()};
|
||||
if(next != this->current_state->get_class()) {
|
||||
this->current_state->state_exited();
|
||||
this->current_state = this->states[next];
|
||||
this->current_state->state_entered();
|
||||
}
|
||||
}
|
||||
93
modules/going/player_states.h
Normal file
93
modules/going/player_states.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#ifndef PLAYER_STATES_H
|
||||
#define PLAYER_STATES_H
|
||||
|
||||
#include "core/object/object.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "scene/main/node.h"
|
||||
class PlayerBody;
|
||||
|
||||
class PlayerState : public Object {
|
||||
GDCLASS(PlayerState, Object);
|
||||
friend class PlayerStateMachine;
|
||||
public:
|
||||
typedef StringName StateID;
|
||||
public:
|
||||
virtual StateID get_next_state() const {
|
||||
return this->get_class();
|
||||
}
|
||||
virtual void state_entered() {}
|
||||
virtual void process(double delta) {}
|
||||
virtual void physics_process(double delta) {}
|
||||
virtual void state_exited() {}
|
||||
|
||||
PlayerBody *get_body() const;
|
||||
private:
|
||||
PlayerBody *body{nullptr};
|
||||
};
|
||||
|
||||
class StandingState : public PlayerState {
|
||||
GDCLASS(StandingState, PlayerState);
|
||||
public:
|
||||
virtual StateID get_next_state() const override;
|
||||
virtual void state_entered() override;
|
||||
virtual void physics_process(double delta) override;
|
||||
};
|
||||
|
||||
class RunningState : public PlayerState {
|
||||
GDCLASS(RunningState, PlayerState);
|
||||
public:
|
||||
virtual StateID get_next_state() const override;
|
||||
virtual void state_entered() override;
|
||||
virtual void process(double delta) override;
|
||||
virtual void physics_process(double delta) override;
|
||||
virtual void state_exited() override;
|
||||
private:
|
||||
float lean_modifier{0.f};
|
||||
};
|
||||
|
||||
class SplitStepState : public PlayerState {
|
||||
GDCLASS(SplitStepState, PlayerState);
|
||||
public:
|
||||
virtual StateID get_next_state() const override;
|
||||
virtual void state_entered() override;
|
||||
virtual void process(double delta) override;
|
||||
virtual void state_exited() override;
|
||||
private:
|
||||
Vector3 last_velocity{0.f, 0.f, 0.f};
|
||||
double timer{0.0};
|
||||
};
|
||||
|
||||
class FallingState : public PlayerState {
|
||||
GDCLASS(FallingState, PlayerState);
|
||||
public:
|
||||
virtual StateID get_next_state() const override;
|
||||
virtual void state_entered() override;
|
||||
virtual void process(double delta) override;
|
||||
};
|
||||
|
||||
class PlayerStateMachine : public Node {
|
||||
GDCLASS(PlayerStateMachine, Node);
|
||||
static void _bind_methods();
|
||||
void _notification(int what);
|
||||
void ready();
|
||||
void try_transition();
|
||||
template <class TState>
|
||||
void add_state();
|
||||
|
||||
PlayerBody *body{nullptr};
|
||||
PlayerState *current_state{nullptr};
|
||||
HashMap<PlayerState::StateID, PlayerState*> states;
|
||||
};
|
||||
|
||||
template <class TState>
|
||||
void PlayerStateMachine::add_state() {
|
||||
PlayerState *state{new TState()};
|
||||
state->body = this->body;
|
||||
this->states.insert(TState::get_class_static(), state);
|
||||
if(this->current_state == nullptr) {
|
||||
this->current_state = state;
|
||||
state->state_entered();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !PLAYER_STATES_H
|
||||
BIN
modules/going/player_states.linuxbsd.editor.dev.x86_64.llvm.o
Normal file
BIN
modules/going/player_states.linuxbsd.editor.dev.x86_64.llvm.o
Normal file
Binary file not shown.
|
|
@ -1,11 +1,19 @@
|
|||
#include "register_types.h"
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "going/player_body.h"
|
||||
#include "going/player_states.h"
|
||||
|
||||
void initialize_going_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
ClassDB::register_class<PlayerBody>();
|
||||
ClassDB::register_class<PlayerState>();
|
||||
ClassDB::register_class<StandingState>();
|
||||
ClassDB::register_class<RunningState>();
|
||||
ClassDB::register_class<SplitStepState>();
|
||||
ClassDB::register_class<PlayerStateMachine>();
|
||||
}
|
||||
|
||||
void uninitialize_going_module(ModuleInitializationLevel p_level) {
|
||||
|
|
|
|||
BIN
modules/going/register_types.linuxbsd.editor.dev.x86_64.llvm.o
Normal file
BIN
modules/going/register_types.linuxbsd.editor.dev.x86_64.llvm.o
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue