feat: added simple enemy with chase behaviour
This commit is contained in:
parent
4f564a4f4c
commit
19ba4047ca
46
godot/Enemies/enemy.tscn
Normal file
46
godot/Enemies/enemy.tscn
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
[gd_scene load_steps=6 format=3 uid="uid://deb8qiasxsobt"]
|
||||||
|
|
||||||
|
[sub_resource type="SphereShape3D" id="SphereShape3D_tnc8b"]
|
||||||
|
radius = 7.23
|
||||||
|
|
||||||
|
[sub_resource type="SphereShape3D" id="SphereShape3D_1inhn"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_y3cyo"]
|
||||||
|
albedo_color = Color(0.0666667, 0.223529, 0.254902, 1)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_y311e"]
|
||||||
|
material = SubResource("StandardMaterial3D_y3cyo")
|
||||||
|
size = Vector3(0.77, 0.59, 0.805)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_bc6co"]
|
||||||
|
material = SubResource("StandardMaterial3D_y3cyo")
|
||||||
|
size = Vector3(0.475, 0.495, 0.47)
|
||||||
|
|
||||||
|
[node name="Enemy" type="Enemy"]
|
||||||
|
collision_layer = 7
|
||||||
|
|
||||||
|
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
|
||||||
|
path_desired_distance = 2.25
|
||||||
|
|
||||||
|
[node name="VisionArea" type="Area3D" parent="."]
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 2
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="VisionArea"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.32109, 0)
|
||||||
|
shape = SubResource("SphereShape3D_tnc8b")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.495191, 0)
|
||||||
|
shape = SubResource("SphereShape3D_1inhn")
|
||||||
|
|
||||||
|
[node name="Health" type="Health" parent="."]
|
||||||
|
max_health = 10
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.445159, -0.0811542)
|
||||||
|
mesh = SubResource("BoxMesh_y311e")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D2" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.430494, 0.458904)
|
||||||
|
mesh = SubResource("BoxMesh_bc6co")
|
|
@ -1,7 +1,7 @@
|
||||||
[gd_scene load_steps=3 format=3 uid="uid://cqkbxe758jr7p"]
|
[gd_scene load_steps=3 format=3 uid="uid://cqkbxe758jr7p"]
|
||||||
|
|
||||||
[sub_resource type="Curve" id="Curve_bxjan"]
|
[sub_resource type="Curve" id="Curve_bxjan"]
|
||||||
_data = [Vector2(0, 0), 0.0, 0.0628674, 0, 0, Vector2(0.51626, 0.549451), 2.7033, 2.7033, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
_data = [Vector2(0, 0), 0.0, 3.33407, 0, 0, Vector2(0.430894, 0.692308), 0.730621, 0.730621, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||||
point_count = 3
|
point_count = 3
|
||||||
|
|
||||||
[sub_resource type="SphereMesh" id="SphereMesh_jkn5p"]
|
[sub_resource type="SphereMesh" id="SphereMesh_jkn5p"]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=5 format=3 uid="uid://dpda341t6ipiv"]
|
[gd_scene load_steps=6 format=3 uid="uid://dpda341t6ipiv"]
|
||||||
|
|
||||||
[sub_resource type="Curve" id="Curve_7rmf4"]
|
[sub_resource type="Curve" id="Curve_7rmf4"]
|
||||||
min_value = 0.2
|
min_value = 0.2
|
||||||
|
@ -12,11 +12,18 @@ height = 1.59321
|
||||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_rwcvu"]
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_rwcvu"]
|
||||||
height = 1.605
|
height = 1.605
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_scmx3"]
|
||||||
|
albedo_color = Color(0.94902, 0.909804, 0, 1)
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_f5yvh"]
|
[sub_resource type="BoxMesh" id="BoxMesh_f5yvh"]
|
||||||
size = Vector3(0.125, 0.14, 0.94)
|
size = Vector3(0.125, 0.14, 0.94)
|
||||||
|
|
||||||
[node name="PlayerCharacter" type="PlayerCharacter"]
|
[node name="PlayerCharacter" type="PlayerCharacter"]
|
||||||
rotation_speed_curve = SubResource("Curve_7rmf4")
|
rotation_speed_curve = SubResource("Curve_7rmf4")
|
||||||
|
collision_layer = 7
|
||||||
|
|
||||||
|
[node name="Health" type="Health" parent="."]
|
||||||
|
max_health = 5
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.802835, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.802835, 0)
|
||||||
|
@ -27,7 +34,9 @@ shape = SubResource("CapsuleShape3D_3g72p")
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8121, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8121, 0)
|
||||||
mesh = SubResource("CapsuleMesh_rwcvu")
|
mesh = SubResource("CapsuleMesh_rwcvu")
|
||||||
|
surface_material_override/0 = SubResource("StandardMaterial3D_scmx3")
|
||||||
|
|
||||||
[node name="MeshInstance3D2" type="MeshInstance3D" parent="."]
|
[node name="MeshInstance3D2" type="MeshInstance3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.509142, 0.986876, 0.380722)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.509142, 0.986876, 0.380722)
|
||||||
mesh = SubResource("BoxMesh_f5yvh")
|
mesh = SubResource("BoxMesh_f5yvh")
|
||||||
|
surface_material_override/0 = SubResource("StandardMaterial3D_scmx3")
|
||||||
|
|
|
@ -37,3 +37,9 @@ move_backward={
|
||||||
"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,"echo":false,"script":null)
|
"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,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[layer_names]
|
||||||
|
|
||||||
|
3d_physics/layer_1="Default"
|
||||||
|
3d_physics/layer_2="Vision"
|
||||||
|
3d_physics/layer_3="Hitboxes"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
[gd_scene load_steps=9 format=3 uid="uid://m36guasmi3c1"]
|
[gd_scene load_steps=11 format=3 uid="uid://m36guasmi3c1"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://cqkbxe758jr7p" path="res://player.tscn" id="1_hv5rj"]
|
[ext_resource type="PackedScene" uid="uid://cqkbxe758jr7p" path="res://player.tscn" id="1_hv5rj"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://deb8qiasxsobt" path="res://Enemies/enemy.tscn" id="2_10wh5"]
|
||||||
|
|
||||||
[sub_resource type="GameState" id="GameState_k4j3x"]
|
[sub_resource type="GameState" id="GameState_k4j3x"]
|
||||||
|
|
||||||
|
@ -17,6 +18,10 @@ sky_material = SubResource("ProceduralSkyMaterial_s4k5k")
|
||||||
background_mode = 2
|
background_mode = 2
|
||||||
sky = SubResource("Sky_oquga")
|
sky = SubResource("Sky_oquga")
|
||||||
|
|
||||||
|
[sub_resource type="NavigationMesh" id="NavigationMesh_gx4lq"]
|
||||||
|
vertices = PackedVector3Array(-9.5, 0.625, -9.5, -9.5, 0.625, 9.5, 9.5, 0.625, 9.5, 9.5, 0.625, -9.5)
|
||||||
|
polygons = [PackedInt32Array(3, 2, 0), PackedInt32Array(0, 2, 1)]
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_5glbk"]
|
[sub_resource type="BoxMesh" id="BoxMesh_5glbk"]
|
||||||
size = Vector3(20, 0.25, 20)
|
size = Vector3(20, 0.25, 20)
|
||||||
|
|
||||||
|
@ -29,14 +34,20 @@ game_mode_prototype = SubResource("TunnelsGameMode_itn7y")
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
environment = SubResource("Environment_mt2l0")
|
environment = SubResource("Environment_mt2l0")
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="WorldEnvironment"]
|
[node name="NavigationRegion3D" type="NavigationRegion3D" parent="WorldEnvironment"]
|
||||||
|
navigation_mesh = SubResource("NavigationMesh_gx4lq")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="WorldEnvironment/NavigationRegion3D"]
|
||||||
mesh = SubResource("BoxMesh_5glbk")
|
mesh = SubResource("BoxMesh_5glbk")
|
||||||
skeleton = NodePath("../..")
|
skeleton = NodePath("../../..")
|
||||||
|
|
||||||
[node name="StaticBody3D" type="StaticBody3D" parent="WorldEnvironment"]
|
[node name="StaticBody3D" type="StaticBody3D" parent="WorldEnvironment/NavigationRegion3D/MeshInstance3D"]
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="WorldEnvironment/StaticBody3D"]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="WorldEnvironment/NavigationRegion3D/MeshInstance3D/StaticBody3D"]
|
||||||
shape = SubResource("BoxShape3D_kacqg")
|
shape = SubResource("BoxShape3D_kacqg")
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="WorldEnvironment"]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="WorldEnvironment"]
|
||||||
transform = Transform3D(0.581243, 0.357328, -0.731077, 0, 0.898426, 0.439124, 0.81373, -0.255238, 0.522204, 0, 2.44259, 0)
|
transform = Transform3D(0.581243, 0.357328, -0.731077, 0, 0.898426, 0.439124, 0.81373, -0.255238, 0.522204, 0, 2.44259, 0)
|
||||||
|
|
||||||
|
[node name="Enemy" parent="." instance=ExtResource("2_10wh5")]
|
||||||
|
transform = Transform3D(-0.925514, 0, -0.378713, 0, 1, 0, 0.378713, 0, -0.925514, -3.91746, 0.125, 5.6328)
|
||||||
|
|
69
src/enemy.cpp
Normal file
69
src/enemy.cpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#include "enemy.hpp"
|
||||||
|
#include "godot_cpp/classes/time.hpp"
|
||||||
|
#include "godot_cpp/variant/utility_functions.hpp"
|
||||||
|
#include "health.hpp"
|
||||||
|
#include "player_character.hpp"
|
||||||
|
#include "utils/godot_macros.h"
|
||||||
|
#include <godot_cpp/classes/navigation_agent3d.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
void Enemy::_bind_methods() {
|
||||||
|
#define CLASSNAME Enemy
|
||||||
|
GDFUNCTION_ARGS(body_entered_vision_area, "body");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enemy::_ready() { GDGAMEONLY();
|
||||||
|
this->health = this->get_node<Health>("Health");
|
||||||
|
this->nav_agent = this->get_node<NavigationAgent3D>("NavigationAgent3D");
|
||||||
|
this->vision_area = this->get_node<Area3D>("VisionArea");
|
||||||
|
this->vision_area->connect("body_entered", Callable(this, "body_entered_vision_area"));
|
||||||
|
this->update_navigation_target();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enemy::_process(double delta_time) { GDGAMEONLY();
|
||||||
|
if(this->renav_time > Time::get_singleton()->get_ticks_msec() / 1000.f) {
|
||||||
|
this->update_navigation_target();
|
||||||
|
}
|
||||||
|
this->process_navigation(delta_time);
|
||||||
|
this->move_and_slide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enemy::process_navigation(double delta_time) {
|
||||||
|
if(this->nav_agent->is_navigation_finished()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Vector3 const desired_direction = (this->nav_agent->get_next_path_position() - this->get_global_position()).normalized();
|
||||||
|
this->set_velocity(desired_direction);
|
||||||
|
Transform3D trans = this->get_global_transform();
|
||||||
|
Vector3 const forward = desired_direction;
|
||||||
|
trans.set_basis(Basis{desired_direction.cross(Vector3{0.f, 1.f, 0.f}), Vector3{0.f, 1.f, 0.f}, forward});
|
||||||
|
this->set_global_transform(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enemy::body_entered_vision_area(Node3D *body) {
|
||||||
|
PlayerCharacter *player = Object::cast_to<PlayerCharacter>(body);
|
||||||
|
if(player == nullptr)
|
||||||
|
return;
|
||||||
|
// TODO: replace this with some condition deciding wether to attack the new character or the current target
|
||||||
|
this->target_player = player;
|
||||||
|
this->update_navigation_target();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enemy::update_navigation_target() {
|
||||||
|
this->renav_time = float(Time::get_singleton()->get_ticks_msec()) / 1000.f + Enemy::RENAV_INTERVAL;
|
||||||
|
if(this->target_player == nullptr)
|
||||||
|
this->nav_agent->set_target_position(this->get_global_position());
|
||||||
|
else
|
||||||
|
this->nav_agent->set_target_position(this->target_player->get_global_position());
|
||||||
|
}
|
||||||
|
|
||||||
|
Health *Enemy::get_health() {
|
||||||
|
return this->health;
|
||||||
|
}
|
||||||
|
|
||||||
|
Health const *Enemy::get_health() const {
|
||||||
|
return this->health;
|
||||||
|
}
|
||||||
|
|
||||||
|
float const Enemy::RENAV_INTERVAL{0.25f};
|
||||||
|
}
|
40
src/enemy.hpp
Normal file
40
src/enemy.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef ENEMY_HPP
|
||||||
|
#define ENEMY_HPP
|
||||||
|
|
||||||
|
#include "godot_cpp/classes/area3d.hpp"
|
||||||
|
#include "health.hpp"
|
||||||
|
#include <godot_cpp/classes/character_body3d.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
class PlayerCharacter;
|
||||||
|
class NavigationAgent3D;
|
||||||
|
|
||||||
|
class Enemy : public CharacterBody3D,
|
||||||
|
public IHealthEntity {
|
||||||
|
GDCLASS(Enemy, CharacterBody3D);
|
||||||
|
static void _bind_methods();
|
||||||
|
public:
|
||||||
|
virtual void _ready() override;
|
||||||
|
virtual void _process(double delta_time) override;
|
||||||
|
void process_navigation(double delta_time);
|
||||||
|
|
||||||
|
void body_entered_vision_area(Node3D *body);
|
||||||
|
|
||||||
|
void update_navigation_target();
|
||||||
|
|
||||||
|
virtual Health *get_health() override;
|
||||||
|
virtual Health const *get_health() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float renav_time{0.f};
|
||||||
|
|
||||||
|
PlayerCharacter *target_player{nullptr};
|
||||||
|
Health *health{nullptr};
|
||||||
|
NavigationAgent3D *nav_agent{nullptr};
|
||||||
|
Area3D *vision_area{nullptr};
|
||||||
|
|
||||||
|
static float const RENAV_INTERVAL;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !ENEMY_HPP
|
|
@ -9,6 +9,7 @@
|
||||||
#include "utils/level.hpp"
|
#include "utils/level.hpp"
|
||||||
#include "utils/player_input.hpp"
|
#include "utils/player_input.hpp"
|
||||||
#include "utils/spawn_point.hpp"
|
#include "utils/spawn_point.hpp"
|
||||||
|
#include "enemy.hpp"
|
||||||
#include "health.hpp"
|
#include "health.hpp"
|
||||||
#include "player_character.hpp"
|
#include "player_character.hpp"
|
||||||
#include "tunnels_game_mode.hpp"
|
#include "tunnels_game_mode.hpp"
|
||||||
|
@ -22,16 +23,18 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClassDB::register_abstract_class<GameRoot>();
|
ClassDB::register_abstract_class<GameRoot>();
|
||||||
ClassDB::register_class<GameRoot3D>();
|
|
||||||
ClassDB::register_class<SpawnPoint3D>();
|
|
||||||
ClassDB::register_class<PlayerInput>();
|
|
||||||
ClassDB::register_class<GameMode>();
|
ClassDB::register_class<GameMode>();
|
||||||
|
ClassDB::register_class<GameRoot3D>();
|
||||||
ClassDB::register_class<GameState>();
|
ClassDB::register_class<GameState>();
|
||||||
ClassDB::register_class<Level3D>();
|
ClassDB::register_class<Level3D>();
|
||||||
|
ClassDB::register_class<PlayerInput>();
|
||||||
|
ClassDB::register_class<SpawnPoint3D>();
|
||||||
|
|
||||||
|
ClassDB::register_class<Enemy>();
|
||||||
|
ClassDB::register_class<Health>();
|
||||||
|
ClassDB::register_class<PlayerCharacter>();
|
||||||
ClassDB::register_class<TunnelsGameMode>();
|
ClassDB::register_class<TunnelsGameMode>();
|
||||||
ClassDB::register_class<TunnelsPlayer>();
|
ClassDB::register_class<TunnelsPlayer>();
|
||||||
ClassDB::register_class<PlayerCharacter>();
|
|
||||||
ClassDB::register_class<Health>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
|
|
@ -68,7 +68,7 @@ void TunnelsPlayer::process_mouse_location(double delta_time) {
|
||||||
|
|
||||||
void TunnelsPlayer::process_camera_rotation(double delta_time) {
|
void TunnelsPlayer::process_camera_rotation(double delta_time) {
|
||||||
Vector3 rotation = this->get_global_rotation();
|
Vector3 rotation = this->get_global_rotation();
|
||||||
float const y_multiplier = std::max(0.4f, this->mouse_location.y); // the influence of the mouse's y position on the rotation speed
|
float const y_multiplier = std::max(TunnelsPlayer::ROTATION_Y_MIN_INFLUENCE, this->mouse_location.y); // the influence of the mouse's y position on the rotation speed
|
||||||
// rotate the camera when the mouse is close to the edge of the screen
|
// rotate the camera when the mouse is close to the edge of the screen
|
||||||
if(this->mouse_location.x < TunnelsPlayer::ROTATION_MARGIN) {
|
if(this->mouse_location.x < TunnelsPlayer::ROTATION_MARGIN) {
|
||||||
// normalized measurement of how far into the rotation margin the mouse is
|
// normalized measurement of how far into the rotation margin the mouse is
|
||||||
|
@ -156,6 +156,7 @@ Ref<Curve> TunnelsPlayer::get_camera_rotation_ramp() const {
|
||||||
return this->camera_rotation_ramp;
|
return this->camera_rotation_ramp;
|
||||||
}
|
}
|
||||||
|
|
||||||
float const TunnelsPlayer::ROTATION_SPEED{4.f};
|
float const TunnelsPlayer::ROTATION_SPEED{6.f};
|
||||||
float const TunnelsPlayer::ROTATION_MARGIN{0.35f};
|
float const TunnelsPlayer::ROTATION_Y_MIN_INFLUENCE{7.f};
|
||||||
|
float const TunnelsPlayer::ROTATION_MARGIN{0.4f};
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,9 @@ private:
|
||||||
Camera3D *camera{nullptr};
|
Camera3D *camera{nullptr};
|
||||||
|
|
||||||
Ref<Curve> camera_rotation_ramp{};
|
Ref<Curve> camera_rotation_ramp{};
|
||||||
|
|
||||||
static float const ROTATION_SPEED;
|
static float const ROTATION_SPEED;
|
||||||
|
static float const ROTATION_Y_MIN_INFLUENCE;
|
||||||
static float const ROTATION_MARGIN;
|
static float const ROTATION_MARGIN;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue