hitbox changes
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://b22ou40sbkavj" path="res://assets/characters/player/LilguyRigged.glb" id="2_lilguy"]
|
[ext_resource type="PackedScene" uid="uid://b22ou40sbkavj" path="res://assets/characters/player/LilguyRigged.glb" id="2_lilguy"]
|
||||||
[ext_resource type="Script" uid="uid://cf7jky1bcs560" path="res://level/scripts/lilguy_body.gd" id="3_body"]
|
[ext_resource type="Script" uid="uid://cf7jky1bcs560" path="res://level/scripts/lilguy_body.gd" id="3_body"]
|
||||||
[ext_resource type="Script" uid="uid://bj7yrijm7bppq" path="res://level/scripts/spring_arm_offset.gd" id="4_spring"]
|
[ext_resource type="Script" uid="uid://bj7yrijm7bppq" path="res://level/scripts/spring_arm_offset.gd" id="4_spring"]
|
||||||
[ext_resource type="Script" path="res://level/scripts/hurt_box.gd" id="5_hurtbox"]
|
[ext_resource type="Script" uid="uid://bj3uepduxvgju" path="res://level/scripts/hurt_box.gd" id="5_hurtbox"]
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_yxyay"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_yxyay"]
|
||||||
radius = 0.35796
|
radius = 0.35796
|
||||||
@@ -110,7 +110,7 @@ bone_idx = 10
|
|||||||
transform = Transform3D(-17.74905, -295.46814, -48.82108, 21.019196, -50.01525, 295.05362, -298.73593, 14.035805, 23.660797, 0.005859375, 0.39337158, 0.06616211)
|
transform = Transform3D(-17.74905, -295.46814, -48.82108, 21.019196, -50.01525, 295.05362, -298.73593, 14.035805, 23.660797, 0.005859375, 0.39337158, 0.06616211)
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.066, 0.828, 0.01)
|
transform = Transform3D(2, 0, 0, 0, 2, 0, 0, 0, 2, -0.066, 1.647685, 0.01)
|
||||||
shape = SubResource("CapsuleShape3D_yxyay")
|
shape = SubResource("CapsuleShape3D_yxyay")
|
||||||
|
|
||||||
[node name="HurtBox" type="Area3D" parent="." node_paths=PackedStringArray("owner_entity")]
|
[node name="HurtBox" type="Area3D" parent="." node_paths=PackedStringArray("owner_entity")]
|
||||||
@@ -120,7 +120,7 @@ script = ExtResource("5_hurtbox")
|
|||||||
owner_entity = NodePath("..")
|
owner_entity = NodePath("..")
|
||||||
|
|
||||||
[node name="HurtBoxShape" type="CollisionShape3D" parent="HurtBox"]
|
[node name="HurtBoxShape" type="CollisionShape3D" parent="HurtBox"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.066, 0.828, 0.01)
|
transform = Transform3D(1.9228287, 0, 0, 0, 1.4454772, 0, 0, 0, 1.4906956, -0.066, 2.0836046, 0.01)
|
||||||
shape = SubResource("CapsuleShape3D_hurtbox")
|
shape = SubResource("CapsuleShape3D_hurtbox")
|
||||||
|
|
||||||
[node name="SpringArmOffset" type="Node3D" parent="." node_paths=PackedStringArray("_spring_arm")]
|
[node name="SpringArmOffset" type="Node3D" parent="." node_paths=PackedStringArray("_spring_arm")]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=4 format=3]
|
[gd_scene load_steps=4 format=3 uid="uid://byxqw8bg5da2c"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://level/scripts/world_weapon.gd" id="1"]
|
[ext_resource type="Script" uid="uid://ccnnd0y4jqiot" path="res://level/scripts/world_weapon.gd" id="1"]
|
||||||
[ext_resource type="Resource" path="res://level/resources/weapon_sword.tres" id="2"]
|
[ext_resource type="Resource" path="res://level/resources/weapon_sword.tres" id="2"]
|
||||||
|
|
||||||
[sub_resource type="BoxShape3D" id="1"]
|
[sub_resource type="BoxShape3D" id="1"]
|
||||||
|
|||||||
@@ -90,6 +90,12 @@ func _on_hitbox_hit(target: Node, damage_amount: float, knockback_amount: float,
|
|||||||
if not target or not owner_character:
|
if not target or not owner_character:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Flash the target's hurtbox red for visual feedback
|
||||||
|
if target is Node:
|
||||||
|
var hurtbox = target.find_child("HurtBox", true, false)
|
||||||
|
if hurtbox and hurtbox.has_method("flash_hit"):
|
||||||
|
hurtbox.flash_hit()
|
||||||
|
|
||||||
hit_connected.emit(target)
|
hit_connected.emit(target)
|
||||||
|
|
||||||
# Route damage through server
|
# Route damage through server
|
||||||
@@ -145,7 +151,7 @@ func perform_attack() -> bool:
|
|||||||
attack_performed.emit()
|
attack_performed.emit()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
## Activate the hitbox for attack detection using frame data timing
|
## Activate the hitbox for attack detection - active for entire animation
|
||||||
func _activate_hitbox():
|
func _activate_hitbox():
|
||||||
if not _hitbox:
|
if not _hitbox:
|
||||||
return
|
return
|
||||||
@@ -153,21 +159,14 @@ func _activate_hitbox():
|
|||||||
# Update owner reference in case it changed
|
# Update owner reference in case it changed
|
||||||
_hitbox.owner_entity = owner_character
|
_hitbox.owner_entity = owner_character
|
||||||
|
|
||||||
# STARTUP PHASE - Wait before activating hitbox (wind-up)
|
# Activate hitbox immediately for entire animation duration
|
||||||
var startup = weapon_data.startup_time if weapon_data else 0.15
|
|
||||||
if startup > 0:
|
|
||||||
await get_tree().create_timer(startup).timeout
|
|
||||||
|
|
||||||
if not _hitbox or not is_instance_valid(_hitbox):
|
|
||||||
return
|
|
||||||
|
|
||||||
# ACTIVE PHASE - Hitbox is on, can deal damage
|
|
||||||
_hitbox.activate()
|
_hitbox.activate()
|
||||||
|
|
||||||
var active = weapon_data.active_time if weapon_data else 0.2
|
# Keep active for the full attack cooldown
|
||||||
await get_tree().create_timer(active).timeout
|
var duration = weapon_data.attack_cooldown if weapon_data else 0.5
|
||||||
|
await get_tree().create_timer(duration).timeout
|
||||||
|
|
||||||
# RECOVERY PHASE - Hitbox off (recovery time is remaining cooldown)
|
# Deactivate when attack is complete
|
||||||
if _hitbox and is_instance_valid(_hitbox):
|
if _hitbox and is_instance_valid(_hitbox):
|
||||||
_hitbox.deactivate()
|
_hitbox.deactivate()
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,50 @@ var is_active: bool = false
|
|||||||
var _hits_this_attack: Array[Node] = []
|
var _hits_this_attack: Array[Node] = []
|
||||||
## Shape for queries (extracted from child CollisionShape3D)
|
## Shape for queries (extracted from child CollisionShape3D)
|
||||||
var _query_shape: Shape3D = null
|
var _query_shape: Shape3D = null
|
||||||
|
## Debug mesh for visualization
|
||||||
|
var _debug_mesh: MeshInstance3D = null
|
||||||
|
var _debug_material: StandardMaterial3D = null
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# Find the collision shape for queries
|
# Find the collision shape for queries
|
||||||
for child in get_children():
|
for child in get_children():
|
||||||
if child is CollisionShape3D and child.shape:
|
if child is CollisionShape3D and child.shape:
|
||||||
_query_shape = child.shape
|
_query_shape = child.shape
|
||||||
|
_create_debug_visualization(child)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
func _create_debug_visualization(collision_shape: CollisionShape3D):
|
||||||
|
# Create a semi-transparent red mesh to visualize the hitbox
|
||||||
|
_debug_mesh = MeshInstance3D.new()
|
||||||
|
_debug_material = StandardMaterial3D.new()
|
||||||
|
_debug_material.albedo_color = Color(1.0, 0.0, 0.0, 0.4) # Red, semi-transparent
|
||||||
|
_debug_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
_debug_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
_debug_material.cull_mode = BaseMaterial3D.CULL_DISABLED # Visible from both sides
|
||||||
|
|
||||||
|
# Create mesh matching the collision shape
|
||||||
|
var mesh: Mesh = null
|
||||||
|
if collision_shape.shape is BoxShape3D:
|
||||||
|
var box_mesh = BoxMesh.new()
|
||||||
|
box_mesh.size = collision_shape.shape.size
|
||||||
|
mesh = box_mesh
|
||||||
|
elif collision_shape.shape is SphereShape3D:
|
||||||
|
var sphere_mesh = SphereMesh.new()
|
||||||
|
sphere_mesh.radius = collision_shape.shape.radius
|
||||||
|
sphere_mesh.height = collision_shape.shape.radius * 2
|
||||||
|
mesh = sphere_mesh
|
||||||
|
elif collision_shape.shape is CapsuleShape3D:
|
||||||
|
var capsule_mesh = CapsuleMesh.new()
|
||||||
|
capsule_mesh.radius = collision_shape.shape.radius
|
||||||
|
capsule_mesh.height = collision_shape.shape.height
|
||||||
|
mesh = capsule_mesh
|
||||||
|
|
||||||
|
if mesh:
|
||||||
|
_debug_mesh.mesh = mesh
|
||||||
|
_debug_mesh.material_override = _debug_material
|
||||||
|
# Don't set transform - it inherits from parent CollisionShape3D
|
||||||
|
collision_shape.add_child(_debug_mesh)
|
||||||
|
|
||||||
func _physics_process(_delta):
|
func _physics_process(_delta):
|
||||||
if not is_active:
|
if not is_active:
|
||||||
return
|
return
|
||||||
@@ -83,11 +119,17 @@ func _process_hit(hurtbox: HurtBox):
|
|||||||
func activate():
|
func activate():
|
||||||
is_active = true
|
is_active = true
|
||||||
_hits_this_attack.clear()
|
_hits_this_attack.clear()
|
||||||
|
# Change to yellow when active
|
||||||
|
if _debug_material:
|
||||||
|
_debug_material.albedo_color = Color(1.0, 1.0, 0.0, 0.5) # Yellow, semi-transparent
|
||||||
|
|
||||||
## Deactivate hitbox (call when attack ends)
|
## Deactivate hitbox (call when attack ends)
|
||||||
func deactivate():
|
func deactivate():
|
||||||
is_active = false
|
is_active = false
|
||||||
_hits_this_attack.clear()
|
_hits_this_attack.clear()
|
||||||
|
# Change back to red when inactive
|
||||||
|
if _debug_material:
|
||||||
|
_debug_material.albedo_color = Color(1.0, 0.0, 0.0, 0.4) # Red, semi-transparent
|
||||||
|
|
||||||
## Set damage stats (usually from weapon data)
|
## Set damage stats (usually from weapon data)
|
||||||
func set_stats(new_damage: float, new_knockback: float):
|
func set_stats(new_damage: float, new_knockback: float):
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ class_name HurtBox
|
|||||||
|
|
||||||
## The entity that owns this hurtbox (should be a BaseUnit or similar)
|
## The entity that owns this hurtbox (should be a BaseUnit or similar)
|
||||||
@export var owner_entity: Node = null
|
@export var owner_entity: Node = null
|
||||||
|
## Debug mesh for visualization
|
||||||
|
var _debug_mesh: MeshInstance3D = null
|
||||||
|
var _debug_material: StandardMaterial3D = null
|
||||||
|
var _hit_flash_timer: float = 0.0
|
||||||
|
const HIT_FLASH_DURATION: float = 0.3 # seconds
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# Auto-find owner if not set (traverse up to find BaseUnit)
|
# Auto-find owner if not set (traverse up to find BaseUnit)
|
||||||
@@ -25,3 +30,57 @@ func _ready():
|
|||||||
# Ensure we can be detected but don't detect others
|
# Ensure we can be detected but don't detect others
|
||||||
monitorable = true
|
monitorable = true
|
||||||
monitoring = false
|
monitoring = false
|
||||||
|
|
||||||
|
# Add debug visualization
|
||||||
|
_create_debug_visualization()
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
# Handle hit flash timer
|
||||||
|
if _hit_flash_timer > 0.0:
|
||||||
|
_hit_flash_timer -= delta
|
||||||
|
if _hit_flash_timer <= 0.0:
|
||||||
|
# Flash finished, return to green
|
||||||
|
if _debug_material:
|
||||||
|
_debug_material.albedo_color = Color(0.0, 1.0, 0.0, 0.3) # Green
|
||||||
|
|
||||||
|
func _create_debug_visualization():
|
||||||
|
# Find the collision shape to visualize
|
||||||
|
for child in get_children():
|
||||||
|
if child is CollisionShape3D and child.shape:
|
||||||
|
# Create a semi-transparent green mesh to visualize the hurtbox
|
||||||
|
_debug_mesh = MeshInstance3D.new()
|
||||||
|
_debug_material = StandardMaterial3D.new()
|
||||||
|
_debug_material.albedo_color = Color(0.0, 1.0, 0.0, 0.3) # Green, semi-transparent
|
||||||
|
_debug_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
_debug_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
_debug_material.cull_mode = BaseMaterial3D.CULL_DISABLED # Visible from both sides
|
||||||
|
|
||||||
|
# Create mesh matching the collision shape
|
||||||
|
var mesh: Mesh = null
|
||||||
|
if child.shape is BoxShape3D:
|
||||||
|
var box_mesh = BoxMesh.new()
|
||||||
|
box_mesh.size = child.shape.size
|
||||||
|
mesh = box_mesh
|
||||||
|
elif child.shape is SphereShape3D:
|
||||||
|
var sphere_mesh = SphereMesh.new()
|
||||||
|
sphere_mesh.radius = child.shape.radius
|
||||||
|
sphere_mesh.height = child.shape.radius * 2
|
||||||
|
mesh = sphere_mesh
|
||||||
|
elif child.shape is CapsuleShape3D:
|
||||||
|
var capsule_mesh = CapsuleMesh.new()
|
||||||
|
capsule_mesh.radius = child.shape.radius
|
||||||
|
capsule_mesh.height = child.shape.height
|
||||||
|
mesh = capsule_mesh
|
||||||
|
|
||||||
|
if mesh:
|
||||||
|
_debug_mesh.mesh = mesh
|
||||||
|
_debug_mesh.material_override = _debug_material
|
||||||
|
# Don't set transform - it inherits from parent CollisionShape3D
|
||||||
|
child.add_child(_debug_mesh)
|
||||||
|
break
|
||||||
|
|
||||||
|
## Call this when the hurtbox is hit to flash red
|
||||||
|
func flash_hit():
|
||||||
|
_hit_flash_timer = HIT_FLASH_DURATION
|
||||||
|
if _debug_material:
|
||||||
|
_debug_material.albedo_color = Color(1.0, 0.0, 0.0, 0.5) # Red, semi-transparent
|
||||||
|
|||||||
@@ -62,6 +62,6 @@ func play_attack(anim_name: String = "Attack_OneHand") -> void:
|
|||||||
animation_player.animation_set_next(anim_name, "")
|
animation_player.animation_set_next(anim_name, "")
|
||||||
else:
|
else:
|
||||||
# Fallback to default if animation doesn't exist
|
# Fallback to default if animation doesn't exist
|
||||||
push_warning("Animation '%s' not found, using Attack_OneHand" % anim_name)
|
#push_warning("Animation '%s' not found, using Attack_OneHand" % anim_name)
|
||||||
animation_player.play("Attack_OneHand", -1, 1.0)
|
animation_player.play("Attack_OneHand", -1, 1.0)
|
||||||
animation_player.animation_set_next("Attack_OneHand", "")
|
animation_player.animation_set_next("Attack_OneHand", "")
|
||||||
|
|||||||
Reference in New Issue
Block a user