2025-04-21 13:31:12 +00:00
|
|
|
@tool
|
|
|
|
|
|
|
|
extends RigidBody2D
|
|
|
|
|
2025-07-04 18:01:02 +00:00
|
|
|
signal hit_ball_touched
|
2025-07-04 22:05:44 +00:00
|
|
|
signal hit_ball_finished(count: int)
|
2025-04-21 13:31:12 +00:00
|
|
|
signal hit_boundary
|
|
|
|
|
2025-06-05 07:17:51 +00:00
|
|
|
@export_enum("1", "2", "3", "4", "5", "6", "7") var id := "1":
|
2025-04-21 13:31:12 +00:00
|
|
|
set(value):
|
|
|
|
id = value
|
|
|
|
if Engine.is_editor_hint() and is_node_ready():
|
|
|
|
_set_up_texture()
|
|
|
|
@export var is_shooter := false:
|
|
|
|
set(value):
|
|
|
|
is_shooter = value
|
|
|
|
@export var enable := true:
|
|
|
|
set(value):
|
|
|
|
enable = value
|
|
|
|
if is_node_ready():
|
|
|
|
$CollisionShape2D.set_deferred("disabled", !value)
|
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
@onready var collision_shape = $CollisionShape2D as CollisionShape2D
|
2025-04-21 13:31:12 +00:00
|
|
|
@onready var sprite2d = $Sprite2D as Sprite2D
|
2025-05-13 11:45:33 +00:00
|
|
|
@onready var shadow = $Shadow as Sprite2D
|
2025-04-21 13:31:12 +00:00
|
|
|
|
|
|
|
const texture_dict = {
|
2025-05-13 11:45:33 +00:00
|
|
|
"1": preload("res://asset/art/little_game/弹珠/球1.png"),
|
|
|
|
"2": preload("res://asset/art/little_game/弹珠/球2.png"),
|
|
|
|
"3": preload("res://asset/art/little_game/弹珠/球3.png"),
|
|
|
|
"4": preload("res://asset/art/little_game/弹珠/球4.png"),
|
|
|
|
"5": preload("res://asset/art/little_game/弹珠/球5.png"),
|
|
|
|
"6": preload("res://asset/art/little_game/弹珠/球6.png"),
|
|
|
|
"7": preload("res://asset/art/little_game/弹珠/球7.png"),
|
2025-04-21 13:31:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2025-06-05 07:17:51 +00:00
|
|
|
if is_shooter:
|
|
|
|
linear_damp = 0
|
2025-04-21 13:31:12 +00:00
|
|
|
contact_monitor = true
|
|
|
|
max_contacts_reported = 1
|
|
|
|
_set_up_texture()
|
|
|
|
body_entered.connect(_on_body_entered)
|
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
|
2025-06-05 07:17:51 +00:00
|
|
|
func dispatch(origin_pos: Vector2):
|
|
|
|
visible = true
|
|
|
|
modulate.a = 0.3
|
|
|
|
var target_pos = position
|
|
|
|
position = origin_pos
|
|
|
|
collision_shape.disabled = true
|
|
|
|
var tween = create_tween()
|
|
|
|
tween.tween_property(self, "modulate:a", 1.0, 0.4)
|
|
|
|
tween.parallel().tween_property(self, "rotation", 4.0, 0.8).set_ease(Tween.EASE_IN_OUT)
|
|
|
|
tween.parallel().tween_property(self, "position", target_pos, 0.8).set_ease(Tween.EASE_IN_OUT)
|
|
|
|
tween.tween_callback(func(): collision_shape.disabled = false)
|
|
|
|
|
|
|
|
|
|
|
|
func hide_away():
|
|
|
|
visible = false
|
|
|
|
collision_shape.disabled = true
|
|
|
|
|
2025-07-04 18:01:02 +00:00
|
|
|
|
2025-06-05 09:37:30 +00:00
|
|
|
func ease_out():
|
|
|
|
collision_shape.disabled = true
|
|
|
|
create_tween().tween_property(self, "modulate:a", 0.0, 0.5)
|
|
|
|
|
2025-06-05 07:17:51 +00:00
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
func rand_id():
|
|
|
|
# 随机 id
|
|
|
|
var rand = randi() % 3 + 1
|
|
|
|
id = str(rand)
|
|
|
|
_set_up_texture()
|
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
func _set_up_texture():
|
|
|
|
sprite2d.texture = texture_dict[id]
|
|
|
|
|
|
|
|
|
2025-07-04 18:01:02 +00:00
|
|
|
var shoot_count := 0
|
|
|
|
var shooted_instances = {}
|
2025-04-21 13:31:12 +00:00
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
func _on_body_entered(node: Node):
|
2025-07-04 18:01:02 +00:00
|
|
|
if not is_shooter:
|
2025-04-21 13:31:12 +00:00
|
|
|
return
|
2025-07-04 18:01:02 +00:00
|
|
|
var tween
|
2025-04-21 13:31:12 +00:00
|
|
|
if node is StaticBody2D:
|
2025-07-04 18:01:02 +00:00
|
|
|
if shoot_count == 0:
|
|
|
|
tween = create_tween()
|
|
|
|
tween.tween_property(self, "modulate:a", 0.0, 1.0)
|
|
|
|
tween.tween_callback(hit_boundary.emit)
|
|
|
|
tween.tween_callback(queue_free)
|
|
|
|
return
|
|
|
|
# 过滤抖动/重复碰撞
|
|
|
|
var instance_id = node.get_instance_id()
|
|
|
|
if shooted_instances.has(instance_id):
|
2025-04-21 13:31:12 +00:00
|
|
|
return
|
2025-07-04 18:01:02 +00:00
|
|
|
print("[debug] first hit node", node.get_path())
|
|
|
|
shooted_instances[instance_id] = true
|
|
|
|
shoot_count += 1
|
2025-07-04 22:05:44 +00:00
|
|
|
# 从被打击到的 ball 上建立 tween
|
|
|
|
tween = node.create_tween()
|
|
|
|
# sfx 碰撞音效
|
2025-07-04 18:01:02 +00:00
|
|
|
hit_ball_touched.emit()
|
2025-07-04 22:05:44 +00:00
|
|
|
# 新碰到会刷新 modulate:a -> 0 时间
|
|
|
|
tween.tween_property(self, "modulate:a", 0.0, 1.5).set_ease(Tween.EASE_IN)
|
2025-06-05 09:37:30 +00:00
|
|
|
tween.parallel().tween_property(node, "modulate:a", 0.0, 1.5)
|
2025-07-04 22:05:44 +00:00
|
|
|
# 和第一个碰撞的球消失时一起消失
|
|
|
|
if shoot_count == 1:
|
|
|
|
tween.tween_callback(_on_shoot_finished)
|
2025-04-21 13:31:12 +00:00
|
|
|
tween.tween_callback(node.queue_free)
|
2025-07-04 18:01:02 +00:00
|
|
|
print("[ball] shoot count: ", shoot_count)
|
|
|
|
if shoot_count >= 3:
|
|
|
|
#TODO 连续射中三个成就
|
|
|
|
pass
|
2025-04-21 13:31:12 +00:00
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
|
2025-07-04 22:05:44 +00:00
|
|
|
func _on_shoot_finished() -> void:
|
|
|
|
# 计数+shoot_count & reload_hand_ball
|
|
|
|
hit_ball_finished.emit(shoot_count)
|
|
|
|
queue_free()
|
|
|
|
|
|
|
|
|
2025-04-21 13:31:12 +00:00
|
|
|
func _process(_delta: float) -> void:
|
2025-05-13 11:45:33 +00:00
|
|
|
if Engine.is_editor_hint():
|
|
|
|
return
|
2025-04-21 13:31:12 +00:00
|
|
|
# 根据 y 坐标缩放
|
2025-05-13 11:45:33 +00:00
|
|
|
var ball_scale = 1.0 + (global_position.y / 400.0)
|
|
|
|
sprite2d.scale = Vector2(ball_scale, ball_scale) * 0.4
|
|
|
|
shadow.scale = Vector2(ball_scale, ball_scale)
|
|
|
|
shadow.rotation = -rotation
|
|
|
|
shadow.position = Vector2(0, 6.0 * ball_scale).rotated(-rotation)
|
|
|
|
collision_shape.scale = Vector2(ball_scale, ball_scale)
|