feat: finishing touches
This commit is contained in:
parent
cebae80b5c
commit
6d4a961423
39 changed files with 4028 additions and 2162 deletions
67
modules/going/game_over_effect.cpp
Normal file
67
modules/going/game_over_effect.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#include "game_over_effect.h"
|
||||
#include "core/config/engine.h"
|
||||
#include "core/typedefs.h"
|
||||
#include "going/player_body.h"
|
||||
|
||||
void GameOverEffect::_bind_methods() {
|
||||
}
|
||||
|
||||
void GameOverEffect::_notification(int what) {
|
||||
if(Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch(what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
this->ready();
|
||||
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;
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
RenderingServer::get_singleton()->global_shader_parameter_set("game_over_percentage", this->game_over_percentage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GameOverEffect::ready() {
|
||||
this->set_process(true);
|
||||
this->set_physics_process(true);
|
||||
this->body = Object::cast_to<PlayerBody>(this->get_parent());
|
||||
this->anim = Object::cast_to<AnimationPlayer>(this->get_node(NodePath("AnimationPlayer")));
|
||||
this->camera = Object::cast_to<Camera3D>(this->get_node(NodePath("../Camera3D")));
|
||||
this->anim->play("game_over", -1, 0.0);
|
||||
this->set_visible(false);
|
||||
}
|
||||
|
||||
void GameOverEffect::process(double delta) {
|
||||
this->anim->seek(MIN(this->game_over_percentage * 2.f, 1.0f), true);
|
||||
RenderingServer::get_singleton()->global_shader_parameter_set("game_over_percentage", this->game_over_percentage);
|
||||
this->set_visible(this->game_over_percentage > 0.1);
|
||||
if(this->game_over_percentage >= 1.0) {
|
||||
this->body->load_checkpoint();
|
||||
this->game_over_percentage = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void GameOverEffect::physics_process(double delta) {
|
||||
Vector3 const delta_position{this->get_global_position() - this->last_frame_point};
|
||||
if(delta_position.length() / delta > this->game_over_max_delta) { // convert per-frame movement to approximate movement/second
|
||||
this->game_over_percentage = 0.f;
|
||||
} else {
|
||||
this->game_over_percentage += delta * this->game_over_speed;
|
||||
}
|
||||
this->last_frame_point = this->get_global_position();
|
||||
Vector3 const position{this->body->get_global_position()};
|
||||
Vector3 camera_position{this->camera->get_global_position()};
|
||||
camera_position.y = this->get_global_position().y;
|
||||
this->set_global_position(position);
|
||||
Vector3 const target{position - (camera_position - position)};
|
||||
if(!target.is_equal_approx(position)) {
|
||||
this->look_at(target);
|
||||
}
|
||||
}
|
||||
27
modules/going/game_over_effect.h
Normal file
27
modules/going/game_over_effect.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef GAME_OVER_EFFECT_H
|
||||
#define GAME_OVER_EFFECT_H
|
||||
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
class PlayerBody;
|
||||
|
||||
class GameOverEffect : public Node3D {
|
||||
GDCLASS(GameOverEffect, Node3D);
|
||||
static void _bind_methods();
|
||||
void _notification(int what);
|
||||
void ready();
|
||||
void process(double delta);
|
||||
void physics_process(double delta);
|
||||
private:
|
||||
Vector3 last_frame_point{};
|
||||
float game_over_max_delta{1.0};
|
||||
float game_over_speed{1.0/2.0};
|
||||
double game_over_percentage{0.f};
|
||||
|
||||
AnimationPlayer *anim{nullptr};
|
||||
PlayerBody *body{nullptr};
|
||||
Camera3D *camera{nullptr};
|
||||
};
|
||||
|
||||
#endif // !GAME_OVER_EFFECT_H
|
||||
|
|
@ -16,6 +16,7 @@ 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() {
|
||||
ADD_SIGNAL(MethodInfo("obstacle_broken"));
|
||||
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);
|
||||
ClassDB::bind_method(D_METHOD("save_checkpoint"), &self_type::save_checkpoint);
|
||||
|
|
@ -31,10 +32,10 @@ void PlayerBody::_bind_methods() {
|
|||
BIND_PROPERTY(Variant::FLOAT, split_step_stop_time);
|
||||
BIND_PROPERTY(Variant::FLOAT, bash_speed);
|
||||
BIND_PROPERTY(Variant::FLOAT, bash_time);
|
||||
BIND_PROPERTY(Variant::FLOAT, extra_bash_time);
|
||||
BIND_PROPERTY(Variant::VECTOR2, jump_impulse);
|
||||
BIND_PROPERTY(Variant::FLOAT, model_lean);
|
||||
BIND_PROPERTY(Variant::FLOAT, model_lean_speed);
|
||||
BIND_PROPERTY(Variant::FLOAT, game_over_speed);
|
||||
BIND_PROPERTY(Variant::BOOL, can_jump);
|
||||
BIND_PROPERTY(Variant::BOOL, can_bash);
|
||||
}
|
||||
|
|
@ -97,7 +98,7 @@ void PlayerBody::load_checkpoint() {
|
|||
this->last_checkpoint->load(this);
|
||||
this->force_update_transform();
|
||||
this->set_velocity(Vector3{});
|
||||
this->state->force_state<FallingState>();
|
||||
this->state->_force_state<FallingState>();
|
||||
}
|
||||
|
||||
Vector3 PlayerBody::get_desired_direction() const {
|
||||
|
|
@ -210,6 +211,14 @@ double PlayerBody::get_bash_time() const {
|
|||
return this->bash_time;
|
||||
}
|
||||
|
||||
void PlayerBody::set_extra_bash_time(double value) {
|
||||
this->extra_bash_time = value;
|
||||
}
|
||||
|
||||
double PlayerBody::get_extra_bash_time() const {
|
||||
return this->extra_bash_time;
|
||||
}
|
||||
|
||||
void PlayerBody::set_jump_impulse(Vector2 value) {
|
||||
this->jump_impulse = value;
|
||||
}
|
||||
|
|
@ -234,14 +243,6 @@ float PlayerBody::get_model_lean_speed() const {
|
|||
return this->model_lean_speed;
|
||||
}
|
||||
|
||||
void PlayerBody::set_game_over_speed(float value) {
|
||||
this->game_over_speed = value;
|
||||
}
|
||||
|
||||
float PlayerBody::get_game_over_speed() const {
|
||||
return this->game_over_speed;
|
||||
}
|
||||
|
||||
void PlayerBody::set_can_jump(bool value) {
|
||||
this->can_jump = value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,14 +47,14 @@ public:
|
|||
float get_bash_speed() const;
|
||||
void set_bash_time(double value);
|
||||
double get_bash_time() const;
|
||||
void set_extra_bash_time(double value);
|
||||
double get_extra_bash_time() const;
|
||||
void set_jump_impulse(Vector2 value);
|
||||
Vector2 get_jump_impulse() 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;
|
||||
void set_game_over_speed(float value);
|
||||
float get_game_over_speed() const;
|
||||
void set_can_jump(bool value);
|
||||
bool get_can_jump() const;
|
||||
void set_can_bash(bool value);
|
||||
|
|
@ -76,13 +76,13 @@ private:
|
|||
double split_step_stop_time{0.5};
|
||||
float bash_speed{100.f};
|
||||
double bash_time{0.25};
|
||||
double extra_bash_time{0.25};
|
||||
Vector2 jump_impulse{5.f, 5.f};
|
||||
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};
|
||||
double game_over_speed{1.0/4.0};
|
||||
bool can_bash{false};
|
||||
bool can_jump{false};
|
||||
Ref<Checkpoint> last_checkpoint{nullptr};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#include "core/typedefs.h"
|
||||
#include "going/player_body.h"
|
||||
#include "scene/main/scene_tree.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
|
||||
PlayerBody *PlayerState::get_body() const {
|
||||
|
|
@ -24,18 +23,6 @@ PlayerState::StateID StandingState::get_next_state() const {
|
|||
|
||||
void StandingState::state_entered() {
|
||||
this->get_body()->get_anim()->play("RESET", 0.1);
|
||||
this->game_over_timer = 0.0;
|
||||
}
|
||||
|
||||
void StandingState::process(double delta) {
|
||||
this->game_over_timer += delta * this->get_body()->get_game_over_speed();
|
||||
if(this->game_over_timer > 1.0) {
|
||||
RenderingServer::get_singleton()->global_shader_parameter_set(this->game_over_param, 0.0);
|
||||
this->get_body()->load_checkpoint();
|
||||
} else {
|
||||
RenderingServer::get_singleton()->global_shader_parameter_set(this->game_over_param, float(this->game_over_timer));
|
||||
this->game_over_timer = MIN(this->game_over_timer, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
void StandingState::physics_process(double delta) {
|
||||
|
|
@ -44,10 +31,6 @@ void StandingState::physics_process(double delta) {
|
|||
this->get_body()->set_velocity(current.move_toward(Vector3(), speed_delta));
|
||||
}
|
||||
|
||||
void StandingState::state_exited() {
|
||||
RenderingServer::get_singleton()->global_shader_parameter_set(this->game_over_param, 0.0f);
|
||||
}
|
||||
|
||||
PlayerState::StateID RunningState::get_next_state() const {
|
||||
Vector3 const velocity{this->get_body()->get_velocity()};
|
||||
Vector3 const desired{this->get_body()->get_desired_velocity()};
|
||||
|
|
@ -202,6 +185,8 @@ void JumpingState::state_entered() {
|
|||
Vector3{current.x * impulse.x, impulse.y, current.z * impulse.x}
|
||||
));
|
||||
this->get_body()->get_anim()->play("jump");
|
||||
this->vfx = cast_to<Node3D>(this->get_body()->get_node(NodePath("character/jump_effect")));
|
||||
this->vfx->set_visible(true);
|
||||
}
|
||||
|
||||
void JumpingState::physics_process(double delta) {
|
||||
|
|
@ -210,23 +195,33 @@ void JumpingState::physics_process(double delta) {
|
|||
this->get_body()->set_velocity((flattened - (flattened * 0.015f)) + Vector3{0.f, current.y - float(9.8 * delta), 0.f});
|
||||
}
|
||||
|
||||
void JumpingState::state_exited() {
|
||||
this->vfx->set_visible(false);
|
||||
}
|
||||
|
||||
PlayerState::StateID BashState::get_next_state() const {
|
||||
if(!this->get_body()->is_on_floor()) {
|
||||
return FallingState::get_class_static();
|
||||
} else if (this->timer < 0.0) {
|
||||
return StandingState::get_class_static();
|
||||
} else if(Input::get_singleton()->is_action_just_pressed(PlayerBody::split_step_action)) {
|
||||
return SplitStepState::get_class_static();
|
||||
} else if(this->timer < 0.0) {
|
||||
return RunningState::get_class_static();
|
||||
}
|
||||
return this->get_class();
|
||||
}
|
||||
|
||||
void BashState::state_entered() {
|
||||
this->get_body()->get_anim()->play("bash");
|
||||
this->get_body()->connect("obstacle_broken", this->on_obstacle_broken_cal);
|
||||
this->timer = this->get_body()->get_bash_time();
|
||||
this->speed = this->get_body()->get_bash_speed();
|
||||
(this->vfx = Object::cast_to<Node3D>(this->get_body()->get_node(this->vfx_path)))->set_visible(true);
|
||||
}
|
||||
|
||||
void BashState::on_obstacle_broken() {
|
||||
this->timer += this->get_body()->get_extra_bash_time();
|
||||
}
|
||||
|
||||
void BashState::process(double delta) {
|
||||
this->timer -= delta;
|
||||
}
|
||||
|
|
@ -245,13 +240,37 @@ void BashState::physics_process(double delta) {
|
|||
|
||||
void BashState::state_exited() {
|
||||
this->vfx->set_visible(false);
|
||||
if(!this->get_body()->is_on_floor()) {
|
||||
this->get_body()->set_velocity(this->get_body()->get_velocity() / 2.f);
|
||||
this->get_body()->set_velocity(this->get_body()->get_model()->get_global_basis().get_column(2) * this->get_body()->get_target_speed());
|
||||
this->get_body()->disconnect("obstacle_broken", this->on_obstacle_broken_cal);
|
||||
}
|
||||
|
||||
void VictoryState::state_entered() {
|
||||
this->camera = cast_to<Camera3D>(this->get_body()->get_node(NodePath("Camera3D")));
|
||||
this->camera->set_process(false);
|
||||
this->camera->set_physics_process(false);
|
||||
this->get_body()->get_node(NodePath("GameOverEffect"))->queue_free();
|
||||
this->get_body()->get_anim()->play("victory");
|
||||
this->camera_target = this->camera->get_global_position() - this->camera->get_global_basis().get_column(2) * 3.f;
|
||||
}
|
||||
|
||||
void VictoryState::process(double delta) {
|
||||
if(this->get_body()->get_velocity().length() < 0.05) {
|
||||
this->get_body()->set_velocity(Vector3{});
|
||||
}
|
||||
float fov{this->camera->get_fov()};
|
||||
this->camera->set_fov(Math::move_toward(fov, 30.f, 20.f * (float)delta));
|
||||
}
|
||||
|
||||
void VictoryState::physics_process(double delta) {
|
||||
Vector3 const velocity{this->get_body()->get_velocity() * 0.8};
|
||||
this->get_body()->set_velocity(velocity + Vector3{0.f, -50.f, 0.f});
|
||||
this->camera->look_at(this->camera_target);
|
||||
this->camera_target.y = MIN(this->camera_target.y + delta * 0.2, 30.f);
|
||||
}
|
||||
|
||||
void PlayerStateMachine::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_current_state"), &self_type::get_current_state);
|
||||
ClassDB::bind_method(D_METHOD("force_state"), &self_type::force_state);
|
||||
}
|
||||
|
||||
void PlayerStateMachine::_notification(int what) {
|
||||
|
|
@ -284,6 +303,7 @@ void PlayerStateMachine::ready() {
|
|||
this->add_state<SplitStepState>();
|
||||
this->add_state<JumpingState>();
|
||||
this->add_state<BashState>();
|
||||
this->add_state<VictoryState>();
|
||||
}
|
||||
|
||||
void PlayerStateMachine::try_transition() {
|
||||
|
|
@ -298,3 +318,13 @@ void PlayerStateMachine::try_transition() {
|
|||
StringName PlayerStateMachine::get_current_state() const {
|
||||
return this->current_state->get_class();
|
||||
}
|
||||
|
||||
void PlayerStateMachine::force_state(StringName name) {
|
||||
if(this->states.has(name)) {
|
||||
this->current_state->state_exited();
|
||||
this->current_state = this->states[name];
|
||||
this->current_state->state_entered();
|
||||
} else {
|
||||
ERR_FAIL_EDMSG("Attempt to force switch to state that is not registered");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "core/object/object.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/variant/callable.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/main/node.h"
|
||||
class PlayerBody;
|
||||
|
||||
|
|
@ -30,12 +32,7 @@ class StandingState : public 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:
|
||||
double game_over_timer{0.0};
|
||||
StringName game_over_param{"game_over_percentage"};
|
||||
};
|
||||
|
||||
class RunningState : public PlayerState {
|
||||
|
|
@ -81,6 +78,9 @@ public:
|
|||
virtual StateID get_next_state() const override;
|
||||
virtual void state_entered() override;
|
||||
virtual void physics_process(double delta) override;
|
||||
virtual void state_exited() override;
|
||||
private:
|
||||
Node3D *vfx{nullptr};
|
||||
};
|
||||
|
||||
class BashState : public PlayerState {
|
||||
|
|
@ -88,16 +88,29 @@ class BashState : public PlayerState {
|
|||
public:
|
||||
virtual StateID get_next_state() const override;
|
||||
virtual void state_entered() override;
|
||||
void on_obstacle_broken();
|
||||
virtual void process(double delta) override;
|
||||
virtual void physics_process(double delta) override;
|
||||
virtual void state_exited() override;
|
||||
private:
|
||||
Callable on_obstacle_broken_cal{callable_mp(this, &self_type::on_obstacle_broken)};
|
||||
NodePath vfx_path{"character/bash_attack"};
|
||||
Node3D *vfx{nullptr};
|
||||
double timer{0.0};
|
||||
float speed{0.f};
|
||||
};
|
||||
|
||||
class VictoryState : public PlayerState {
|
||||
GDCLASS(VictoryState, PlayerState);
|
||||
public:
|
||||
virtual void state_entered() override;
|
||||
virtual void process(double delta) override;
|
||||
virtual void physics_process(double delta) override;
|
||||
private:
|
||||
Camera3D *camera{nullptr};
|
||||
Vector3 camera_target{};
|
||||
};
|
||||
|
||||
class PlayerStateMachine : public Node {
|
||||
GDCLASS(PlayerStateMachine, Node);
|
||||
static void _bind_methods();
|
||||
|
|
@ -108,8 +121,9 @@ class PlayerStateMachine : public Node {
|
|||
void add_state();
|
||||
public:
|
||||
StringName get_current_state() const;
|
||||
void force_state(StringName name);
|
||||
template <class TState>
|
||||
void force_state();
|
||||
void _force_state();
|
||||
private:
|
||||
PlayerBody *body{nullptr};
|
||||
PlayerState *current_state{nullptr};
|
||||
|
|
@ -128,12 +142,10 @@ void PlayerStateMachine::add_state() {
|
|||
}
|
||||
|
||||
template <class TState>
|
||||
void PlayerStateMachine::force_state() {
|
||||
void PlayerStateMachine::_force_state() {
|
||||
PlayerState::StateID next{TState::get_class_static()};
|
||||
if(!next.is_empty()) {
|
||||
this->current_state->state_exited();
|
||||
this->current_state = this->states[TState::get_class_static()];
|
||||
this->current_state->state_entered();
|
||||
this->force_state(next);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
#include "core/object/class_db.h"
|
||||
#include "going/checkpoint.h"
|
||||
#include "going/game_over_effect.h"
|
||||
#include "going/game_ui.h"
|
||||
#include "going/player_body.h"
|
||||
#include "going/player_states.h"
|
||||
#include "going/valley_root.h"
|
||||
|
||||
void initialize_going_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
|
|
@ -23,6 +25,8 @@ void initialize_going_module(ModuleInitializationLevel p_level) {
|
|||
ClassDB::register_class<Checkpoint>();
|
||||
ClassDB::register_class<CheckpointArea>();
|
||||
ClassDB::register_class<ReloadArea>();
|
||||
ClassDB::register_class<GameOverEffect>();
|
||||
ClassDB::register_class<ValleyRoot>();
|
||||
}
|
||||
|
||||
void uninitialize_going_module(ModuleInitializationLevel p_level) {
|
||||
|
|
|
|||
29
modules/going/valley_root.cpp
Normal file
29
modules/going/valley_root.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#include "valley_root.h"
|
||||
#include "scene/gui/subviewport_container.h"
|
||||
|
||||
double ValleyRoot::render_factor{1.0};
|
||||
|
||||
void ValleyRoot::_bind_methods() {
|
||||
ClassDB::bind_static_method("ValleyRoot", D_METHOD("set_render_factor", "value"), &self_type::set_render_factor);
|
||||
}
|
||||
|
||||
void ValleyRoot::_notification(int what) {
|
||||
if(Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch(what) {
|
||||
case NOTIFICATION_READY:
|
||||
this->ready();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ValleyRoot::ready() {
|
||||
if(SubViewportContainer *container{cast_to<SubViewportContainer>(this->get_node(NodePath("SubViewportContainer")))}) {
|
||||
container->set_stretch_shrink(this->render_factor);
|
||||
}
|
||||
}
|
||||
|
||||
void ValleyRoot::set_render_factor(double factor) {
|
||||
self_type::render_factor = factor;
|
||||
}
|
||||
16
modules/going/valley_root.h
Normal file
16
modules/going/valley_root.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef VALLEY_ROOT_H
|
||||
#define VALLEY_ROOT_H
|
||||
|
||||
#include "scene/main/node.h"
|
||||
class ValleyRoot : public Node {
|
||||
GDCLASS(ValleyRoot, Node);
|
||||
static void _bind_methods();
|
||||
void _notification(int what);
|
||||
void ready();
|
||||
private:
|
||||
static double render_factor;
|
||||
public:
|
||||
static void set_render_factor(double factor);
|
||||
};
|
||||
|
||||
#endif // !VALLEY_ROOT_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue