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="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" 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"]
|
||||
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)
|
||||
|
||||
[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")
|
||||
|
||||
[node name="HurtBox" type="Area3D" parent="." node_paths=PackedStringArray("owner_entity")]
|
||||
@@ -120,7 +120,7 @@ script = ExtResource("5_hurtbox")
|
||||
owner_entity = NodePath("..")
|
||||
|
||||
[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")
|
||||
|
||||
[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"]
|
||||
|
||||
[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:
|
||||
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)
|
||||
|
||||
# Route damage through server
|
||||
@@ -145,7 +151,7 @@ func perform_attack() -> bool:
|
||||
attack_performed.emit()
|
||||
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():
|
||||
if not _hitbox:
|
||||
return
|
||||
@@ -153,21 +159,14 @@ func _activate_hitbox():
|
||||
# Update owner reference in case it changed
|
||||
_hitbox.owner_entity = owner_character
|
||||
|
||||
# STARTUP PHASE - Wait before activating hitbox (wind-up)
|
||||
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
|
||||
# Activate hitbox immediately for entire animation duration
|
||||
_hitbox.activate()
|
||||
|
||||
var active = weapon_data.active_time if weapon_data else 0.2
|
||||
await get_tree().create_timer(active).timeout
|
||||
# Keep active for the full attack cooldown
|
||||
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):
|
||||
_hitbox.deactivate()
|
||||
|
||||
|
||||
@@ -20,14 +20,50 @@ var is_active: bool = false
|
||||
var _hits_this_attack: Array[Node] = []
|
||||
## Shape for queries (extracted from child CollisionShape3D)
|
||||
var _query_shape: Shape3D = null
|
||||
## Debug mesh for visualization
|
||||
var _debug_mesh: MeshInstance3D = null
|
||||
var _debug_material: StandardMaterial3D = null
|
||||
|
||||
func _ready():
|
||||
# Find the collision shape for queries
|
||||
for child in get_children():
|
||||
if child is CollisionShape3D and child.shape:
|
||||
_query_shape = child.shape
|
||||
_create_debug_visualization(child)
|
||||
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):
|
||||
if not is_active:
|
||||
return
|
||||
@@ -83,11 +119,17 @@ func _process_hit(hurtbox: HurtBox):
|
||||
func activate():
|
||||
is_active = true
|
||||
_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)
|
||||
func deactivate():
|
||||
is_active = false
|
||||
_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)
|
||||
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)
|
||||
@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():
|
||||
# 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
|
||||
monitorable = true
|
||||
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, "")
|
||||
else:
|
||||
# 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.animation_set_next("Attack_OneHand", "")
|
||||
|
||||
Reference in New Issue
Block a user