feat: updated enemy

This commit is contained in:
Sara 2024-12-11 18:39:41 +01:00
parent c3a4f8baae
commit 84673c6c29
9 changed files with 257 additions and 147 deletions

View file

@ -19,31 +19,30 @@ void Enemy::_ready() {
this->add_child(timer);
timer->start(this->update_interval);
timer->connect("timeout", callable_mp(this, &Enemy::update));
this->target_rotation = this->get_rotation().y;
}
void Enemy::_process(double delta) {
float const angle_left{this->target_rotation - this->get_rotation().y};
float const step(gd::Math::sign(angle_left) * delta * this->TURN_SPEED);
if(gd::Math::abs(angle_left) <= gd::Math::abs(step)) {
this->rotate_y(angle_left);
this->at_target_angle = true;
} else {
this->rotate_y(step);
this->at_target_angle = false;
}
}
void Enemy::update() {
if(this->can_see_player)
this->last_known_player_position = this->player->get_global_position();
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 at_end{!this->agent->is_navigation_finished()};
this->anim_tree->set_lock_running(at_end);
this->anim_tree->set_aim_weapon(!at_end);
if(at_end) {
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});
} else 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::wait_line_of_sight() {
if(this->can_see_player)
return (ActionFn)&Enemy::take_aim;
else
@ -51,49 +50,37 @@ Enemy::ActionFn Enemy::wait_line_of_sight() {
}
Enemy::ActionFn Enemy::take_aim() {
this->target_rotation = this->get_global_basis().get_column(2).signed_angle_to(this->last_known_player_position - this->get_global_position(), {0.f, 1.f, 0.f});
this->target_rotation -= gd::Math::sign(this->target_rotation) * this->MISS_ANGLE;
this->target_rotation += this->get_global_rotation().y;
this->anim_tree->set_aim_weapon(true);
if(this->anim_tree->get_current_state().begins_with("Aim"))
return (ActionFn)&Enemy::hit;
if(this->anim_tree->get_current_state().begins_with("Aim") && this->at_target_angle)
return (ActionFn)&Enemy::fire;
else
return(ActionFn)&Enemy::take_aim;
}
Enemy::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) * 0.6f);
this->anim_tree->set_aim_weapon(true);
this->anim_tree->set_fire_weapon();
++this->missed_shots;
return (ActionFn)&Enemy::wait_end_of_shot;
} else {
this->chase();
}
return (ActionFn)&Enemy::miss;
}
Enemy::ActionFn Enemy::hit() {
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::wait_end_of_shot;
} else {
this->chase();
}
return (ActionFn)&Enemy::hit;
Enemy::ActionFn Enemy::fire() {
this->anim_tree->set_fire_weapon();
this->missed_shots = (this->missed_shots + 1) % (this->SHOTS_BEFORE_HIT + 1);
return (ActionFn)&Enemy::wait_end_of_shot;
}
Enemy::ActionFn Enemy::wait_end_of_shot() {
if(this->anim_tree->get_current_state().begins_with("Fire") || this->anim_tree->get_fire_weapon()) // last shot still going
return (ActionFn)&Enemy::wait_end_of_shot;
else
return this->missed_shots >= SHOTS_BEFORE_HIT ? (ActionFn)&Enemy::hit : (ActionFn)&Enemy::miss;
float const target_diff{this->get_global_basis().get_column(2).signed_angle_to(this->last_known_player_position - this->get_global_position(), {0.f, 1.f, 0.f})};
this->target_rotation = this->get_global_rotation().y + target_diff;
if(this->missed_shots < this->SHOTS_BEFORE_HIT)
this->target_rotation -= gd::Math::sign(target_diff) * this->MISS_ANGLE;
else if(this->missed_shots >= this->SHOTS_BEFORE_HIT)
this->target_rotation -= gd::Math::sign(target_diff) * this->HIT_ANGLE;
if(this->at_target_angle && !this->anim_tree->get_current_state().begins_with("Fire") && !this->anim_tree->get_fire_weapon())
return (ActionFn)&Enemy::fire;
else
return (ActionFn)&Enemy::wait_end_of_shot;
}
void Enemy::_physics_process(double delta) {
void Enemy::_physics_process(double delta [[maybe_unused]]) {
this->update_can_see_player();
gd::Basis const basis{this->get_global_basis()};
gd::Vector3 const motion{this->anim_tree->get_root_motion_position()};
@ -119,11 +106,17 @@ void Enemy::notice_player(Player *player) {
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);
gd::Vector3 const origin{this->get_global_position() + gd::Vector3{0.f, 1.8f, 0.f}};
gd::Vector3 const target{this->player->get_global_position() + gd::Vector3{0.f, 1.8f, 0.f}};
float const dot{(target - origin).normalized().dot(this->get_global_basis().get_column(2))};
if(dot <= 0.2f && target.distance_to(origin) > 4.f) {
this->can_see_player = false;
} else {
gd::PhysicsDirectSpaceState3D *space{this->get_world_3d()->get_direct_space_state()};
gd::Ref<gd::PhysicsRayQueryParameters3D> query{gd::PhysicsRayQueryParameters3D::create(origin, target)};
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) {

View file

@ -16,27 +16,29 @@ typedef void *(Enemy::*ActionFn_)();
typedef ActionFn_ (Enemy::*ActionFn)();
public:
virtual void _ready() override;
virtual void _process(double delta) override;
void update();
void chase_enter();
void chase();
ActionFn wait_line_of_sight();
ActionFn take_aim();
ActionFn miss();
ActionFn hit();
ActionFn fire();
ActionFn stab_enter();
ActionFn stab();
ActionFn wait_end_of_shot();
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{1};
int const SHOTS_BEFORE_HIT{0};
float const TURN_SPEED{3.f};
float const HIT_ANGLE{.05f};
float const MISS_ANGLE{.2f};
float target_rotation{0.f};
bool at_target_angle{false};
gd::Vector3 last_known_player_position{0.f, 0.f, 0.f};
int missed_shots{0};
double update_interval{0.4};
ActionFn current_action_fn{nullptr};