feat: start of firing enemies

main
Sara 2024-12-09 23:55:11 +01:00
parent 7a6aefc44c
commit 2054474c01
12 changed files with 212 additions and 14 deletions

View File

@ -1,18 +1,41 @@
[gd_scene load_steps=4 format=3 uid="uid://c2kiuk2yxdlfr"]
[gd_scene load_steps=6 format=3 uid="uid://c2kiuk2yxdlfr"]
[ext_resource type="PackedScene" uid="uid://1kr3qqan3trk" path="res://objects/player.tscn" id="1_7kr4i"]
[ext_resource type="PackedScene" uid="uid://dm3355tbkvx24" path="res://objects/section_2.tscn" id="2_og5hb"]
[ext_resource type="PackedScene" uid="uid://0fykl1mw3c12" path="res://objects/enemy.tscn" id="3_ppe0x"]
[sub_resource type="GDScript" id="GDScript_peyat"]
script/source = "extends Area3D
func _on_body_entered(body: Node3D) -> void:
if body is Player:
$\"../Enemy2\".notice_player(body as Player)
$\"../Enemy\".notice_player(body as Player)
"
[sub_resource type="BoxShape3D" id="BoxShape3D_ggysn"]
size = Vector3(3.69043, 1, 8.0553)
[node name="Boot" type="Node3D"]
[node name="Environment" parent="." instance=ExtResource("2_og5hb")]
[node name="Enemy" parent="." instance=ExtResource("3_ppe0x")]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 9.02629, 0.21507, -7.46576)
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 9.02629, 0.258895, -7.46576)
[node name="Area3D" type="Area3D" parent="."]
transform = Transform3D(0.734015, 0, -0.679134, 0, 1, 0, 0.679134, 0, 0.734015, 2.09421, 0.536154, -14.1446)
script = SubResource("GDScript_peyat")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Area3D"]
transform = Transform3D(1, 0, -4.91743e-07, 0, 1, 0, 4.91743e-07, 0, 1, 0, 0, 0)
shape = SubResource("BoxShape3D_ggysn")
[node name="Enemy2" parent="." instance=ExtResource("3_ppe0x")]
transform = Transform3D(0.139795, 0, -0.990181, 0, 1, 0, 0.990181, 0, 0.139795, 8.69408, 0.536154, -15.4052)
[node name="Player" parent="." instance=ExtResource("1_7kr4i")]
transform = Transform3D(0.0816776, 0, 0.996659, 0, 1, 0, -0.996659, 0, 0.0816776, -2.58966, 0.891191, -2.77265)
[connection signal="body_entered" from="Area3D" to="Area3D" method="_on_body_entered"]

View File

@ -172,9 +172,9 @@ advance_mode = 2
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_g0i3x"]
xfade_time = 0.4
priority = 2
advance_mode = 2
advance_expression = "get_is_running()
"
advance_expression = "get_is_running() && is_walking()"
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_ocffv"]
xfade_time = 0.3
@ -185,7 +185,7 @@ advance_expression = "get_is_running()
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_h0my5"]
xfade_time = 0.4
advance_mode = 2
advance_expression = "!get_is_running()"
advance_expression = "!get_is_running() || !is_walking()"
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_nymcr"]
xfade_time = 0.3
@ -205,6 +205,7 @@ advance_expression = "get_is_running()
"
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_4q0ot"]
priority = 2
advance_mode = 2
advance_expression = "get_stab()"
@ -244,7 +245,7 @@ states/Start/position = Vector2(572.26, 14.954)
"states/Walk [turn]/node" = SubResource("AnimationNodeBlendTree_2mbyh")
"states/Walk [turn]/position" = Vector2(572.26, 258.767)
transitions = ["Aim [aim] [turn]", "Aim Down [aim]", SubResource("AnimationNodeStateMachineTransition_4lybd"), "Aim Down [aim]", "Walk [turn]", SubResource("AnimationNodeStateMachineTransition_5aafd"), "Aim [aim] [turn]", "Fire [aim]", SubResource("AnimationNodeStateMachineTransition_8f4gl"), "Fire [aim]", "Aim [aim] [turn]", SubResource("AnimationNodeStateMachineTransition_xnat3"), "Walk [turn]", "Aim [aim] [turn]", SubResource("AnimationNodeStateMachineTransition_12bos"), "Aim Down [aim]", "Stationary [turn]", SubResource("AnimationNodeStateMachineTransition_n0ndr"), "Walk [turn]", "Stationary [turn]", SubResource("AnimationNodeStateMachineTransition_0wc5e"), "Stationary [turn]", "Walk [turn]", SubResource("AnimationNodeStateMachineTransition_4hisb"), "Start", "Stationary [turn]", SubResource("AnimationNodeStateMachineTransition_bmty6"), "Stationary [turn]", "RESET To Aim", SubResource("AnimationNodeStateMachineTransition_kyd6p"), "RESET To Aim", "Aim [aim] [turn]", SubResource("AnimationNodeStateMachineTransition_w5kob"), "Stationary [turn]", "Run", SubResource("AnimationNodeStateMachineTransition_g0i3x"), "Walk [turn]", "Run", SubResource("AnimationNodeStateMachineTransition_ocffv"), "Run", "Stationary [turn]", SubResource("AnimationNodeStateMachineTransition_h0my5"), "Run", "Walk [turn]", SubResource("AnimationNodeStateMachineTransition_nymcr"), "Run", "Stab", SubResource("AnimationNodeStateMachineTransition_g5qf0"), "Stab", "Run", SubResource("AnimationNodeStateMachineTransition_27kmb"), "Stationary [turn]", "Stab", SubResource("AnimationNodeStateMachineTransition_4q0ot"), "Stab", "Stationary [turn]", SubResource("AnimationNodeStateMachineTransition_h6ujc"), "Walk [turn]", "Stab", SubResource("AnimationNodeStateMachineTransition_3bu8l"), "Stab", "Walk [turn]", SubResource("AnimationNodeStateMachineTransition_cpibo")]
graph_offset = Vector2(-172.078, -8.655)
graph_offset = Vector2(-87.4971, -116.108)
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_au62i"]
animation = &"Fall_die"

View File

@ -98,4 +98,7 @@ aim_weapon = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.19104, 0)
shape = SubResource("CapsuleShape3D_3tduq")
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
unique_name_in_owner = true
[editable path="CharacterModel"]

View File

@ -11,7 +11,7 @@ radius = 0.1
[node name="Player" type="Player"]
collision_layer = 3
collision_mask = 3
floor_snap_length = 0.2
floor_snap_length = 1.0
[node name="CharacterModel" parent="." instance=ExtResource("1_cwt7u")]
unique_name_in_owner = true

View File

@ -353,7 +353,16 @@ frequency_max = 5.0
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.66028, 2.51555, -0.763191)
[node name="ArtilleryTarget3" parent="." instance=ExtResource("13_rl1f8")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.00799, 2.51555, -22.162)
frequency_max = 8.0
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.977764, 2.51555, -22.0148)
[node name="ArtilleryTarget6" parent="." instance=ExtResource("13_rl1f8")]
frequency_max = 8.0
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -7.26403, 2.51555, -11.5654)
[node name="ArtilleryTarget7" parent="." instance=ExtResource("13_rl1f8")]
frequency_max = 8.0
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 17.9765, 2.51555, -24.0753)
[node name="ArtilleryTarget4" parent="." instance=ExtResource("13_rl1f8")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.9532, 2.51555, -6.60339)

BIN
models/character.blend (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

View File

@ -1,9 +1,102 @@
#include "enemy.hpp"
#include "utils/godot_macros.hpp"
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>
#include <godot_cpp/classes/timer.hpp>
#include <godot_cpp/classes/world3d.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
void Enemy::_bind_methods() {}
void Enemy::_bind_methods() {
#define CLASSNAME Enemy
GDFUNCTION_ARGS(notice_player, "player");
GDPROPERTY(update_interval, gd::Variant::FLOAT);
}
void Enemy::_ready() {
this->anim_tree = this->get_node<PlayerAnimTree>("CharacterModel/AnimationTree");
this->agent = this->get_node<gd::NavigationAgent3D>("%NavigationAgent3D");
gd::Timer *timer{memnew(gd::Timer)};
this->add_child(timer);
timer->start(this->update_interval);
timer->connect("timeout", callable_mp(this, &Enemy::update));
}
void Enemy::update() {
if(this->current_action_fn != nullptr)
this->current_action_fn = (ActionFn)(this->*current_action_fn)();
}
void Enemy::chase_enter() {
if(this->player != nullptr)
this->agent->set_target_position(this->player->get_global_position());
}
void Enemy::chase() {
bool const is_chasing{!this->agent->is_navigation_finished()};
this->anim_tree->set_lock_running(is_chasing);
this->anim_tree->set_aim_weapon(!is_chasing);
if(is_chasing) {
gd::Vector3 const global_pos{this->get_global_position()};
gd::Vector3 const target{global_pos * 2 - this->agent->get_next_path_position()};
this->look_at({target.x, global_pos.y, target.z});
}
if(this->get_global_position().distance_to(this->player->get_global_position()) >= this->agent->get_target_desired_distance())
this->chase_enter(); // repath
}
Enemy::ActionFn Enemy::miss_enter() {
this->anim_tree->set_aim_weapon(true);
return (ActionFn)&Enemy::miss;
}
Enemy::ActionFn Enemy::miss() {
if(this->anim_tree->get_current_state().begins_with("Fire") || this->anim_tree->get_fire_weapon()) // last shot still going
return (ActionFn)&Enemy::miss;
if(this->can_see_player) {
gd::Basis const basis{this->get_global_basis()};
this->look_at(this->get_global_position() * 2 - this->player->get_global_position() + basis.get_column(0));
this->anim_tree->set_aim_weapon(true);
this->anim_tree->set_fire_weapon();
if(this->anim_tree->get_fire_weapon()) {
gd::UtilityFunctions::print("!!! miss fired");
return ++this->missed_shots > SHOTS_BEFORE_HIT ? (ActionFn)&Enemy::hit_enter : (ActionFn)&Enemy::miss_enter;
}
} else {
this->chase();
}
return (ActionFn)&Enemy::miss;
}
Enemy::ActionFn Enemy::hit_enter() {
this->anim_tree->set_aim_weapon(true);
return (ActionFn)&Enemy::hit;
}
Enemy::ActionFn Enemy::hit() {
if(this->anim_tree->get_current_state().begins_with("Fire") || this->anim_tree->get_fire_weapon()) // last shot still going
return (ActionFn)&Enemy::hit;
else if(this->can_see_player) {
this->look_at(this->get_global_position() * 2 - this->player->get_global_position());
this->anim_tree->set_aim_weapon(true);
this->anim_tree->set_fire_weapon();
this->missed_shots = 0;
return (ActionFn)&Enemy::miss_enter;
} else {
this->chase();
}
return (ActionFn)&Enemy::hit;
}
void Enemy::_physics_process(double delta) {
this->update_can_see_player();
gd::Basis const basis{this->get_global_basis()};
gd::Vector3 const motion{this->anim_tree->get_root_motion_position()};
this->set_velocity({
basis.get_column(0) * motion.x +
basis.get_column(1) * motion.y +
basis.get_column(2) * motion.z
});
this->move_and_slide();
}
void Enemy::damage() {
@ -11,3 +104,26 @@ void Enemy::damage() {
this->set_collision_mask(0x0);
this->set_collision_layer(0x0);
}
void Enemy::notice_player(Player *player) {
this->player = player;
this->current_action_fn = (ActionFn)&Enemy::miss_enter;
}
void Enemy::update_can_see_player() {
if(this->player == nullptr)
return;
gd::Vector3 origin{this->get_global_position() + gd::Vector3{0.f, 1.8f, 0.f}};
gd::PhysicsDirectSpaceState3D *space{this->get_world_3d()->get_direct_space_state()};
gd::Ref<gd::PhysicsRayQueryParameters3D> query{gd::PhysicsRayQueryParameters3D::create(origin, this->player->get_global_position() + gd::Vector3{0.f, 1.8f, 0.f})};
gd::Dictionary dict{space->intersect_ray(query)};
this->can_see_player = (dict.is_empty() || gd::Object::cast_to<Node>(dict["collider"]) == this->player);
}
void Enemy::set_update_interval(float time) {
this->update_interval = time;
}
float Enemy::get_update_interval() const {
return this->update_interval;
}

View File

@ -2,17 +2,46 @@
#define ENEMY_HPP
#include "damageable_entity.hpp"
#include "player.hpp"
#include "player_anim_tree.hpp"
#include "utils/godot_macros.hpp"
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/navigation_agent3d.hpp>
namespace gd = godot;
class Enemy : public gd::CharacterBody3D, public DamageableEntity {
GDCLASS(Enemy, gd::CharacterBody3D);
static void _bind_methods();
typedef void *(Enemy::*ActionFn_)();
typedef ActionFn_ (Enemy::*ActionFn)();
public:
virtual void _ready() override;
void update();
void chase_enter();
void chase();
ActionFn miss_enter();
ActionFn miss();
ActionFn hit_enter();
ActionFn hit();
ActionFn stab_enter();
ActionFn stab();
virtual void _physics_process(double delta) override;
virtual void damage() override;
void notice_player(Player *player);
void update_can_see_player();
void set_update_interval(float time);
float get_update_interval() const;
private:
int const SHOTS_BEFORE_HIT{2};
int missed_shots{0};
double update_interval{0.2};
ActionFn current_action_fn{nullptr};
bool can_see_player{false};
Player *player{nullptr};
gd::NavigationAgent3D *agent{nullptr};
PlayerAnimTree *anim_tree{nullptr};
};

View File

@ -7,6 +7,7 @@ void PlayerAnimTree::_bind_methods() {
GDPROPERTY(target_turn_speed, gd::Variant::FLOAT);
GDPROPERTY(is_walking, gd::Variant::FLOAT);
GDPROPERTY(walk_speed, gd::Variant::FLOAT);
GDPROPERTY(lock_running, gd::Variant::BOOL);
GDFUNCTION(get_is_running);
GDPROPERTY(aim_weapon, gd::Variant::BOOL);
GDFUNCTION(get_fire_weapon);
@ -66,12 +67,20 @@ float PlayerAnimTree::get_walk_speed() const {
return this->walk_speed;
}
void PlayerAnimTree::set_lock_running(bool value) {
this->lock_running = value;
}
bool PlayerAnimTree::get_lock_running() const {
return this->lock_running;
}
void PlayerAnimTree::set_is_running() {
this->running_time = this->RUN_PARAM_DECAY;
}
bool PlayerAnimTree::get_is_running() const {
return this->running_time > 0.0;
return this->lock_running || this->running_time > 0.0;
}
void PlayerAnimTree::set_aim_weapon(bool value) {
@ -97,7 +106,7 @@ void PlayerAnimTree::set_stab() {
}
bool PlayerAnimTree::get_stab() {
bool const is_set{this->fire_weapon > 0.0};
bool const is_set{this->stab > 0.0};
this->stab = 0.0;
return is_set;
}
@ -111,6 +120,10 @@ bool PlayerAnimTree::match_tags(Tags tags) const {
return (this->current_tags & tags) != Tags::None;
}
gd::StringName const &PlayerAnimTree::get_current_state() const {
return this->last_known_anim;
}
void PlayerAnimTree::update_tags(gd::StringName const &anim) {
if(anim != this->last_known_anim && this->fsm->get_travel_path().size() <= 1) {
this->last_known_anim = anim;

View File

@ -26,6 +26,8 @@ public:
bool get_is_walking() const;
void set_walk_speed(float value);
float get_walk_speed() const;
void set_lock_running(bool value);
bool get_lock_running() const;
void set_is_running();
bool get_is_running() const;
void set_aim_weapon(bool value);
@ -36,6 +38,7 @@ public:
bool get_stab();
void death_animation();
bool match_tags(Tags tags) const;
gd::StringName const &get_current_state() const;
private:
void update_tags(gd::StringName const &anim);
void commit_turn_speed();
@ -50,6 +53,7 @@ private:
float turn_speed{0.f}; //!< blend position of turn animation (-1 to 1). Moved towards target_turn_speed every frame.
float target_turn_speed{0.f}; //!< target blend position of turn animation.
bool is_walking{false}; //!< set to true if the walk animation should be playing.
bool lock_running{false}; //!< lock animation into running instead of walking.
float walk_speed{0.f}; //!< blend amount between RESET/Rest animation and walk animation in walk state.
double running_time{0.0}; //!< time in seconds to keep running for.
bool aim_weapon{false}; //!< set to true to play the aim animation.

View File

@ -23,7 +23,7 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level)
utils::godot_cpp_utils_register_types();
GDREGISTER_CLASS(Player);
GDREGISTER_CLASS(PlayerAnimTree);
GDREGISTER_CLASS(Enemy);
GDREGISTER_RUNTIME_CLASS(Enemy);
GDREGISTER_CLASS(HitscanMuzzle);
GDREGISTER_RUNTIME_CLASS(CameraEffects);
GDREGISTER_RUNTIME_CLASS(CameraEffectSource);