feat: commenting pass
parent
4fde83c38b
commit
56485dd86e
|
@ -8,26 +8,35 @@
|
||||||
|
|
||||||
void Enemy::_bind_methods() {
|
void Enemy::_bind_methods() {
|
||||||
#define CLASSNAME Enemy
|
#define CLASSNAME Enemy
|
||||||
GDFUNCTION_ARGS(notice_player, "player");
|
|
||||||
GDPROPERTY(update_interval, gd::Variant::FLOAT);
|
GDPROPERTY(update_interval, gd::Variant::FLOAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enemy::_ready() {
|
void Enemy::_ready() {
|
||||||
this->anim_tree = this->get_node<PlayerAnimTree>("CharacterModel/AnimationTree");
|
this->anim_tree = this->get_node<PlayerAnimTree>("CharacterModel/AnimationTree"); // character animation tree from model (model shared with player)
|
||||||
this->agent = this->get_node<gd::NavigationAgent3D>("%NavigationAgent3D");
|
this->agent = this->get_node<gd::NavigationAgent3D>("%NavigationAgent3D"); // navigation agent
|
||||||
|
// set up the timer used to reduce process time
|
||||||
gd::Timer *timer{memnew(gd::Timer)};
|
gd::Timer *timer{memnew(gd::Timer)};
|
||||||
this->add_child(timer);
|
this->add_child(timer);
|
||||||
timer->start(this->update_interval);
|
timer->start(this->update_interval);
|
||||||
timer->connect("timeout", callable_mp(this, &Enemy::update));
|
timer->connect("timeout", callable_mp(this, &Enemy::update));
|
||||||
|
// starting target rotation is just the current rotation
|
||||||
this->target_rotation = this->get_rotation().y;
|
this->target_rotation = this->get_rotation().y;
|
||||||
|
// a sound the enemy makes while alive to induce tension and inform the player of an enemy nearby
|
||||||
this->drone_sound = this->get_node<gd::AudioStreamPlayer3D>("%DroneSound");
|
this->drone_sound = this->get_node<gd::AudioStreamPlayer3D>("%DroneSound");
|
||||||
|
// debugging label (only enable if available)
|
||||||
if(this->has_node("%DebugLabel"))
|
if(this->has_node("%DebugLabel"))
|
||||||
this->debug_label = this->get_node<gd::Label3D>("%DebugLabel");
|
this->debug_label = this->get_node<gd::Label3D>("%DebugLabel");
|
||||||
|
// setup navigation obstacle avoidance
|
||||||
this->agent->connect("velocity_computed", callable_mp(this, &Enemy::_on_velocity_calculated));
|
this->agent->connect("velocity_computed", callable_mp(this, &Enemy::_on_velocity_calculated));
|
||||||
|
// fetch player and setup current action
|
||||||
|
this->player = Player::get_player_instance();
|
||||||
|
this->current_action_fn = (ActionFn)&Enemy::wait_line_of_sight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enemy::_process(double delta) {
|
void Enemy::_process(double delta) {
|
||||||
|
// update debuging label
|
||||||
this->set_current_state_name(this->current_state_name);
|
this->set_current_state_name(this->current_state_name);
|
||||||
|
// rotate towards target (defined by either aiming or navigation)
|
||||||
float const angle_left{gd::Math::wrapf(this->target_rotation - this->get_rotation().y, -M_PIf, M_PIf)};
|
float const angle_left{gd::Math::wrapf(this->target_rotation - this->get_rotation().y, -M_PIf, M_PIf)};
|
||||||
float const step(gd::Math::sign(angle_left) * delta * (this->anim_tree->get_current_state().begins_with("Run") ? this->TURN_SPEED : this->AIM_SPEED));
|
float const step(gd::Math::sign(angle_left) * delta * (this->anim_tree->get_current_state().begins_with("Run") ? this->TURN_SPEED : this->AIM_SPEED));
|
||||||
if(gd::Math::abs(angle_left) <= gd::Math::abs(step)) {
|
if(gd::Math::abs(angle_left) <= gd::Math::abs(step)) {
|
||||||
|
@ -37,6 +46,7 @@ void Enemy::_process(double delta) {
|
||||||
this->rotate_y(step);
|
this->rotate_y(step);
|
||||||
this->at_target_angle = false;
|
this->at_target_angle = false;
|
||||||
}
|
}
|
||||||
|
// keep track of the player's last known transform for chasing
|
||||||
if(this->can_see_player) {
|
if(this->can_see_player) {
|
||||||
this->last_known_player_position = this->player->get_global_position();
|
this->last_known_player_position = this->player->get_global_position();
|
||||||
this->last_known_player_rotation = -this->player->get_global_rotation().y;
|
this->last_known_player_rotation = -this->player->get_global_rotation().y;
|
||||||
|
@ -148,15 +158,16 @@ Enemy::ActionFn Enemy::stop_running() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enemy::_physics_process(double delta) {
|
void Enemy::_physics_process(double delta) {
|
||||||
|
// transform root motion to global velocity
|
||||||
gd::Basis const basis{this->get_global_basis()};
|
gd::Basis const basis{this->get_global_basis()};
|
||||||
gd::Vector3 const motion{this->anim_tree->get_root_motion_position()};
|
gd::Vector3 const motion{this->anim_tree->get_root_motion_position()};
|
||||||
this->set_velocity(gd::Vector3{
|
this->set_velocity(gd::Vector3{
|
||||||
basis.get_column(0) * motion.x +
|
basis.get_column(0) * motion.x +
|
||||||
basis.get_column(1) * motion.y +
|
basis.get_column(1) * motion.y +
|
||||||
basis.get_column(2) * motion.z
|
basis.get_column(2) * motion.z
|
||||||
} / delta);
|
} / delta); // convert from m/s to m/frame
|
||||||
this->move_and_slide();
|
this->move_and_slide(); // update movement
|
||||||
this->update_can_see_player();
|
this->update_can_see_player(); // check vision
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enemy::damage() {
|
void Enemy::damage() {
|
||||||
|
@ -170,20 +181,20 @@ void Enemy::damage() {
|
||||||
this->set_current_state_name("None");
|
this->set_current_state_name("None");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enemy::notice_player(Player *player) {
|
|
||||||
this->player = player;
|
|
||||||
this->current_action_fn = (ActionFn)&Enemy::wait_line_of_sight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Enemy::update_can_see_player() {
|
void Enemy::update_can_see_player() {
|
||||||
if(this->player == nullptr)
|
// don't update sightlines if player is not found
|
||||||
|
// also don't update sightlines if the current action is firing, to avoid sudden turn-around shots that feel unfair
|
||||||
|
if(this->player == nullptr || this->current_action_fn == (ActionFn)&Enemy::wait_end_of_shot)
|
||||||
return;
|
return;
|
||||||
|
// calculate line segment to cast
|
||||||
gd::Vector3 const origin{this->get_global_position() + gd::Vector3{0.f, 1.8f, 0.f}};
|
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}};
|
gd::Vector3 const target{this->player->get_global_position() + gd::Vector3{0.f, 1.8f, 0.f}};
|
||||||
|
// check if the target is in field of view
|
||||||
float const dot{(target - origin).normalized().dot(this->get_global_basis().get_column(2))};
|
float const dot{(target - origin).normalized().dot(this->get_global_basis().get_column(2))};
|
||||||
if(this->current_action_fn != (ActionFn)&Enemy::chase_player && dot <= 0.2f && target.distance_to(origin) > 4.f) {
|
if(this->current_action_fn != (ActionFn)&Enemy::chase_player && dot <= 0.2f && target.distance_to(origin) > 4.f) {
|
||||||
this->can_see_player = false;
|
this->can_see_player = false; // target not in field of view
|
||||||
} else {
|
} else {
|
||||||
|
// check if the sightline is obstructed by raycast
|
||||||
gd::PhysicsDirectSpaceState3D *space{this->get_world_3d()->get_direct_space_state()};
|
gd::PhysicsDirectSpaceState3D *space{this->get_world_3d()->get_direct_space_state()};
|
||||||
gd::Ref<gd::PhysicsRayQueryParameters3D> query{gd::PhysicsRayQueryParameters3D::create(origin, target)};
|
gd::Ref<gd::PhysicsRayQueryParameters3D> query{gd::PhysicsRayQueryParameters3D::create(origin, target)};
|
||||||
gd::Dictionary dict{space->intersect_ray(query)};
|
gd::Dictionary dict{space->intersect_ray(query)};
|
||||||
|
|
|
@ -32,7 +32,6 @@ public:
|
||||||
ActionFn stop_running();
|
ActionFn stop_running();
|
||||||
virtual void _physics_process(double delta) override;
|
virtual void _physics_process(double delta) override;
|
||||||
virtual void damage() override;
|
virtual void damage() override;
|
||||||
void notice_player(Player *player);
|
|
||||||
void update_can_see_player();
|
void update_can_see_player();
|
||||||
void set_update_interval(float time);
|
void set_update_interval(float time);
|
||||||
float get_update_interval() const;
|
float get_update_interval() const;
|
||||||
|
@ -44,7 +43,7 @@ private:
|
||||||
int const SHOTS_BEFORE_MOVE{5};
|
int const SHOTS_BEFORE_MOVE{5};
|
||||||
float const AIM_SPEED{8.f};
|
float const AIM_SPEED{8.f};
|
||||||
float const TURN_SPEED{10.f};
|
float const TURN_SPEED{10.f};
|
||||||
float const AIM_OFFSET{-0.18f};
|
float const AIM_OFFSET{-0.2f};
|
||||||
float const STAB_RANGE{3.5f};
|
float const STAB_RANGE{3.5f};
|
||||||
float const MOVING_NAV_PRIORITY{.5f};
|
float const MOVING_NAV_PRIORITY{.5f};
|
||||||
float const STATIONARY_NAV_PRIORITY{1.f};
|
float const STATIONARY_NAV_PRIORITY{1.f};
|
||||||
|
|
|
@ -9,6 +9,10 @@ void Player::_bind_methods() {
|
||||||
GDFUNCTION(get_input_directions);
|
GDFUNCTION(get_input_directions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::_enter_tree() {
|
||||||
|
this->player_instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
void Player::_ready() {
|
void Player::_ready() {
|
||||||
if(gd::Engine::get_singleton()->is_editor_hint())
|
if(gd::Engine::get_singleton()->is_editor_hint())
|
||||||
return;
|
return;
|
||||||
|
@ -123,3 +127,9 @@ void Player::_on_switch_shoulder(gd::Ref<gd::InputEvent> event, float) {
|
||||||
gd::Vector2 Player::get_input_directions() const {
|
gd::Vector2 Player::get_input_directions() const {
|
||||||
return this->input_directions;
|
return this->input_directions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player *Player::get_player_instance() {
|
||||||
|
return Player::player_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player *Player::player_instance{nullptr};
|
||||||
|
|
|
@ -13,6 +13,7 @@ class Player : public gd::CharacterBody3D, public DamageableEntity {
|
||||||
GDCLASS(Player, gd::CharacterBody3D);
|
GDCLASS(Player, gd::CharacterBody3D);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
public:
|
public:
|
||||||
|
virtual void _enter_tree() override;
|
||||||
virtual void _ready() override;
|
virtual void _ready() override;
|
||||||
virtual void _process(double delta) override;
|
virtual void _process(double delta) override;
|
||||||
virtual void _physics_process(double delta) override;
|
virtual void _physics_process(double delta) override;
|
||||||
|
@ -29,6 +30,8 @@ public:
|
||||||
void _on_switch_shoulder(gd::Ref<gd::InputEvent> event, float);
|
void _on_switch_shoulder(gd::Ref<gd::InputEvent> event, float);
|
||||||
|
|
||||||
gd::Vector2 get_input_directions() const;
|
gd::Vector2 get_input_directions() const;
|
||||||
|
|
||||||
|
static Player *get_player_instance();
|
||||||
private:
|
private:
|
||||||
PlayerAnimTree *anim_tree{nullptr};
|
PlayerAnimTree *anim_tree{nullptr};
|
||||||
gd::Node3D *camera_parent{nullptr};
|
gd::Node3D *camera_parent{nullptr};
|
||||||
|
@ -46,6 +49,8 @@ private:
|
||||||
float const AIMING_CAMERA_ROTATION_SPEED{1.f};
|
float const AIMING_CAMERA_ROTATION_SPEED{1.f};
|
||||||
float const AIM_INPUT_THRESHOLD{-0.9f};
|
float const AIM_INPUT_THRESHOLD{-0.9f};
|
||||||
float const WALK_INPUT_THRESHOLD{0.5f};
|
float const WALK_INPUT_THRESHOLD{0.5f};
|
||||||
|
|
||||||
|
static Player *player_instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !TR_PLAYER_HPP
|
#endif // !TR_PLAYER_HPP
|
||||||
|
|
Loading…
Reference in New Issue