diff --git a/godot/Main.gdns b/godot/Main.gdns index 764a788..524516e 100644 --- a/godot/Main.gdns +++ b/godot/Main.gdns @@ -1,6 +1,6 @@ [gd_resource type="NativeScript" load_steps=2 format=2] -[ext_resource path="res://gdnative/libalai.tres" type="GDNativeLibrary" id=1] +[ext_resource path="res://gdnative/alai.tres" type="GDNativeLibrary" id=1] [resource] resource_name = "Main" diff --git a/godot/Main.tscn b/godot/Main.tscn index 9d1190c..d264a5c 100644 --- a/godot/Main.tscn +++ b/godot/Main.tscn @@ -7,9 +7,3 @@ script = ExtResource( 1 ) [node name="Level1" parent="." instance=ExtResource( 2 )] - -[node name="StateMachine" type="Node" parent="."] - -[node name="Walk" type="Node" parent="StateMachine"] - -[node name="Run" type="Node" parent="StateMachine"] diff --git a/godot/characters/player/Player.gdns b/godot/characters/player/Player.gdns index 40af8ae..4553176 100644 --- a/godot/characters/player/Player.gdns +++ b/godot/characters/player/Player.gdns @@ -1,8 +1,13 @@ [gd_resource type="NativeScript" load_steps=2 format=2] -[ext_resource path="res://gdnative/libalai.tres" type="GDNativeLibrary" id=1] +[sub_resource type="GDNativeLibrary" id=1] +symbol_prefix = "godot_player" +entry/Windows.64 = "res://gdnative/windows.64/libalai.dll" +entry/X11.64 = "res://gdnative/linux.64/libalai.so" +dependency/Windows.64 = [ ] +dependency/X11.64 = [ ] [resource] resource_name = "Player" class_name = "Player" -library = ExtResource( 1 ) +library = SubResource( 1 ) diff --git a/godot/characters/player/Player.tscn b/godot/characters/player/Player.tscn index b5643c5..fd71fe2 100644 --- a/godot/characters/player/Player.tscn +++ b/godot/characters/player/Player.tscn @@ -1,13 +1,18 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=9 format=2] [ext_resource path="res://characters/player/sprites/green.tres" type="SpriteFrames" id=1] -[ext_resource path="res://characters/player/Player.gdns" type="Script" id=3] +[ext_resource path="res://characters/player/states/Idle.gdns" type="Script" id=2] +[ext_resource path="res://state_machine/StateMachine.gdns" type="Script" id=3] +[ext_resource path="res://characters/player/states/Move.gdns" type="Script" id=4] +[ext_resource path="res://characters/player/Player.gdns" type="Script" id=5] +[ext_resource path="res://characters/player/states/Jump.gdns" type="Script" id=6] +[ext_resource path="res://characters/player/states/Fall.gdns" type="Script" id=7] [sub_resource type="RectangleShape2D" id=1] -extents = Vector2( 7, 11.5 ) +extents = Vector2( 7, 12 ) [node name="Player" type="KinematicBody2D"] -script = ExtResource( 3 ) +script = ExtResource( 5 ) [node name="AnimatedSprite" type="AnimatedSprite" parent="."] frames = ExtResource( 1 ) @@ -15,17 +20,21 @@ animation = "idle" centered = false [node name="CollisionShape2D" type="CollisionShape2D" parent="."] -position = Vector2( 12, 12.5 ) +position = Vector2( 12, 12 ) shape = SubResource( 1 ) [node name="StateMachine" type="Node" parent="."] +script = ExtResource( 3 ) +default_state = "Idle" [node name="Idle" type="Node" parent="StateMachine"] +script = ExtResource( 2 ) [node name="Move" type="Node" parent="StateMachine"] +script = ExtResource( 4 ) [node name="Jump" type="Node" parent="StateMachine"] - -[node name="DoubleJump" type="Node" parent="StateMachine"] +script = ExtResource( 6 ) [node name="Fall" type="Node" parent="StateMachine"] +script = ExtResource( 7 ) diff --git a/godot/characters/player/states/Fall.gdns b/godot/characters/player/states/Fall.gdns new file mode 100644 index 0000000..520e3af --- /dev/null +++ b/godot/characters/player/states/Fall.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gdnative/alai.tres" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "PlayerFall" +class_name = "PlayerFall" +library = ExtResource( 1 ) diff --git a/godot/characters/player/states/Idle.gdns b/godot/characters/player/states/Idle.gdns new file mode 100644 index 0000000..4e5fb7d --- /dev/null +++ b/godot/characters/player/states/Idle.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gdnative/alai.tres" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "PlayerIdle" +class_name = "PlayerIdle" +library = ExtResource( 1 ) diff --git a/godot/characters/player/states/Jump.gdns b/godot/characters/player/states/Jump.gdns new file mode 100644 index 0000000..915bbca --- /dev/null +++ b/godot/characters/player/states/Jump.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gdnative/alai.tres" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "PlayerJump" +class_name = "PlayerJump" +library = ExtResource( 1 ) diff --git a/godot/characters/player/states/Move.gdns b/godot/characters/player/states/Move.gdns new file mode 100644 index 0000000..5b43556 --- /dev/null +++ b/godot/characters/player/states/Move.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gdnative/alai.tres" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "PlayerMove" +class_name = "PlayerMove" +library = ExtResource( 1 ) diff --git a/godot/gdnative/libalai.tres b/godot/gdnative/alai.tres similarity index 100% rename from godot/gdnative/libalai.tres rename to godot/gdnative/alai.tres diff --git a/godot/state_machine/State.gdns b/godot/state_machine/State.gdns new file mode 100644 index 0000000..dad8e97 --- /dev/null +++ b/godot/state_machine/State.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gdnative/libalai.tres" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "State" +class_name = "State" +library = ExtResource( 1 ) diff --git a/godot/state_machine/StateMachine.gdns b/godot/state_machine/StateMachine.gdns new file mode 100644 index 0000000..77bc502 --- /dev/null +++ b/godot/state_machine/StateMachine.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gdnative/alai.tres" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "StateMachine" +class_name = "StateMachine" +library = ExtResource( 1 ) diff --git a/src/Main.cpp b/src/Main.cpp index 315bf3b..ef0fe55 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -3,14 +3,15 @@ #include using namespace godot; +using namespace main; void Main::_register_methods() { register_method("_ready", &Main::_ready); register_method("_physics_process", &Main::_physics_process); - register_property("full_screen", &Main::set_full_screen, &Main::get_full_screen, ALAI_MAIN_FULL_SCREEN); - register_property("window_size", &Main::set_window_size, &Main::get_window_size, ALAI_MAIN_WINDOW_SIZE); - register_property("launch_screen", &Main::set_launch_screen, &Main::get_launch_screen, ALAI_MAIN_LAUNCH_SCREEN); + register_property("full_screen", &Main::set_full_screen, &Main::get_full_screen, main::full_screen); + register_property("window_size", &Main::set_window_size, &Main::get_window_size, main::window_size); + register_property("launch_screen", &Main::set_launch_screen, &Main::get_launch_screen, main::launch_screen); } Main::Main() @@ -26,9 +27,9 @@ void Main::_init() _os = OS::get_singleton(); _input = Input::get_singleton(); - full_screen = ALAI_MAIN_FULL_SCREEN; - window_size = ALAI_MAIN_WINDOW_SIZE; - launch_screen = ALAI_MAIN_LAUNCH_SCREEN; + full_screen = main::full_screen; + window_size = main::window_size; + launch_screen = main::launch_screen; } void Main::_ready() @@ -39,17 +40,10 @@ void Main::_ready() } else { - String resolution = String("Resolution before: " + String().num(_os->get_window_size().x) + "x" + String().num(_os->get_window_size().y)); - Godot::print(resolution); - _os->set_window_size(window_size); _os->set_window_position( _os->get_screen_position(get_launch_screen()) + _os->get_screen_size() * 0.5 - _os->get_window_size() * 0.5 ); - - resolution = String("Resolution after: " + String().num(_os->get_window_size().x) + "x" + String().num(_os->get_window_size().y)); - - Godot::print(resolution); } } diff --git a/src/Main.h b/src/Main.h index 71c9253..3a65398 100644 --- a/src/Main.h +++ b/src/Main.h @@ -6,138 +6,130 @@ #include #include -/** - * @brief If the game should be full screen or not by default. - */ -#define ALAI_MAIN_FULL_SCREEN false -/** - * @brief The default size of the window. - * @details This is ignored if full screen is true. - */ -#define ALAI_MAIN_WINDOW_SIZE Vector2(1280, 720) -/** - * @brief Which screen to launch the game on. - * @details If -1 it will launch the game on the "active" screen. Anything between 0 and N represents the screen number to show the game on when opened. - */ -#define ALAI_MAIN_LAUNCH_SCREEN -1 - /** * @brief This is the godot namespace for all the code included in the library. * @details This namespace is used a prefix when the Godot engine looks for classes, methods, and properties. */ namespace godot { - /** - * @brief This class controls the Main node. - * @details The main node is responsible for controling the window and the game iteself is a child of it. - */ - class Main : public Node + namespace main { - GODOT_CLASS(Main, Node) + const bool full_screen = false; + const Vector2 window_size = Vector2(1280, 720); + const int8_t launch_screen = -1; - private: - /** - * @brief OS singleton. - */ - OS *_os; - /** - * @brief Input singleton. - */ - Input *_input; + /** + * @brief This class controls the Main node. + * @details The main node is responsible for controling the window and the game iteself is a child of it. + */ + class Main : public Node + { + GODOT_CLASS(Main, Node) - /** - * @brief If the window is full screen or not. - */ - bool full_screen; - /** - * @brief The size of the window. - */ - Vector2 window_size; - /** - * @brief The screen to launch the game on. - */ - int8_t launch_screen; + private: + /** + * @brief OS singleton. + */ + OS *_os; + /** + * @brief Input singleton. + */ + Input *_input; - public: - /** - * @brief This method registers classes with Godot. - * @details This method registers methods, properties, and signals with the Godot engine. - */ - static void _register_methods(); + /** + * @brief If the window is full screen or not. + */ + bool full_screen; + /** + * @brief The size of the window. + */ + Vector2 window_size; + /** + * @brief The screen to launch the game on. + */ + int8_t launch_screen; - /** - * @brief Construct a new Main object. - */ - Main(); + public: + /** + * @brief This method registers classes with Godot. + * @details This method registers methods, properties, and signals with the Godot engine. + */ + static void _register_methods(); - /** - * @brief Destroy the Main object. - */ - ~Main(); + /** + * @brief Construct a new Main object. + */ + Main(); - /** - * @brief Initialize the class from Godot. - * @details This method is called just once when the Godot engine connects to the instance of the class. - */ - void _init(); + /** + * @brief Destroy the Main object. + */ + ~Main(); - /** - * @brief Code to be run when ready. - * @details This method is run when all the children of this node are ready. - */ - void _ready(); + /** + * @brief Initialize the class from Godot. + * @details This method is called just once when the Godot engine connects to the instance of the class. + */ + void _init(); - /** - * @brief This class handles the physics processing. - * @details Every delta time, this function is called to check for input and update positioning. - * - * @param[in] delta The difference in time that passed since the last call to this method. - */ - void _physics_process(float delta); + /** + * @brief Code to be run when ready. + * @details This method is run when all the children of this node are ready. + */ + void _ready(); - /** - * @brief Set the full screen object. - * - * @param[in] new_full_screen The new full screen state. - */ - void set_full_screen(bool new_full_screen); + /** + * @brief This class handles the physics processing. + * @details Every delta time, this function is called to check for input and update positioning. + * + * @param[in] delta The difference in time that passed since the last call to this method. + */ + void _physics_process(float delta); - /** - * @brief Get the full screen object. - * - * @return true If full screen. - * @return false If not full screen. - */ - bool get_full_screen(); + /** + * @brief Set the full screen object. + * + * @param[in] new_full_screen The new full screen state. + */ + void set_full_screen(bool new_full_screen); - /** - * @brief Set the window size object. - * - * @param[in] new_window_size The new window size. - */ - void set_window_size(Vector2 new_window_size); + /** + * @brief Get the full screen object. + * + * @return true If full screen. + * @return false If not full screen. + */ + bool get_full_screen(); - /** - * @brief Get the window size object. - * - * @return Vector2 The window size. - */ - Vector2 get_window_size(); + /** + * @brief Set the window size object. + * + * @param[in] new_window_size The new window size. + */ + void set_window_size(Vector2 new_window_size); - /** - * @brief Set the launch screen object. - * - * @param[in] new_launch_screen The launch screen to use. - */ - void set_launch_screen(int8_t new_launch_screen); + /** + * @brief Get the window size object. + * + * @return Vector2 The window size. + */ + Vector2 get_window_size(); - /** - * @brief Get the launch screen object. - * - * @return int8_t The launch screen. - */ - int8_t get_launch_screen(); - }; + /** + * @brief Set the launch screen object. + * + * @param[in] new_launch_screen The launch screen to use. + */ + void set_launch_screen(int8_t new_launch_screen); + + /** + * @brief Get the launch screen object. + * + * @return int8_t The launch screen. + */ + int8_t get_launch_screen(); + }; + } } #endif diff --git a/src/godot.cpp b/src/godot.cpp index d8f50b7..b449e46 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -1,12 +1,13 @@ #include -#include "StateMachine.h" -#include "State.h" -#include "Walk.h" -#include "Run.h" +#include "state_machine/StateMachine.h" +#include "state_machine/State.h" #include "Main.h" -#include "Player.h" -#include "PlayerIdle.h" +#include "player/Player.h" +#include "player/states/PlayerIdle.h" +#include "player/states/PlayerMove.h" +#include "player/states/PlayerJump.h" +#include "player/states/PlayerFall.h" using namespace godot; @@ -28,9 +29,10 @@ extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) Godot::nativescript_init(handle); register_class(); register_class(); - register_class(); - register_class(); - register_class
(); - register_class(); - register_class(); + register_class(); + register_class(); + register_class(); + register_class(); + register_class(); + register_class(); } diff --git a/src/player/Player.cpp b/src/player/Player.cpp index 0b0e0fe..30c801d 100644 --- a/src/player/Player.cpp +++ b/src/player/Player.cpp @@ -7,18 +7,20 @@ #include using namespace godot; +using namespace player; void Player::_register_methods() { register_method("_ready", &Player::_ready); register_method("_physics_process", &Player::_physics_process); - register_method("_on_Player_player_moved", &Player::_on_Player_player_moved); + register_method("set_velocity", &Player::set_velocity); + register_method("get_velocity", &Player::get_velocity); //register_property>("sprite_frames", &Player::set_sprite_frames, &Player::get_sprite_frames, Ref(), GODOT_METHOD_RPC_MODE_DISABLED, GODOT_PROPERTY_USAGE_DEFAULT, GODOT_PROPERTY_HINT_RESOURCE_TYPE, String("SpriteFrames")); - register_property("speed", &Player::set_speed, &Player::get_speed, ALAI_PLAYER_SPEED); - register_property("jump_force", &Player::set_jump_force, &Player::get_jump_force, ALAI_PLAYER_JUMP_FORCE); - register_property("gravity", &Player::set_gravity, &Player::get_gravity, ALAI_PLAYER_GRAVITY); - register_property("run_speed", &Player::set_run_speed, &Player::get_run_speed, ALAI_PLAYER_RUN_SPEED); - register_signal("player_moved", "position", GODOT_VARIANT_TYPE_VECTOR2); + register_property("speed", &Player::set_speed, &Player::get_speed, player::speed); + register_property("jump_force", &Player::set_jump_force, &Player::get_jump_force, player::jump_force); + register_property("gravity", &Player::set_gravity, &Player::get_gravity, player::gravity); + register_property("run_speed", &Player::set_run_speed, &Player::get_run_speed, player::run_speed); + register_property("double_jump", &Player::set_double_jump, &Player::get_double_jump, player::double_jump); } Player::Player() @@ -31,21 +33,18 @@ Player::~Player() void Player::_init() { - _os = OS::get_singleton(); - _input = Input::get_singleton(); _resource_loader = ResourceLoader::get_singleton(); - //sprite_frames = _resource_loader->load(ALAI_PLAYER_SPRITE_FRAMES); - set_speed(ALAI_PLAYER_SPEED); - set_jump_force(ALAI_PLAYER_JUMP_FORCE); - set_gravity(ALAI_PLAYER_GRAVITY); - set_run_speed(ALAI_PLAYER_RUN_SPEED); + //sprite_frames = _resource_loader->load(player::sprite_frames); + set_speed(player::speed); + set_jump_force(player::jump_force); + set_gravity(player::gravity); + set_run_speed(player::run_speed); + set_double_jump(player::double_jump); coins = 0; velocity = Vector2(); - - jumping = 0; } void Player::_ready() @@ -53,7 +52,7 @@ void Player::_ready() animated_sprite = get_node("AnimatedSprite"); if (!animated_sprite) { - WARN_PRINT("AnimateSprite not found!"); + ERR_PRINT("AnimateSprite not found!"); animated_sprite = AnimatedSprite()._new(); } //animated_sprite->set_sprite_frames(sprite_frames); @@ -74,74 +73,7 @@ void Player::_ready() void Player::_physics_process(float delta) { - auto current_speed = get_speed(); - velocity.x = 0; - if (_input->is_action_pressed("run")) - { - current_speed *= get_run_speed(); - } - - if (_input->is_action_pressed("right")) - { - velocity.x += current_speed; - } - - if (_input->is_action_pressed("left")) - { - velocity.x += -current_speed; - } - - if (velocity.x > 0) - { - animated_sprite->play("move"); - animated_sprite->set_flip_h(true); - } - else if (velocity.x < 0) - { - animated_sprite->play("move"); - animated_sprite->set_flip_h(false); - } - else - { - animated_sprite->stop(); - animated_sprite->set_animation("idle"); - } - - if (jumping > 0 && is_on_floor()) - { - animated_sprite->set_frame(1); - jumping = 0; - } - - if (!is_on_floor()) - { - animated_sprite->stop(); - animated_sprite->set_animation("air"); - if (jumping == 0) - { - jumping = 2; - } - } - velocity.y += get_gravity(); - if (_input->is_action_just_pressed("jump") && jumping < 2) - { - velocity.y = -get_jump_force(); - jumping++; - } - - if (_input->is_action_just_released("jump")) - { - if (velocity.y < -100) - { - velocity.y = -100; - } - } - - if (velocity.x < 0 || velocity.x > 0) - { - //emit_signal("player_moved", get_position()); - } velocity = move_and_slide(velocity, Vector2::UP, true); velocity.x = Math::lerp((float) velocity.x, (float) 0, (float) 0.2); @@ -170,7 +102,6 @@ void Player::_physics_process(float delta) { if (get_parent()->get_class() == "TileMap") { - Godot::print("Off screen"); auto error = get_tree()->change_scene("res://Main.tscn"); if (error != Error::OK) { @@ -231,7 +162,22 @@ float Player::get_run_speed() return run_speed; } -void Player::_on_Player_player_moved(Vector2 position) +void Player::set_double_jump(bool double_jump) { - Godot::print(position); + this->double_jump = double_jump; +} + +bool Player::get_double_jump() +{ + return this->double_jump; +} + +void Player::set_velocity(Vector2 velocity) +{ + this->velocity = velocity; +} + +Vector2 Player::get_velocity() +{ + return this->velocity; } diff --git a/src/player/Player.h b/src/player/Player.h index aca9b16..67ff8c4 100644 --- a/src/player/Player.h +++ b/src/player/Player.h @@ -1,37 +1,12 @@ #ifndef ALAI_PLAYER_H #define ALAI_PLAYER_H -/** - * @brief This resource is loaded by default for the AnimatedSprite node. - */ -#define ALAI_PLAYER_SPRITE_FRAMES "res://characters/player/sprites/green.tres" -/** - * @brief The speed the player should move it. - */ -#define ALAI_PLAYER_SPEED 60.0 -/** - * @brief The force applied to the player when jumping. - */ -#define ALAI_PLAYER_JUMP_FORCE 300.0 -/** - * @brief The gravity applied to the player. - */ -#define ALAI_PLAYER_GRAVITY 9.81 -/** - * @brief The multiplier used to change the speed of the player when running. - */ -#define ALAI_PLAYER_RUN_SPEED 2.0 - #include #include -#include #include -#include -#include #include #include #include -#include /** * @brief This is the godot namespace for all the code included in the library. @@ -39,181 +14,178 @@ */ namespace godot { - /** - * @brief This class is used to control the player. - * @details This class allows the player to move, run, and jump as well as controls the sprite displayed for those actions. - */ - class Player : public KinematicBody2D + namespace player { - GODOT_CLASS(Player, KinematicBody2D) + const char player_sprite_frames[] = "res://characters/player/sprites/green.tres"; + const float speed = 60.0; + const float jump_force = 300.0; + const float gravity = 9.81; + const float run_speed = 2.0; + const bool double_jump = true; - private: - /** - * @brief OS singleton. - */ - OS *_os; - /** - * @brief Input singleton. - */ - Input *_input; - /** - * @brief ResourceLoader singleton. - */ - ResourceLoader *_resource_loader; + /** + * @brief This class is used to control the player. + * @details This class allows the player to move, run, and jump as well as controls the sprite displayed for those actions. + */ + class Player : public KinematicBody2D + { + GODOT_CLASS(Player, KinematicBody2D) - /** - * @brief The animated sprite connected to the KinematicBody2D. - */ - AnimatedSprite *animated_sprite; - /** - * @brief The sprite frames used in the animated sprite. - */ - Ref sprite_frames; - /** - * @brief The coins the player has collected. - */ - uint8_t coins; - /** - * @brief The velocity at which moves the player moves. - */ - Vector2 velocity; - /** - * @brief The speed that the player moves in. - */ - float speed; - /** - * @brief The force applied to the player when jumping. - */ - float jump_force; - /** - * @brief The gravity applied to the player. - */ - float gravity; - /** - * @brief The speed multiplier used to make the player move faster. - */ - float run_speed; + private: + /** + * @brief ResourceLoader singleton. + */ + ResourceLoader *_resource_loader; - /** - * @brief State of jumping of the player. To be replaced with a state machine in the future. - */ - uint8_t jumping; + /** + * @brief The animated sprite connected to the KinematicBody2D. + */ + AnimatedSprite *animated_sprite; + /** + * @brief The sprite frames used in the animated sprite. + */ + Ref sprite_frames; + /** + * @brief The coins the player has collected. + */ + uint8_t coins; + /** + * @brief The velocity at which moves the player moves. + */ + Vector2 velocity; + /** + * @brief The speed that the player moves in. + */ + float speed; + /** + * @brief The force applied to the player when jumping. + */ + float jump_force; + /** + * @brief The gravity applied to the player. + */ + float gravity; + /** + * @brief The speed multiplier used to make the player move faster. + */ + float run_speed; - public: - /** - * @brief This method registers classes with Godot. - * @details This method registers methods, properties, and signals with the Godot engine. - */ - static void _register_methods(); + bool double_jump; - /** - * @brief Construct a new Player object. - */ - Player(); + public: + /** + * @brief This method registers classes with Godot. + * @details This method registers methods, properties, and signals with the Godot engine. + */ + static void _register_methods(); - /** - * @brief Destroy the Player object. - */ - ~Player(); + /** + * @brief Construct a new Player object. + */ + Player(); - /** - * @brief Initialize the class from Godot. - * @details This method is called just once when the Godot engine connects to the instance of the class. - */ - void _init(); + /** + * @brief Destroy the Player object. + */ + ~Player(); - /** - * @brief Code to be run when ready. - * @details This method is run when all the children of this node are ready. - */ - void _ready(); + /** + * @brief Initialize the class from Godot. + * @details This method is called just once when the Godot engine connects to the instance of the class. + */ + void _init(); - /** - * @brief This class handles the physics processing. - * @details Every delta time, this function is called to check for input and update positioning. - * - * @param[in] delta The difference in time that passed since the last call to this method. - */ - void _physics_process(float delta); + /** + * @brief Code to be run when ready. + * @details This method is run when all the children of this node are ready. + */ + void _ready(); - /** - * @brief Set the sprite frames object. - * - * @param[in] new_sprite_frames The new sprite frame. - */ - void set_sprite_frames(Ref new_sprite_frames); + /** + * @brief This class handles the physics processing. + * @details Every delta time, this function is called to check for input and update positioning. + * + * @param[in] delta The difference in time that passed since the last call to this method. + */ + void _physics_process(float delta); - /** - * @brief Get the sprite frames object. - * - * @return Ref A reference to the sprite frames object. - */ - Ref get_sprite_frames(); + /** + * @brief Set the sprite frames object. + * + * @param[in] new_sprite_frames The new sprite frame. + */ + void set_sprite_frames(Ref new_sprite_frames); - /** - * @brief Set the speed object. - * - * @param[in] new_speed The new speed. - */ - void set_speed(float new_speed); + /** + * @brief Get the sprite frames object. + * + * @return Ref A reference to the sprite frames object. + */ + Ref get_sprite_frames(); - /** - * @brief Get the speed object. - * - * @return float The current speed of the player. - */ - float get_speed(); + /** + * @brief Set the speed object. + * + * @param[in] new_speed The new speed. + */ + void set_speed(float new_speed); - /** - * @brief Set the jump force object. - * - * @param[in] new_jump_force The new force applied to the player to make him jump. - */ - void set_jump_force(float new_jump_force); + /** + * @brief Get the speed object. + * + * @return float The current speed of the player. + */ + float get_speed(); - /** - * @brief Get the jump force object. - * - * @return float The current force being applied to the player. - */ - float get_jump_force(); + /** + * @brief Set the jump force object. + * + * @param[in] new_jump_force The new force applied to the player to make him jump. + */ + void set_jump_force(float new_jump_force); - /** - * @brief Set the gravity object. - * - * @param[in] new_gravity The new gravity to apply to the player. - */ - void set_gravity(float new_gravity); + /** + * @brief Get the jump force object. + * + * @return float The current force being applied to the player. + */ + float get_jump_force(); - /** - * @brief Get the gravity object. - * - * @return float The current gravity applied to the player. - */ - float get_gravity(); + /** + * @brief Set the gravity object. + * + * @param[in] new_gravity The new gravity to apply to the player. + */ + void set_gravity(float new_gravity); - /** - * @brief Set the run speed object. - * - * @param[in] new_run_speed The new speed for running. - */ - void set_run_speed(float new_run_speed); + /** + * @brief Get the gravity object. + * + * @return float The current gravity applied to the player. + */ + float get_gravity(); - /** - * @brief Get the run speed object. - * - * @return float The current run speed of the player. - */ - float get_run_speed(); + /** + * @brief Set the run speed object. + * + * @param[in] new_run_speed The new speed for running. + */ + void set_run_speed(float new_run_speed); - /** - * @brief This signal is called when the player moves. - * @details The jump action is not included in this signal, just moving left or right. - * - * @param[in] position The new position of the player. - */ - void _on_Player_player_moved(Vector2 position); - }; + /** + * @brief Get the run speed object. + * + * @return float The current run speed of the player. + */ + float get_run_speed(); + + void set_double_jump(bool double_jump); + bool get_double_jump(); + + void set_velocity(Vector2 velocity); + Vector2 get_velocity(); + }; + } } #endif diff --git a/src/player/states/PlayerFall.cpp b/src/player/states/PlayerFall.cpp new file mode 100644 index 0000000..c3e6ff4 --- /dev/null +++ b/src/player/states/PlayerFall.cpp @@ -0,0 +1,78 @@ +#include "player/states/PlayerFall.h" +#include "player/Player.h" + +using namespace godot; +using namespace player; + +void PlayerFall::_register_methods() +{ + register_method("_state_enter", &PlayerFall::_state_enter); + register_method("_state_exit", &PlayerFall::_state_exit); + register_method("_physics_process", &PlayerFall::_physics_process); +} + +PlayerFall::PlayerFall() +{ +} + +PlayerFall::~PlayerFall() +{ +} + +void PlayerFall::_init() +{ + _input = Input::get_singleton(); +} + +void PlayerFall::_state_enter() +{ + animated_sprite = get_parent()->get_node("AnimatedSprite"); + animated_sprite->stop(); + animated_sprite->set_animation("air"); +} + +void PlayerFall::_state_exit() +{ + animated_sprite->set_animation("move"); + animated_sprite->set_frame(1); +} + +void PlayerFall::_physics_process(float delta) +{ + auto parent = Object::cast_to(get_parent()); + + if (parent->is_on_floor()) + { + get_state_machine()->change("Move"); + return; + } + + auto current_speed = parent->get_speed(); + auto velocity = parent->get_velocity(); + velocity.x = 0; + if (_input->is_action_pressed("run")) + { + current_speed *= parent->get_run_speed(); + } + + if (_input->is_action_pressed("right")) + { + velocity.x += current_speed; + } + + if (_input->is_action_pressed("left")) + { + velocity.x += -current_speed; + } + + if (velocity.x > 0) + { + animated_sprite->set_flip_h(true); + } + else if (velocity.x < 0) + { + animated_sprite->set_flip_h(false); + } + + parent->set_velocity(velocity); +} diff --git a/src/player/states/PlayerFall.h b/src/player/states/PlayerFall.h new file mode 100644 index 0000000..4fcbf83 --- /dev/null +++ b/src/player/states/PlayerFall.h @@ -0,0 +1,44 @@ +#ifndef JUEGO_PLAYER_FALL_H +#define JUEGO_PLAYER_FALL_H + +#include "state_machine/State.h" + +#include +#include +#include + +namespace godot +{ + namespace player + { + class PlayerFall : public State + { + GODOT_CLASS(PlayerFall, State) + + private: + Input *_input; + AnimatedSprite *animated_sprite; + + public: + static void _register_methods(); + + PlayerFall(); + + ~PlayerFall(); + + /** + * @brief Initialize the class from Godot. + * @details This method is called just once when the Godot engine connects to the instance of the class. + */ + void _init(); + + void _state_enter(); + + void _state_exit(); + + void _physics_process(float delta); + }; + } +} + +#endif diff --git a/src/player/states/PlayerIdle.cpp b/src/player/states/PlayerIdle.cpp new file mode 100644 index 0000000..026b5cf --- /dev/null +++ b/src/player/states/PlayerIdle.cpp @@ -0,0 +1,65 @@ +#include "player/states/PlayerIdle.h" +#include "player/Player.h" + +using namespace godot; +using namespace player; + +void PlayerIdle::_register_methods() +{ + register_method("_state_enter", &PlayerIdle::_state_enter); + register_method("_state_exit", &PlayerIdle::_state_exit); + register_method("_physics_process", &PlayerIdle::_physics_process); +} + +PlayerIdle::PlayerIdle() +{ +} + +PlayerIdle::~PlayerIdle() +{ +} + +void PlayerIdle::_init() +{ + _input = Input::get_singleton(); +} + +void PlayerIdle::_state_enter() +{ + animated_sprite = get_parent()->get_node("AnimatedSprite"); + animated_sprite->stop(); + animated_sprite->set_animation("idle"); +} + +void PlayerIdle::_state_exit() +{ +} + +void PlayerIdle::_physics_process(float delta) +{ + auto parent = Object::cast_to(get_parent()); + + if (_input->is_action_pressed("right")) + { + get_state_machine()->change("Move"); + return; + } + + if (_input->is_action_pressed("left")) + { + get_state_machine()->change("Move"); + return; + } + + if (_input->is_action_just_pressed("jump")) + { + get_state_machine()->change("Jump"); + return; + } + + if (!parent->is_on_floor()) + { + get_state_machine()->change("Fall"); + return; + } +} diff --git a/src/player/states/PlayerIdle.h b/src/player/states/PlayerIdle.h new file mode 100644 index 0000000..631aef4 --- /dev/null +++ b/src/player/states/PlayerIdle.h @@ -0,0 +1,45 @@ +#ifndef JUEGO_PLAYER_IDLE_H +#define JUEGO_PLAYER_IDLE_H + +#include "state_machine/State.h" + +#include +#include +#include +#include + +namespace godot +{ + namespace player + { + class PlayerIdle : public State + { + GODOT_CLASS(PlayerIdle, State) + + private: + Input *_input; + AnimatedSprite *animated_sprite; + + public: + static void _register_methods(); + + PlayerIdle(); + + ~PlayerIdle(); + + /** + * @brief Initialize the class from Godot. + * @details This method is called just once when the Godot engine connects to the instance of the class. + */ + void _init(); + + void _state_enter(); + + void _state_exit(); + + void _physics_process(float delta); + }; + } +} + +#endif diff --git a/src/player/states/PlayerJump.cpp b/src/player/states/PlayerJump.cpp new file mode 100644 index 0000000..8255e1b --- /dev/null +++ b/src/player/states/PlayerJump.cpp @@ -0,0 +1,105 @@ +#include "player/states/PlayerJump.h" +#include "player/Player.h" + +using namespace godot; +using namespace player; + +void PlayerJump::_register_methods() +{ + register_method("_state_enter", &PlayerJump::_state_enter); + register_method("_state_exit", &PlayerJump::_state_exit); + register_method("_physics_process", &PlayerJump::_physics_process); +} + +PlayerJump::PlayerJump() +{ +} + +PlayerJump::~PlayerJump() +{ +} + +void PlayerJump::_init() +{ + _input = Input::get_singleton(); +} + +void PlayerJump::_state_enter(const String state) +{ + animated_sprite = get_parent()->get_node("AnimatedSprite"); + animated_sprite->stop(); + animated_sprite->set_animation("air"); + + if (state == "Jump") + { + double_jumped = true; + } + else + { + double_jumped = false; + } + + auto parent = Object::cast_to(get_parent()); + auto velocity = parent->get_velocity(); + velocity.y = -parent->get_jump_force(); + parent->set_velocity(velocity); +} + +void PlayerJump::_state_exit() +{ + animated_sprite->set_animation("move"); + animated_sprite->set_frame(1); +} + +void PlayerJump::_physics_process(float delta) +{ + auto parent = Object::cast_to(get_parent()); + if (parent->is_on_floor()) + { + get_state_machine()->change("Move"); + return; + } + + auto current_speed = parent->get_speed(); + auto velocity = parent->get_velocity(); + velocity.x = 0; + if (_input->is_action_pressed("run")) + { + current_speed *= parent->get_run_speed(); + } + + if (_input->is_action_pressed("right")) + { + velocity.x += current_speed; + } + + if (_input->is_action_pressed("left")) + { + velocity.x += -current_speed; + } + + if (_input->is_action_just_released("jump")) + { + if (velocity.y < -100) + { + velocity.y = -100; + } + } + + if (parent->get_double_jump() && !double_jumped && _input->is_action_just_pressed("jump")) + { + get_state_machine()->change("Jump"); + return; + } + + if (velocity.x > 0) + { + animated_sprite->set_flip_h(true); + } + else if (velocity.x < 0) + { + animated_sprite->set_flip_h(false); + } + + parent->set_velocity(velocity); +} diff --git a/src/player/states/PlayerJump.h b/src/player/states/PlayerJump.h new file mode 100644 index 0000000..7458832 --- /dev/null +++ b/src/player/states/PlayerJump.h @@ -0,0 +1,45 @@ +#ifndef JUEGO_PLAYER_JUMP_H +#define JUEGO_PLAYER_JUMP_H + +#include "state_machine/State.h" + +#include +#include +#include + +namespace godot +{ + namespace player + { + class PlayerJump : public State + { + GODOT_CLASS(PlayerJump, State) + + private: + Input *_input; + AnimatedSprite *animated_sprite; + bool double_jumped; + + public: + static void _register_methods(); + + PlayerJump(); + + ~PlayerJump(); + + /** + * @brief Initialize the class from Godot. + * @details This method is called just once when the Godot engine connects to the instance of the class. + */ + void _init(); + + void _state_enter(const String state); + + void _state_exit(); + + void _physics_process(float delta); + }; + } +} + +#endif diff --git a/src/player/states/PlayerMove.cpp b/src/player/states/PlayerMove.cpp new file mode 100644 index 0000000..787db1d --- /dev/null +++ b/src/player/states/PlayerMove.cpp @@ -0,0 +1,94 @@ +#include "player/states/PlayerMove.h" +#include "player/Player.h" + +using namespace godot; +using namespace player; + +void PlayerMove::_register_methods() +{ + register_method("_state_enter", &PlayerMove::_state_enter); + register_method("_state_exit", &PlayerMove::_state_exit); + register_method("_physics_process", &PlayerMove::_physics_process); +} + +PlayerMove::PlayerMove() +{ +} + +PlayerMove::~PlayerMove() +{ +} + +void PlayerMove::_init() +{ + _input = Input::get_singleton(); +} + +void PlayerMove::_state_enter(String state, Array args) +{ + animated_sprite = get_parent()->get_node("AnimatedSprite"); + animated_sprite->set_animation("move"); + animated_sprite->play(); +} + +void PlayerMove::_state_exit() +{ +} + +void PlayerMove::_physics_process(float delta) +{ + auto parent = Object::cast_to(get_parent()); + + auto direction_pressed = false; + + auto current_speed = parent->get_speed(); + auto velocity = parent->get_velocity(); + velocity.x = 0; + if (_input->is_action_pressed("run")) + { + current_speed *= parent->get_run_speed(); + } + + if (_input->is_action_pressed("right")) + { + direction_pressed = true; + velocity.x += current_speed; + } + + if (_input->is_action_pressed("left")) + { + direction_pressed = true; + velocity.x += -current_speed; + } + + if (_input->is_action_just_pressed("jump")) + { + get_state_machine()->change("Jump"); + return; + } + + if (velocity.x > 0) + { + animated_sprite->set_flip_h(true); + } + else if (velocity.x < 0) + { + animated_sprite->set_flip_h(false); + } + else + { + if (!direction_pressed) + { + get_state_machine()->change("Idle"); + } + return; + } + + parent->set_velocity(velocity); + + if (!parent->is_on_floor()) + { + get_state_machine()->change("Fall"); + return; + } +} diff --git a/src/player/states/PlayerMove.h b/src/player/states/PlayerMove.h new file mode 100644 index 0000000..a41e6d4 --- /dev/null +++ b/src/player/states/PlayerMove.h @@ -0,0 +1,46 @@ +#ifndef JUEGO_PLAYER_MOVE_H +#define JUEGO_PLAYER_MOVE_H + +#include "state_machine/State.h" + +#include +#include +#include + +namespace godot +{ + namespace player + { + class PlayerMove : public State + { + GODOT_CLASS(PlayerMove, State) + + private: + Input *_input; + AnimatedSprite *animated_sprite; + + public: + static void _register_methods(); + + PlayerMove(); + + ~PlayerMove(); + + /** + * @brief Initialize the class from Godot. + * @details This method is called just once when the Godot engine connects to the instance of the class. + */ + void _init(); + + void _state_enter(String state, Array args); + + void _state_exit(); + + void _physics_process(float delta); + + Vector2 move_player(Vector2 velocity); + }; + } +} + +#endif diff --git a/src/state_machine/State.cpp b/src/state_machine/State.cpp new file mode 100644 index 0000000..6e53a52 --- /dev/null +++ b/src/state_machine/State.cpp @@ -0,0 +1,54 @@ +#include "State.h" + +using namespace godot; + +void State::_register_methods() +{ + register_method("set_parent", &State::set_parent); + register_method("get_parent", &State::get_parent); + register_method("set_state_machine", &State::set_state_machine); + register_method("_state_enter", &State::_state_enter); + register_method("_state_exit", &State::_state_exit); +} + +State::State() +{ +} + +State::~State() +{ +} + +void State::_init() +{ +} + +void State::_state_enter(const String state, const Array args) +{ + WARN_PRINT("State " + state + " is missing its _state_enter method!"); +} + +void State::_state_exit(const String state, const Array args) +{ + WARN_PRINT("State " + state + " is missing its _state_exit method!"); +} + +void State::set_parent(Node *new_parent) +{ + parent = new_parent; +} + +Node *State::get_parent() +{ + return parent; +} + +void State::set_state_machine(StateMachine *new_state_machine) +{ + state_machine = new_state_machine; +} + +StateMachine *State::get_state_machine() +{ + return state_machine; +} diff --git a/src/state_machine/State.h b/src/state_machine/State.h new file mode 100644 index 0000000..5ae0db8 --- /dev/null +++ b/src/state_machine/State.h @@ -0,0 +1,46 @@ +#ifndef JUEGO_STATE_H +#define JUEGO_STATE_H + +#include "StateMachine.h" + +#include +#include + +namespace godot +{ + class State : public StateMachine + { + GODOT_CLASS(State, Node) + + private: + StateMachine *state_machine; + + public: + Node *parent; + static void _register_methods(); + + State(); + + ~State(); + + /** + * @brief Initialize the class from Godot. + * @details This method is called just once when the Godot engine connects to the instance of the class. + */ + virtual void _init(); + + virtual void _state_enter(const String state, const Array args = Array()); + + virtual void _state_exit(const String state, const Array args = Array()); + + virtual void set_parent(Node *new_parent) final; + + virtual Node *get_parent() final; + + virtual void set_state_machine(StateMachine *new_state_machine) final; + + virtual StateMachine *get_state_machine() final; + }; +} + +#endif diff --git a/src/state_machine/StateMachine.cpp b/src/state_machine/StateMachine.cpp new file mode 100644 index 0000000..200883b --- /dev/null +++ b/src/state_machine/StateMachine.cpp @@ -0,0 +1,208 @@ +#include "StateMachine.h" +#include "State.h" + +using namespace godot; + +void StateMachine::_register_methods() +{ + register_method("_ready", &StateMachine::_ready); + register_method("_on_StateMachine_tree_entered", &StateMachine::_on_StateMachine_tree_entered); + register_method("_on_StateMachine_tree_exiting", &StateMachine::_on_StateMachine_tree_exiting); + register_property("default_state", &StateMachine::set_default_state, &StateMachine::get_default_state, String()); + register_property("debug", &StateMachine::set_debug, &StateMachine::get_debug, false); + register_signal("state_entered", "state", GODOT_VARIANT_TYPE_STRING); + register_signal("state_exited", "state", GODOT_VARIANT_TYPE_STRING); + register_signal("state_restarted", "state", GODOT_VARIANT_TYPE_STRING); +} + +StateMachine::StateMachine() +{ +} + +StateMachine::~StateMachine() +{ +} + +void StateMachine::_init() +{ + set_default_state(String()); + set_debug(false); +} + +void StateMachine::_ready() +{ + connect("tree_entered", this, "_on_StateMachine_tree_entered"); + connect("tree_exiting", this, "_on_StateMachine_tree_exiting"); + parent = get_parent(); + set_current_state(get_default_state()); + setup(); +} + +void StateMachine::setup() +{ + auto children = get_children(); + + if (get_current_state() == "") + { + if (children.size() > 0) + { + WARN_PRINT("State machine doesn't have a default state set, using first child!"); + auto child = Object::cast_to(children[0].operator Object*()); + set_current_state(child->get_name()); + } + else + { + ERR_PRINT("State machine doesn't have a default state set and has no child states!"); + return; + } + } + + for (uint8_t i = 0; i < children.size(); i++) + { + auto child = Object::cast_to(children[i].operator Object*()); + add_state(child->get_name(), child); + + child->call("set_state_machine", this); + + child->call("set_parent", parent); + + if (child->get_name() != get_current_state()) + { + this->remove_child(child); + } + } + + this->call("_state_enter", get_current_state()); +} + +void StateMachine::add_state(const String state, Node *child) +{ + states[state] = child; +} + +bool StateMachine::is_current(const String state) +{ + if (get_current_state() == "") + { + return false; + } + else + { + return get_current_state() == state; + } +} + +bool StateMachine::has(const String state) +{ + return states.has(state); +} + +void StateMachine::restart(const String state, const Array& args) +{ + this->call("_state_exit", state, args); + this->call("_state_enter", state, args); + this->emit_signal("state_restarted", get_current_state()); +} + +void StateMachine::change(const String state, const Array &args) +{ + if (is_current(state)) + { + return this->restart(state, args); + } + + auto previous_state = get_current_state(); + auto exiting = this->call("_state_exit", state, args); + + if (get_current_state() != "") + { + auto child = Object::cast_to(states[get_current_state()].operator Object*()); + if (child != nullptr) + { + this->remove_child(child); + } + this->emit_signal("state_exited", get_current_state()); + if (debug) + { + Godot::print(get_current_state() + " exited!"); + } + } + + set_current_state(state); + auto child = Object::cast_to(states[get_current_state()].operator Object*()); + this->add_child(child); + + this->call("_state_enter", previous_state, args); + this->emit_signal("state_entered", get_current_state()); + if (debug) + { + Godot::print(get_current_state() + " entered!"); + } +} + +Variant StateMachine::call(const String method, const Array &args) +{ + auto node = Object::cast_to(states[get_current_state()].operator Object*()); + if (node != nullptr) + { + return node->call(method, args); + } + return Variant(); +} + +Variant StateMachine::_call(const String method, const Array &args) +{ + return this->call(method, args); +} + +void StateMachine::set_default_state(const String new_default_state) +{ + default_state = new_default_state; +} + +String StateMachine::get_default_state() +{ + return default_state; +} + +void StateMachine::set_current_state(const String new_current_sate) +{ + current_state = new_current_sate; +} + +String StateMachine::get_current_state() +{ + return current_state; +} + +void StateMachine::set_debug(bool debug) +{ + this->debug = debug; +} + +bool StateMachine::get_debug() +{ + return this->debug; +} + +void StateMachine::_on_StateMachine_tree_entered() +{ + setup(); +} + +void StateMachine::_on_StateMachine_tree_exiting() +{ + auto keys = states.keys(); + for (uint8_t i = 0; i < keys.size(); i++) + { + auto child = Object::cast_to(states[keys[i]].operator Object*()); + if (child != nullptr) + { + auto children = get_children(); + if (!children.has(child)) + { + this->add_child(child); + } + } + } +} diff --git a/src/state_machine/StateMachine.h b/src/state_machine/StateMachine.h new file mode 100644 index 0000000..66920e3 --- /dev/null +++ b/src/state_machine/StateMachine.h @@ -0,0 +1,95 @@ +#ifndef JUEGO_STATE_MACHINE_H +#define JUEGO_STATE_MACHINE_H + +#include +#include + +namespace godot +{ + class StateMachine : public Node + { + GODOT_CLASS(StateMachine, Node) + + private: + Node *parent; + String default_state; + bool debug; + String current_state; + Dictionary states; + + void add_state(const String state, Node *child); + + public: + static void _register_methods(); + + StateMachine(); + + ~StateMachine(); + + /** + * @brief Initialize the class from Godot. + * @details This method is called just once when the Godot engine connects to the instance of the class. + */ + void _init(); + + /** + * @brief Code to be run when ready. + * @details This method is run when all the children of this node are ready. + */ + void _ready(); + + void setup(); + + bool is_current(); + + bool is_current(const String state); + + bool has(const String state); + + void restart(const String state, const Array& args = Array()); + + void change(const String state, const Array &args = Array()); + + Variant call(const String method, const Array &args = Array()); + + Variant _call(const String method, const Array &args = Array()); + + template Variant restart(Args ...args) + { + return restart(Array::make(args...)); + } + + template Variant change(const String state, Args ...args) + { + return change(state, Array::make(args...)); + } + + template Variant call(const String method, Args ...args) + { + return call(method, Array::make(args...)); + } + + template Variant _call(const String method, Args ...args) + { + return _call(method, Array::make(args...)); + } + + void set_default_state(const String new_default_state); + + String get_default_state(); + + void set_debug(bool debug); + + bool get_debug(); + + void set_current_state(const String new_current_state); + + String get_current_state(); + + void _on_StateMachine_tree_entered(); + + void _on_StateMachine_tree_exiting(); + }; +} + +#endif