feat: rethought behaviour separation and states
This commit is contained in:
parent
54a887dbf3
commit
c3ff41e1e6
|
|
@ -20,7 +20,7 @@ void Character::physics_process(double delta) {
|
|||
}
|
||||
|
||||
void Character::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
if (Engine::get_singleton()->is_editor_hint() || !this->data.is_valid()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
|
|
@ -38,3 +38,80 @@ void Character::_notification(int what) {
|
|||
void Character::set_movement(Vector2 movement) {
|
||||
this->world_movement_direction = movement;
|
||||
}
|
||||
|
||||
bool Character::is_moving() const {
|
||||
return !this->world_movement_direction.is_zero_approx();
|
||||
}
|
||||
|
||||
void CharacterState::_bind_methods() {
|
||||
BIND_PROPERTY(Variant::BOOL, start_active);
|
||||
}
|
||||
|
||||
void CharacterState::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
this->character = cast_to<Character>(get_parent());
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
if (start_active) {
|
||||
set_state_active(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterState::switch_to_state(String value) {
|
||||
if (this->state_active) {
|
||||
print_error(vformat("Attempt to switch from inactive state %s to new state %s", get_path(), value));
|
||||
return;
|
||||
}
|
||||
set_state_active(false);
|
||||
stack_state_independent(value);
|
||||
}
|
||||
|
||||
void CharacterState::stack_state_dependent(String value) {
|
||||
if (this->state_active) {
|
||||
print_error(vformat("Attempt to stack dependent state %s from inactive state %s", value, get_path()));
|
||||
return;
|
||||
}
|
||||
Node *node{ get_parent()->get_node(value) };
|
||||
if (CharacterState * state{ cast_to<CharacterState>(node) }) {
|
||||
state->depending_state = this;
|
||||
this->dependent_states.insert(state);
|
||||
state->set_state_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterState::stack_state_independent(String value) {
|
||||
if (this->state_active) {
|
||||
print_error(vformat("Attempt to stack state %s from inactive state %s", value, get_path()));
|
||||
return;
|
||||
}
|
||||
Node *node{ get_parent()->get_node(value) };
|
||||
if (CharacterState * state{ cast_to<CharacterState>(node) }) {
|
||||
state->set_state_active(true);
|
||||
} else {
|
||||
print_error(vformat("Attempt to stack nonexistent state %s from %s", value, get_path()));
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterState::set_state_active(bool active) {
|
||||
if (this->state_active == active) {
|
||||
this->state_active = active;
|
||||
if (active) {
|
||||
state_entered();
|
||||
} else {
|
||||
state_exited();
|
||||
this->depending_state = nullptr;
|
||||
for (CharacterState *state : this->dependent_states) {
|
||||
state->set_state_active(false);
|
||||
}
|
||||
this->dependent_states.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "authority/macros.h"
|
||||
#include "core/io/resource.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "scene/3d/physics/character_body_3d.h"
|
||||
|
||||
class CharacterData : public Resource {
|
||||
|
|
@ -25,6 +26,7 @@ protected:
|
|||
|
||||
public:
|
||||
void set_movement(Vector2 movement);
|
||||
bool is_moving() const;
|
||||
|
||||
private:
|
||||
Ref<CharacterData> data{};
|
||||
|
|
@ -33,3 +35,32 @@ private:
|
|||
public:
|
||||
GET_SET_FNS(Ref<CharacterData>, data);
|
||||
};
|
||||
|
||||
class CharacterState : public Node {
|
||||
GDCLASS(CharacterState, Node);
|
||||
static void _bind_methods();
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
void switch_to_state(String state);
|
||||
void stack_state_dependent(String state);
|
||||
void stack_state_independent(String state);
|
||||
virtual void state_entered() {}
|
||||
virtual void state_exited() {}
|
||||
|
||||
public:
|
||||
void set_state_active(bool active);
|
||||
bool get_state_active() const;
|
||||
Character *get_character() const;
|
||||
|
||||
private:
|
||||
bool start_active{ false };
|
||||
bool state_active{ false };
|
||||
|
||||
Character *character{ nullptr };
|
||||
HashSet<CharacterState *> dependent_states{};
|
||||
CharacterState *depending_state{ nullptr };
|
||||
|
||||
public:
|
||||
GET_SET_FNS(bool, start_active);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
#include "player_character.h"
|
||||
#include "core/input/input.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
void PlayerCharacter::_bind_methods() {}
|
||||
|
||||
void PlayerCharacter::process(double delta) {
|
||||
Basis const basis{ get_viewport()->get_camera_3d()->get_global_basis() };
|
||||
Vector2 backward{ basis.get_column(2).x, basis.get_column(2).z };
|
||||
Vector2 right{ basis.get_column(0).x, basis.get_column(0).z };
|
||||
set_movement({ backward.normalized() * this->last_movement_input.x + right.normalized() * this->last_movement_input });
|
||||
}
|
||||
|
||||
void PlayerCharacter::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
set_process(true);
|
||||
set_process_unhandled_input(true);
|
||||
return;
|
||||
case NOTIFICATION_PROCESS:
|
||||
process(get_process_delta_time());
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerCharacter::unhandled_input(Ref<InputEvent> const &what) {
|
||||
if (what->is_action(input_move_left) || what->is_action(input_move_forward) || what->is_action(input_move_right) || what->is_action(input_move_backward)) {
|
||||
this->last_movement_input = {
|
||||
Input::get_singleton()->get_axis(input_move_left, input_move_right),
|
||||
Input::get_singleton()->get_axis(input_move_backward, input_move_forward)
|
||||
};
|
||||
get_viewport()->set_input_as_handled();
|
||||
}
|
||||
}
|
||||
|
||||
String const PlayerCharacter::input_move_left{ "move_left" };
|
||||
String const PlayerCharacter::input_move_right{ "move_right" };
|
||||
String const PlayerCharacter::input_move_forward{ "move_forward" };
|
||||
String const PlayerCharacter::input_move_backward{ "move_backward" };
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "authority/character.h"
|
||||
|
||||
class PlayerCharacter : public Character {
|
||||
GDCLASS(PlayerCharacter, Character);
|
||||
static void _bind_methods();
|
||||
|
||||
protected:
|
||||
void process(double delta);
|
||||
void _notification(int what);
|
||||
virtual void unhandled_input(Ref<InputEvent> const &what) override;
|
||||
|
||||
private:
|
||||
Vector2 last_movement_input{ 0, 0 };
|
||||
|
||||
static String const input_move_left;
|
||||
static String const input_move_right;
|
||||
static String const input_move_forward;
|
||||
static String const input_move_backward;
|
||||
};
|
||||
26
modules/authority/player_states.cpp
Normal file
26
modules/authority/player_states.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#include "player_states.h"
|
||||
|
||||
void PlayerInputState::_bind_methods() {}
|
||||
|
||||
void PlayerInputState::process(double delta) {}
|
||||
|
||||
void PlayerInputState::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_PROCESS:
|
||||
process(get_process_delta_time());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerInputState::state_entered() {
|
||||
set_process(true);
|
||||
}
|
||||
|
||||
void PlayerInputState::state_exited() {
|
||||
set_process(false);
|
||||
}
|
||||
32
modules/authority/player_states.h
Normal file
32
modules/authority/player_states.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "authority/character.h"
|
||||
|
||||
class PlayerInputState : public CharacterState {
|
||||
GDCLASS(PlayerInputState, CharacterState);
|
||||
static void _bind_methods();
|
||||
void process(double delta);
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
void unhandled_input(Ref<InputEvent> const &event) override;
|
||||
void state_entered() override;
|
||||
void state_exited() override;
|
||||
|
||||
public:
|
||||
Vector2 input{};
|
||||
};
|
||||
|
||||
class PlayerMovementState : public CharacterState {
|
||||
GDCLASS(PlayerMovementState, CharacterState);
|
||||
static void _bind_methods();
|
||||
void ready();
|
||||
void process(double delta);
|
||||
|
||||
protected:
|
||||
void state_entered() override;
|
||||
void state_exited() override;
|
||||
|
||||
private:
|
||||
Vector2 movement{};
|
||||
};
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
#include "register_types.h"
|
||||
|
||||
#include "authority/character.h"
|
||||
#include "authority/player_character.h"
|
||||
#include "core/object/class_db.h"
|
||||
|
||||
void initialize_authority_module(ModuleInitializationLevel p_level) {
|
||||
|
|
@ -10,7 +9,7 @@ void initialize_authority_module(ModuleInitializationLevel p_level) {
|
|||
}
|
||||
ClassDB::register_class<CharacterData>();
|
||||
ClassDB::register_class<Character>();
|
||||
ClassDB::register_class<PlayerCharacter>();
|
||||
ClassDB::register_class<CharacterState>();
|
||||
}
|
||||
|
||||
void uninitialize_authority_module(ModuleInitializationLevel p_level) {
|
||||
|
|
|
|||
4
project/data/default_player_character.tres
Normal file
4
project/data/default_player_character.tres
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[gd_resource type="CharacterData" format=3 uid="uid://bmudhddb0vedg"]
|
||||
|
||||
[resource]
|
||||
speed = 1.0
|
||||
|
|
@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.cte
|
|||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
|
|
@ -25,6 +27,10 @@ mipmaps/generate=false
|
|||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
|
|
|
|||
16
project/objects/player_character.tscn
Normal file
16
project/objects/player_character.tscn
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[gd_scene format=3 uid="uid://dcqd0wo5y5a1g"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_vcg8s"]
|
||||
|
||||
[sub_resource type="CylinderMesh" id="CylinderMesh_5kd2n"]
|
||||
|
||||
[node name="PlayerCharacter" type="PlayerCharacter" unique_id=1435471129]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=511026275]
|
||||
shape = SubResource("CapsuleShape3D_vcg8s")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=957221075]
|
||||
mesh = SubResource("CylinderMesh_5kd2n")
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="." unique_id=932811285]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.90373915, 0.42808357, 0, -0.42808357, 0.90373915, 0, 2.1840205, 3.7269862)
|
||||
|
|
@ -8,8 +8,46 @@
|
|||
|
||||
config_version=5
|
||||
|
||||
[animation]
|
||||
|
||||
compatibility/default_parent_skeleton_in_mesh_instance_3d=true
|
||||
|
||||
[application]
|
||||
|
||||
config/name="authority"
|
||||
config/features=PackedStringArray("4.4", "Forward Plus")
|
||||
run/main_scene="uid://cv0ub3llm3jew"
|
||||
config/features=PackedStringArray("4.6", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[display]
|
||||
|
||||
window/size/viewport_width=1920
|
||||
window/size/viewport_height=1080
|
||||
window/size/mode=3
|
||||
|
||||
[input]
|
||||
|
||||
move_left={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_right={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_forward={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_backward={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[physics]
|
||||
|
||||
3d/physics_engine="Jolt Physics"
|
||||
|
|
|
|||
41
project/scenes/test_world.tscn
Normal file
41
project/scenes/test_world.tscn
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
[gd_scene format=3 uid="uid://cv0ub3llm3jew"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://dcqd0wo5y5a1g" path="res://objects/player_character.tscn" id="1_kyfjp"]
|
||||
[ext_resource type="CharacterData" uid="uid://bmudhddb0vedg" path="res://data/default_player_character.tres" id="2_amxg5"]
|
||||
|
||||
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_kyfjp"]
|
||||
sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||
ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||
|
||||
[sub_resource type="Sky" id="Sky_amxg5"]
|
||||
sky_material = SubResource("ProceduralSkyMaterial_kyfjp")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_3263u"]
|
||||
background_mode = 2
|
||||
sky = SubResource("Sky_amxg5")
|
||||
tonemap_mode = 2
|
||||
glow_enabled = true
|
||||
|
||||
[node name="TestWorld" type="Node3D" unique_id=262419127]
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1185961481]
|
||||
environment = SubResource("Environment_3263u")
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1382994887]
|
||||
transform = Transform3D(-0.8660254, -0.43301278, 0.25, 0, 0.49999997, 0.86602545, -0.50000006, 0.75, -0.43301266, 0, 0, 0)
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="PlayerCharacter" parent="." unique_id=1435471129 instance=ExtResource("1_kyfjp")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
data = ExtResource("2_amxg5")
|
||||
|
||||
[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=885387983]
|
||||
use_collision = true
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1853081325]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.5, 0)
|
||||
size = Vector3(100, 1, 100)
|
||||
|
||||
[node name="CSGBox3D2" type="CSGBox3D" parent="CSGCombiner3D" unique_id=40055740]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.293318, 1.1054688, -6.6544046)
|
||||
size = Vector3(5.5302734, 2.2109375, 3.6298828)
|
||||
Loading…
Reference in a new issue