feat: added sound events for enemy awareness
This commit is contained in:
parent
1373656f90
commit
66aede32bd
|
@ -2,6 +2,7 @@
|
|||
#include "health_status.h"
|
||||
#include "hitbox.h"
|
||||
#include "macros.h"
|
||||
#include "muzzle_effect.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
void HitscanMuzzle::_bind_methods() {
|
||||
|
@ -9,6 +10,7 @@ void HitscanMuzzle::_bind_methods() {
|
|||
BIND_PROPERTY(Variant::FLOAT, spread);
|
||||
BIND_PROPERTY(Variant::INT, damage);
|
||||
BIND_PROPERTY(Variant::INT, ray_count);
|
||||
BIND_HPROPERTY(Variant::OBJECT, muzzle_effect, PROPERTY_HINT_NODE_TYPE, "MuzzleEffect");
|
||||
}
|
||||
|
||||
void HitscanMuzzle::instantiate_impact_effect() {
|
||||
|
@ -45,6 +47,7 @@ void HitscanMuzzle::ready() {
|
|||
} else {
|
||||
print_error("HitscanMuzzle::ready: impact effect is invalid");
|
||||
}
|
||||
|
||||
set_enabled(false);
|
||||
}
|
||||
|
||||
|
@ -62,6 +65,9 @@ void HitscanMuzzle::_notification(int what) {
|
|||
}
|
||||
|
||||
void HitscanMuzzle::shoot() {
|
||||
if (this->muzzle_effect != nullptr) {
|
||||
this->muzzle_effect->GDVIRTUAL_CALL(trigger);
|
||||
}
|
||||
for (int i{ this->ray_count }; i > 0; --i) {
|
||||
set_transform(this->home_transform);
|
||||
rotate_object_local(Vector3(0.f, 1.f, 0.f), Math::random(-Math::PI, Math::PI));
|
||||
|
@ -95,3 +101,11 @@ void HitscanMuzzle::set_ray_count(int amount) {
|
|||
int HitscanMuzzle::get_ray_count() const {
|
||||
return this->ray_count;
|
||||
}
|
||||
|
||||
void HitscanMuzzle::set_muzzle_effect(MuzzleEffect *effect) {
|
||||
this->muzzle_effect = effect;
|
||||
}
|
||||
|
||||
MuzzleEffect *HitscanMuzzle::get_muzzle_effect() const {
|
||||
return this->muzzle_effect;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define HITSCAN_MUZZLE_H
|
||||
|
||||
#include "scene/3d/physics/ray_cast_3d.h"
|
||||
class MuzzleEffect;
|
||||
|
||||
class HitscanMuzzle : public RayCast3D {
|
||||
GDCLASS(HitscanMuzzle, RayCast3D);
|
||||
|
@ -21,11 +22,14 @@ public:
|
|||
int get_damage() const;
|
||||
void set_ray_count(int amount);
|
||||
int get_ray_count() const;
|
||||
void set_muzzle_effect(MuzzleEffect *effect);
|
||||
MuzzleEffect *get_muzzle_effect() const;
|
||||
|
||||
private:
|
||||
float spread{ 0.001f };
|
||||
int damage{ 1 };
|
||||
int ray_count{ 1 };
|
||||
MuzzleEffect *muzzle_effect{ nullptr };
|
||||
|
||||
Transform3D home_transform{};
|
||||
Ref<PackedScene> impact_effect{};
|
||||
|
|
5
modules/wave_survival/muzzle_effect.cpp
Normal file
5
modules/wave_survival/muzzle_effect.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include "muzzle_effect.h"
|
||||
|
||||
void MuzzleEffect::_bind_methods() {
|
||||
GDVIRTUAL_BIND(trigger);
|
||||
}
|
14
modules/wave_survival/muzzle_effect.h
Normal file
14
modules/wave_survival/muzzle_effect.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef MUZZLE_EFFECT_H
|
||||
#define MUZZLE_EFFECT_H
|
||||
|
||||
#include "scene/3d/node_3d.h"
|
||||
|
||||
class MuzzleEffect : public Node3D {
|
||||
GDCLASS(MuzzleEffect, Node3D);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
GDVIRTUAL0_REQUIRED(trigger);
|
||||
};
|
||||
|
||||
#endif // !MUZZLE_EFFECT_H
|
|
@ -1,4 +1,5 @@
|
|||
#include "player_detector.h"
|
||||
#include "sound_event_patchboard.h"
|
||||
|
||||
String PlayerDetector::sig_awareness_changed{ "awareness_changed" };
|
||||
|
||||
|
@ -6,6 +7,17 @@ void PlayerDetector::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo(sig_awareness_changed, PropertyInfo(Variant::BOOL, "aware")));
|
||||
}
|
||||
|
||||
// check if there is geometry between detector and player
|
||||
bool PlayerDetector::line_of_sight_exists() const {
|
||||
PhysicsDirectSpaceState3D::RayParameters params{ this->ray_params };
|
||||
params.from = get_global_position();
|
||||
params.to = PlayerBody::get_singleton()->get_global_position();
|
||||
PhysicsDirectSpaceState3D *space{ get_world_3d()->get_direct_space_state() };
|
||||
PhysicsDirectSpaceState3D::RayResult result{};
|
||||
space->intersect_ray(params, result);
|
||||
return result.collider == nullptr;
|
||||
}
|
||||
|
||||
// Check if the player is in a bounded area in front of the detector and unobscured.
|
||||
// As all tests are required to pass, we do them in increasing order of complexity, to minimize unneeded resource use.
|
||||
bool PlayerDetector::check() const {
|
||||
|
@ -13,24 +25,18 @@ bool PlayerDetector::check() const {
|
|||
Vector3 const position{ get_global_position() };
|
||||
Vector3 const target{ PlayerBody::get_singleton()->get_global_position() + Vector3{ 0.f, 1.5f, 0.f } };
|
||||
// check if the target is in a view cone
|
||||
if (forward.dot(target - position) < this->min_dot) {
|
||||
if (forward.dot(target - position) < this->min_sight_dot) {
|
||||
return false;
|
||||
}
|
||||
// check if the target is in range
|
||||
if (position.distance_squared_to(target) > this->max_distance * this->max_distance) {
|
||||
if (position.distance_squared_to(target) > this->max_sight_range * this->max_sight_range) {
|
||||
return false;
|
||||
}
|
||||
// check if the target is obscured
|
||||
PhysicsDirectSpaceState3D::RayParameters params{ this->ray_params };
|
||||
params.from = position;
|
||||
params.to = target;
|
||||
PhysicsDirectSpaceState3D *space{ get_world_3d()->get_direct_space_state() };
|
||||
PhysicsDirectSpaceState3D::RayResult result{};
|
||||
space->intersect_ray(params, result);
|
||||
return result.collider == nullptr;
|
||||
return line_of_sight_exists();
|
||||
}
|
||||
|
||||
void PlayerDetector::ready() {
|
||||
SoundEventPatchboard::get_singleton()->connect(SoundEventPatchboard::sig_sound_triggered, callable_mp(this, &self_type::on_player_sound));
|
||||
this->ray_params.exclude.insert(PlayerBody::get_singleton()->get_rid());
|
||||
}
|
||||
|
||||
|
@ -46,6 +52,12 @@ void PlayerDetector::process(double delta) {
|
|||
}
|
||||
}
|
||||
|
||||
void PlayerDetector::on_player_sound(Vector3 at, float range) {
|
||||
if (get_global_position().distance_squared_to(at) <= range * range && line_of_sight_exists()) {
|
||||
set_aware_of_player(true);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerDetector::set_aware_of_player(bool value) {
|
||||
emit_signal(sig_awareness_changed, value);
|
||||
this->aware_of_player = value;
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
class PlayerDetector : public Node3D {
|
||||
GDCLASS(PlayerDetector, Node3D);
|
||||
static void _bind_methods();
|
||||
bool line_of_sight_exists() const;
|
||||
bool check() const;
|
||||
void ready();
|
||||
void process(double delta);
|
||||
void on_player_sound(Vector3 at, float range);
|
||||
void set_aware_of_player(bool value);
|
||||
|
||||
protected:
|
||||
|
@ -20,10 +22,15 @@ public:
|
|||
|
||||
private:
|
||||
bool aware_of_player{ false };
|
||||
float max_distance{ 100.f };
|
||||
float min_dot{ 0.1f };
|
||||
|
||||
float max_hearing_range{ 40.f };
|
||||
|
||||
float max_sight_range{ 100.f };
|
||||
float min_sight_dot{ 0.1f };
|
||||
|
||||
double query_time{ 0.3 };
|
||||
double query_timer{ 0.0 };
|
||||
|
||||
PhysicsDirectSpaceState3D::RayParameters ray_params{};
|
||||
|
||||
public:
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "wave_survival/hitbox.h"
|
||||
#include "wave_survival/hitscan_muzzle.h"
|
||||
#include "wave_survival/interactable.h"
|
||||
#include "wave_survival/muzzle_effect.h"
|
||||
#include "wave_survival/npc_unit.h"
|
||||
#include "wave_survival/patrol_path.h"
|
||||
#include "wave_survival/player_body.h"
|
||||
|
@ -15,6 +16,8 @@
|
|||
#include "wave_survival/player_detector.h"
|
||||
#include "wave_survival/player_input.h"
|
||||
#include "wave_survival/player_interactor.h"
|
||||
#include "wave_survival/sound_event_node.h"
|
||||
#include "wave_survival/sound_event_patchboard.h"
|
||||
#include "wave_survival/state.h"
|
||||
#include "wave_survival/state_machine.h"
|
||||
#include "wave_survival/weapon_base.h"
|
||||
|
@ -49,6 +52,12 @@ void initialize_wave_survival_module(ModuleInitializationLevel p_level) {
|
|||
GDREGISTER_CLASS(PlayerInteractor);
|
||||
GDREGISTER_CLASS(Interactable);
|
||||
GDREGISTER_CLASS(Revolver);
|
||||
GDREGISTER_CLASS(SoundEventPatchboard);
|
||||
GDREGISTER_CLASS(SoundEventNode);
|
||||
GDREGISTER_CLASS(MuzzleEffect);
|
||||
|
||||
memnew(SoundEventPatchboard);
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("SoundEventPatchboard", SoundEventPatchboard::get_singleton()));
|
||||
}
|
||||
|
||||
void uninitialize_wave_survival_module(ModuleInitializationLevel p_level) {
|
||||
|
|
44
modules/wave_survival/sound_event_node.cpp
Normal file
44
modules/wave_survival/sound_event_node.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "sound_event_node.h"
|
||||
#include "macros.h"
|
||||
#include "sound_event_patchboard.h"
|
||||
|
||||
void SoundEventNode::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("trigger_event"), &self_type::trigger_event);
|
||||
BIND_PROPERTY(Variant::BOOL, trigger_on_ready);
|
||||
}
|
||||
|
||||
void SoundEventNode::ready() {
|
||||
if (this->trigger_on_ready) {
|
||||
trigger_event();
|
||||
}
|
||||
}
|
||||
|
||||
void SoundEventNode::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (what == NOTIFICATION_READY) {
|
||||
ready();
|
||||
}
|
||||
}
|
||||
|
||||
void SoundEventNode::trigger_event() {
|
||||
SoundEventPatchboard::get_singleton()->trigger_sound(get_global_position(), this->range);
|
||||
}
|
||||
|
||||
void SoundEventNode::set_trigger_on_ready(bool value) {
|
||||
this->trigger_on_ready = value;
|
||||
}
|
||||
|
||||
bool SoundEventNode::get_trigger_on_ready() const {
|
||||
return this->trigger_on_ready;
|
||||
}
|
||||
|
||||
void SoundEventNode::set_range(float value) {
|
||||
this->range = value;
|
||||
}
|
||||
|
||||
float SoundEventNode::get_range() const {
|
||||
return this->range;
|
||||
}
|
26
modules/wave_survival/sound_event_node.h
Normal file
26
modules/wave_survival/sound_event_node.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef SOUND_EVENT_NODE_H
|
||||
#define SOUND_EVENT_NODE_H
|
||||
|
||||
#include "scene/3d/node_3d.h"
|
||||
|
||||
class SoundEventNode : public Node3D {
|
||||
GDCLASS(SoundEventNode, Node3D);
|
||||
static void _bind_methods();
|
||||
void ready();
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
|
||||
public:
|
||||
void trigger_event();
|
||||
void set_trigger_on_ready(bool value);
|
||||
bool get_trigger_on_ready() const;
|
||||
void set_range(float value);
|
||||
float get_range() const;
|
||||
|
||||
private:
|
||||
float range{ 20.f };
|
||||
bool trigger_on_ready{ true };
|
||||
};
|
||||
|
||||
#endif // !SOUND_EVENT_NODE_H
|
27
modules/wave_survival/sound_event_patchboard.cpp
Normal file
27
modules/wave_survival/sound_event_patchboard.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "sound_event_patchboard.h"
|
||||
|
||||
SoundEventPatchboard *SoundEventPatchboard::singleton_instance{ nullptr };
|
||||
String const SoundEventPatchboard::sig_sound_triggered{ "sound_triggered" };
|
||||
|
||||
void SoundEventPatchboard::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("trigger_sound", "at", "range"), &self_type::trigger_sound);
|
||||
ADD_SIGNAL(MethodInfo(sig_sound_triggered, PropertyInfo(Variant::VECTOR3, "at"), PropertyInfo(Variant::FLOAT, "range")));
|
||||
}
|
||||
|
||||
SoundEventPatchboard::SoundEventPatchboard() {
|
||||
singleton_instance = this;
|
||||
}
|
||||
|
||||
SoundEventPatchboard::~SoundEventPatchboard() {
|
||||
if (singleton_instance == this) {
|
||||
singleton_instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SoundEventPatchboard *SoundEventPatchboard::get_singleton() {
|
||||
return singleton_instance;
|
||||
}
|
||||
|
||||
void SoundEventPatchboard::trigger_sound(Vector3 at, float range) {
|
||||
emit_signal(sig_sound_triggered, at, range);
|
||||
}
|
22
modules/wave_survival/sound_event_patchboard.h
Normal file
22
modules/wave_survival/sound_event_patchboard.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef SOUND_EVENT_PATCHBOARD_H
|
||||
#define SOUND_EVENT_PATCHBOARD_H
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/object/object.h"
|
||||
|
||||
class SoundEventPatchboard : public Object {
|
||||
GDCLASS(SoundEventPatchboard, Object);
|
||||
static void _bind_methods();
|
||||
static SoundEventPatchboard *singleton_instance;
|
||||
|
||||
public:
|
||||
SoundEventPatchboard();
|
||||
virtual ~SoundEventPatchboard();
|
||||
static SoundEventPatchboard *get_singleton();
|
||||
void trigger_sound(Vector3 at, float range);
|
||||
|
||||
public:
|
||||
static String const sig_sound_triggered;
|
||||
};
|
||||
|
||||
#endif // !SOUND_EVENT_PATCHBOARD_H
|
Binary file not shown.
Binary file not shown.
|
@ -1,7 +1,14 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cfgwif53qypko"]
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cfgwif53qypko"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bkw6pt33nbn2" path="res://assets/models/weapons/revolver.blend" id="1_5ynga"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_5ynga"]
|
||||
script/source = "extends MuzzleEffect
|
||||
|
||||
func trigger() -> void:
|
||||
SoundEventPatchboard.trigger_sound(global_position, 100)
|
||||
"
|
||||
|
||||
[node name="Revolver" type="Revolver" node_paths=PackedStringArray("anim")]
|
||||
anim = NodePath("revolver/AnimationPlayer")
|
||||
single_action_spread = 0.003
|
||||
|
@ -15,12 +22,22 @@ layers = 2
|
|||
[node name="Cube" parent="revolver/Character/Skeleton3D" index="1"]
|
||||
layers = 2
|
||||
|
||||
[node name="HitscanMuzzle" type="HitscanMuzzle" parent="."]
|
||||
[node name="BoneAttachment3D" type="BoneAttachment3D" parent="revolver/Character/Skeleton3D" index="2"]
|
||||
transform = Transform3D(1, -6.350722e-17, 4.732016e-17, 4.732016e-17, 0.95822614, 0.28601173, -6.350722e-17, -0.28601173, 0.95822614, -1.1196792e-16, -0.03667751, 0.009908612)
|
||||
bone_name = "root"
|
||||
bone_idx = 39
|
||||
|
||||
[node name="MuzzleEffect" type="MuzzleEffect" parent="revolver/Character/Skeleton3D/BoneAttachment3D"]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.95768696, -0.28781185, 0, 0.28781185, 0.95768696, 2.4018701e-17, 0.19591203, -0.24464998)
|
||||
script = SubResource("GDScript_5ynga")
|
||||
|
||||
[node name="HitscanMuzzle" type="HitscanMuzzle" parent="." node_paths=PackedStringArray("muzzle_effect")]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(1, 0, 0, 0, -4.3711385e-08, 0.9999999, 0, -0.9999999, -4.3711385e-08, 0, 0, 0)
|
||||
target_position = Vector3(0, 200, 0)
|
||||
collision_mask = 6
|
||||
collide_with_areas = true
|
||||
spread = 0.02
|
||||
muzzle_effect = NodePath("../revolver/Character/Skeleton3D/BoneAttachment3D/MuzzleEffect")
|
||||
|
||||
[editable path="revolver"]
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://ce40pq785yoyi"]
|
||||
[gd_scene load_steps=3 format=3 uid="uid://ce40pq785yoyi"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bodfxp36t6v36" path="res://assets/models/weapons/rifle.blend" id="1_afgyw"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_afgyw"]
|
||||
script/source = "extends MuzzleEffect
|
||||
|
||||
func trigger() -> void:
|
||||
SoundEventPatchboard.trigger_sound(global_position, 100)
|
||||
"
|
||||
|
||||
[node name="Rifle" type="Rifle" node_paths=PackedStringArray("anim")]
|
||||
anim = NodePath("rifle/AnimationPlayer")
|
||||
|
||||
|
@ -13,10 +20,19 @@ layers = 2
|
|||
[node name="mesh" parent="rifle/Character/Skeleton3D" index="1"]
|
||||
layers = 2
|
||||
|
||||
[node name="BoneAttachment3D" type="BoneAttachment3D" parent="rifle/Character/Skeleton3D" index="2"]
|
||||
transform = Transform3D(1, -2.0932122e-15, 2.3524223e-18, 2.3525281e-18, 0.0022477505, 0.99999744, -2.0932126e-15, -0.9999974, 0.0022477508, 0.07988295, -0.13953947, -0.33976445)
|
||||
bone_name = "root"
|
||||
bone_idx = 39
|
||||
|
||||
[node name="MuzzleEffect" type="MuzzleEffect" parent="rifle/Character/Skeleton3D/BoneAttachment3D"]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.95768696, -0.28781185, 0, 0.28781185, 0.95768696, 2.4018701e-17, 0.19591203, -0.24464998)
|
||||
script = SubResource("GDScript_afgyw")
|
||||
|
||||
[node name="AnimationPlayer" parent="rifle" index="2"]
|
||||
playback_default_blend_time = 0.1
|
||||
|
||||
[node name="HitscanMuzzle" type="HitscanMuzzle" parent="."]
|
||||
[node name="HitscanMuzzle" type="HitscanMuzzle" parent="." node_paths=PackedStringArray("muzzle_effect")]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(1, -2.0932111e-15, -2.7987948e-18, 2.3525281e-18, -0.00021316158, 0.99999976, -2.0932126e-15, -0.9999997, -0.00021316111, 0, 0, 0)
|
||||
target_position = Vector3(0, 200, 0)
|
||||
|
@ -24,5 +40,6 @@ collision_mask = 6
|
|||
collide_with_areas = true
|
||||
spread = 0.003
|
||||
damage = 3
|
||||
muzzle_effect = NodePath("../rifle/Character/Skeleton3D/BoneAttachment3D/MuzzleEffect")
|
||||
|
||||
[editable path="rifle"]
|
||||
|
|
Loading…
Reference in a new issue