Files
sijcdusmode/scripts/key.gd
T
sijcdu 9dffacd938 Initial commit: Godot 4.7 pixel platformer prototype
Player: move/jump (Space), test flight (F, debug builds only). Key: pickup, hold S to aim with live trajectory preview, release to throw, W to warp to the thrown key. Spring bounce. Spikes deal damage. 3-heart health, Z consumes a heal item for +1 heart and a 5s shield. HUD with hearts and item count. Death screen with fade-to-black and restart. Hidden passages. Assets: Kenney Pixel Platformer (CC0).
2026-10-07 20:32:42 +08:00

126 lines
3.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Area2D
signal collected
## 钥匙中心到"脚底"的距离:射线从脚底出发,落地时钥匙正好站在表面上
const FOOT_OFFSET := 7.0
const STEP := 1.0 / 60.0
const MAX_STEPS := 240
enum State { IDLE, THROWN, LANDED }
## 关卡里那把钥匙原本是用 TerrainBase 的一块地砖画的。
## 这个开关让钥匙对象在开场时把脚下那块占位地砖擦掉,避免两个钥匙重叠。
@export var erase_placeholder_on_ready: bool = true
## 落地后多久才允许再次被捡起
@export var settle_time: float = 0.4
## 飞行时用哪几层做落地判定。玩家投掷时会把它设成玩家自己的 collision_mask,
## 所以"钥匙能落在哪"严格等于"玩家能站在哪"。
@export var flight_mask: int = 1
var state: State = State.IDLE
var velocity := Vector2.ZERO
var _settle := 0.0
func _ready() -> void:
body_entered.connect(_on_body_entered)
if erase_placeholder_on_ready:
_erase_placeholder_tile()
_start_idle()
func _physics_process(_delta: float) -> void:
if state == State.LANDED:
_settle += STEP
if _settle >= settle_time:
_start_idle()
return
if state != State.THROWN:
return
var g := gravity()
var motion := velocity * STEP
var from := global_position + Vector2(0.0, FOOT_OFFSET)
var query := PhysicsRayQueryParameters2D.create(from, from + motion, flight_mask)
var hit := get_world_2d().direct_space_state.intersect_ray(query)
if hit.is_empty():
global_position += motion
velocity.y += g * STEP
else:
global_position = hit["position"] - Vector2(0.0, FOOT_OFFSET)
velocity = Vector2.ZERO
state = State.LANDED
_settle = 0.0
# ---------------------------------------------------------------- 对外接口
func launch(v: Vector2) -> void:
velocity = v
state = State.THROWN
monitoring = false
_settle = 0.0
func _start_idle() -> void:
state = State.IDLE
velocity = Vector2.ZERO
monitoring = true
func _on_body_entered(body: Node2D) -> void:
if state != State.IDLE:
return
if body.has_method("collect_key"):
body.collect_key()
collected.emit()
queue_free()
# ---------------------------------------------------------------- 轨迹模拟
static func gravity() -> float:
return float(ProjectSettings.get_setting("physics/2d/default_gravity", 980.0))
## 和 _physics_process 里的推进方式完全一致,玩家用它来画预览弧线,
## 所以预览落点和真正落点会重合。mask 就是玩家的碰撞掩码。
static func simulate_trajectory(
space: PhysicsDirectSpaceState2D,
start: Vector2,
vel: Vector2,
mask: int,
max_steps: int = MAX_STEPS) -> Dictionary:
var points := PackedVector2Array()
var p := start
var v := vel
var g := gravity()
points.append(p)
for i in max_steps:
var next := p + v * STEP
var query := PhysicsRayQueryParameters2D.create(
p + Vector2(0.0, FOOT_OFFSET), next + Vector2(0.0, FOOT_OFFSET), mask)
var hit := space.intersect_ray(query)
if not hit.is_empty():
var landing: Vector2 = hit["position"] - Vector2(0.0, FOOT_OFFSET)
points.append(landing)
return {"points": points, "landed": true, "landing": landing}
p = next
v.y += g * STEP
points.append(p)
return {"points": points, "landed": false, "landing": p}
# ---------------------------------------------------------------- 清理占位地砖
func _erase_placeholder_tile() -> void:
var root: Node = owner if owner != null else get_parent()
if root == null:
return
for layer in root.find_children("*", "TileMapLayer", true, false):
var cell: Vector2i = layer.local_to_map(layer.to_local(global_position))
if layer.get_cell_source_id(cell) != -1:
layer.erase_cell(cell)