Add level 3: bullet rain from the sky, and ropes that only block bullets

The tileset gained a second physics layer (collision_layer 2) and the rope tile (11,4) now carries its 18x6 polygon on that layer instead of layer 0, so it is invisible to anything that does not ask for layer 2. The player's mask stays 1 and the key copies it, so both fall straight through ropes; the bullet probe mask went from 1 to 3 so falling bullets are stopped by them. New bullet_rain prefab drops bullets at random x across the level on a jittered timer, sparse by default (1.0s +- 0.6s), and level 3 places one spanning the whole width. Level 3 has no lasers, and level 2's clear screen now leads into it.
This commit is contained in:
2026-10-07 22:51:56 +08:00
parent 8391e1c5da
commit acc7901c2a
7 changed files with 330 additions and 1 deletions
@@ -132,6 +132,7 @@ use_texture_padding = false
9:4/0 = 0
10:4/0 = 0
11:4/0 = 0
11:4/0/physics_layer_1/polygon_0/points = PackedVector2Array(-9, -3, 9, -3, 9, 3, -9, 3)
12:4/0 = 0
13:4/0 = 0
14:4/0 = 0
@@ -264,4 +265,5 @@ use_texture_padding = false
[resource]
tile_size = Vector2i(18, 18)
physics_layer_0/collision_layer = 1
physics_layer_1/collision_layer = 2
sources/0 = SubResource("TileSetAtlasSource_afvjv")
+1
View File
@@ -28,6 +28,7 @@
[node name="DeathScreen" parent="." unique_id=1952687359 instance=ExtResource("2_cqal4")]
[node name="LevelClear" parent="." unique_id=1589634220 instance=ExtResource("3_o3sye")]
next_level_scene = "res://scenes/level3.tscn"
[node name="Level" type="Node2D" parent="." unique_id=1678372922]
+255
View File
@@ -0,0 +1,255 @@
[gd_scene format=4 uid="uid://csvqda2jbvh2o"]
[ext_resource type="PackedScene" path="res://scenes/hud.tscn" id="1_07lh1"]
[ext_resource type="PackedScene" path="res://scenes/death_screen.tscn" id="2_6uuvf"]
[ext_resource type="PackedScene" path="res://scenes/level_clear.tscn" id="3_x008a"]
[ext_resource type="Texture2D" uid="uid://djmk0srfgyafe" path="res://assets/game/backgrounds/cloud_left.png" id="4_atr84"]
[ext_resource type="Texture2D" uid="uid://dptqmtay4plwp" path="res://assets/game/backgrounds/cloud_trees.png" id="5_qyu38"]
[ext_resource type="Texture2D" uid="uid://dys7320ghojh6" path="res://assets/game/backgrounds/cloud_hills.png" id="6_8vbrh"]
[ext_resource type="Texture2D" uid="uid://1b8hp0rm864l" path="res://assets/game/backgrounds/cloud_right.png" id="7_d5gix"]
[ext_resource type="Script" uid="uid://dxwa7fxghp3a2" path="res://scripts/hidden_passages.gd" id="8_7hs6g"]
[ext_resource type="TileSet" uid="uid://dkowcf1ljh41j" path="res://assets/game/tilesets/kenney_terrain_tileset.tres" id="9_yeq8b"]
[ext_resource type="PackedScene" uid="uid://c1u8k6ny02t4y" path="res://scenes/player.tscn" id="10_invaa"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazards/spikes_double.tscn" id="11_88b2t"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazards/spikes_grey.tscn" id="12_0ydwj"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/interactables/spring_red.tscn" id="15_usk20"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/interactables/door.tscn" id="16_y8u0o"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/coin_gold_round.tscn" id="17_16ffm"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/key_gold.tscn" id="18_kvrlk"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/diamond.tscn" id="19_85fmh"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemies/bullet_rain.tscn" id="20_rain"]
[node name="Main" type="Node2D" unique_id=2061129075]
[node name="HUD" parent="." unique_id=215179112 instance=ExtResource("1_07lh1")]
[node name="DeathScreen" parent="." unique_id=1952687359 instance=ExtResource("2_6uuvf")]
[node name="LevelClear" parent="." unique_id=1589634220 instance=ExtResource("3_x008a")]
[node name="Level" type="Node2D" parent="." unique_id=1678372922]
[node name="Background" type="Node2D" parent="Level" unique_id=702011751]
[node name="SkyColor" type="Polygon2D" parent="Level/Background" unique_id=22763597]
z_index = -30
color = Color(0.8509804, 0.9411765, 0.9372549, 1)
polygon = PackedVector2Array(0, 0, 468, 0, 468, 130.0972, 468, 270, 0, 270)
[node name="CloudBand_01" type="Sprite2D" parent="Level/Background" unique_id=589633713]
z_index = -20
texture_filter = 1
position = Vector2(12, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_02" type="Sprite2D" parent="Level/Background" unique_id=225493882]
z_index = -20
texture_filter = 1
position = Vector2(36, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_03" type="Sprite2D" parent="Level/Background" unique_id=646834403]
z_index = -20
texture_filter = 1
position = Vector2(60, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_04" type="Sprite2D" parent="Level/Background" unique_id=1012727532]
z_index = -20
texture_filter = 1
position = Vector2(84, 150)
texture = ExtResource("7_d5gix")
[node name="CloudBand_05" type="Sprite2D" parent="Level/Background" unique_id=1685434781]
z_index = -20
texture_filter = 1
position = Vector2(108, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_06" type="Sprite2D" parent="Level/Background" unique_id=1425986424]
z_index = -20
texture_filter = 1
position = Vector2(132, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_07" type="Sprite2D" parent="Level/Background" unique_id=1333305041]
z_index = -20
texture_filter = 1
position = Vector2(156, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_08" type="Sprite2D" parent="Level/Background" unique_id=55356860]
z_index = -20
texture_filter = 1
position = Vector2(180, 150)
texture = ExtResource("7_d5gix")
[node name="CloudBand_09" type="Sprite2D" parent="Level/Background" unique_id=379152848]
z_index = -20
texture_filter = 1
position = Vector2(204, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_10" type="Sprite2D" parent="Level/Background" unique_id=325990577]
z_index = -20
texture_filter = 1
position = Vector2(228, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_11" type="Sprite2D" parent="Level/Background" unique_id=1505438200]
z_index = -20
texture_filter = 1
position = Vector2(252, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_12" type="Sprite2D" parent="Level/Background" unique_id=190709163]
z_index = -20
texture_filter = 1
position = Vector2(276, 150)
texture = ExtResource("7_d5gix")
[node name="CloudBand_13" type="Sprite2D" parent="Level/Background" unique_id=349156114]
z_index = -20
texture_filter = 1
position = Vector2(300, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_14" type="Sprite2D" parent="Level/Background" unique_id=1838903106]
z_index = -20
texture_filter = 1
position = Vector2(324, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_15" type="Sprite2D" parent="Level/Background" unique_id=169474301]
z_index = -20
texture_filter = 1
position = Vector2(348, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_16" type="Sprite2D" parent="Level/Background" unique_id=156763289]
z_index = -20
texture_filter = 1
position = Vector2(372, 150)
texture = ExtResource("7_d5gix")
[node name="CloudBand_17" type="Sprite2D" parent="Level/Background" unique_id=161240142]
z_index = -20
texture_filter = 1
position = Vector2(396, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_18" type="Sprite2D" parent="Level/Background" unique_id=1823235224]
z_index = -20
texture_filter = 1
position = Vector2(420, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_19" type="Sprite2D" parent="Level/Background" unique_id=2092676382]
z_index = -20
texture_filter = 1
position = Vector2(444, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_20" type="Sprite2D" parent="Level/Background" unique_id=124375716]
z_index = -20
texture_filter = 1
position = Vector2(468, 150)
texture = ExtResource("7_d5gix")
[node name="TerrainTileMaps" type="Node2D" parent="Level" unique_id=652327492]
script = ExtResource("8_7hs6g")
[node name="TerrainBase" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=91115499]
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAD//wkAAAACAAEAAAD//woAAAAEAAAAAAABAA0AAAACAAYAAAACAA0AAAAEAAUAAAACAA4AAAAEAAUAAAADAA0AAAACAAYAAAABAAoAAAACAAYAAAAAAAoAAAAEAAUAAAAAAAkAAAACAAEAAAACAAsAAAAEAAUAAAAAAA0AAAAEAAUAAAD//w0AAAAEAAEAAAAFAA0AAAAEAAUAAAAEAA4AAAAEAAUAAAADAA4AAAACAAYAAAAAAA4AAAACAAYAAAABAA4AAAACAAYAAAAHAAwAAAACAAEAAAALAAwAAAACAAEAAAAKAA0AAAAEAAUAAAAIAA4AAAAEAAUAAAALAA4AAAAEAAUAAAAMAA4AAAACAAYAAAANAA0AAAACAAYAAAAOAA0AAAAEAAUAAAAPAA0AAAAEAAUAAAAQAA0AAAAEAAUAAAARAAwAAAACAAUAAAATAAwAAAACAAUAAAAVAAsAAAACAAYAAAAWAAsAAAAEAAUAAAAUAAwAAAACAAYAAAAQAAwAAAACAAMAAAAKAA4AAAAEAAUAAAAJAA4AAAAEAAUAAAAFAA4AAAACAAYAAAAGAA4AAAAEAAUAAAAHAA4AAAAEAAUAAAANAA4AAAACAAYAAAAOAA4AAAACAAYAAAAPAA4AAAACAAYAAAASAA0AAAACAAYAAAATAA0AAAAEAAUAAAAUAA0AAAACAAYAAAAVAA0AAAACAAYAAAAWAA0AAAACAAYAAAAXAA0AAAAEAAUAAAALAA0AAAAEAAUAAAAMAA0AAAACAAYAAAAPAAwAAAACAAMAAAAXAAsAAAAEAAUAAAAYAAsAAAAEAAUAAAAZAAsAAAACAAYAAAAZAAwAAAACAAYAAAAYAAwAAAAEAAUAAAAZAA4AAAAEAAUAAAAXAA4AAAAEAAUAAAAWAA4AAAAEAAUAAAAVAA4AAAACAAYAAAAUAA4AAAACAAYAAAATAA4AAAACAAYAAAASAA4AAAACAAYAAAAYAA4AAAACAAYAAAARAA4AAAACAAYAAAAEAA0AAAACAAYAAAD//w4AAAAEAAEAAAABAAkAAAACAAEAAAAIAAwAAAACAAEAAAAOAAwAAAACAAMAAAANAAwAAAACAAMAAAAMAAwAAAACAAMAAAAUAAkAAAABAAUAAAAUAAoAAAABAAYAAAAPAAgAAAASAAQAABAWAAoAAAACAAYAAAAXAAcAAAACAAUAAAAYAAkAAAACAAYAAAAYAAoAAAACAAYAAAAXAAoAAAACAAYAAAAXAAkAAAACAAYAAAAZAAkAAAACAAYAAAAZAAoAAAACAAYAAAAQAA4AAAAEAAUAAAARAA0AAAACAAYAAAASAAwAAAACAAUAAAAVAAoAAAACAAYAAAAWAAkAAAACAAUAAAAXAAgAAAAEAAEAAAAUAAsAAAAFAAEAAAAVAAwAAAAEAAUAAAAWAAwAAAACAAYAAAAXAAwAAAAEAAUAAAAYAA0AAAACAAYAAAAZAA0AAAAEAAUAAAAAAAwAAAACAAYAAAACAAwAAAACAAYAAAADAAwAAAAEAAUAAAAEAAwAAAACAAYAAAAFAAwAAAAEAAUAAAAGAAwAAAAEAAUAAAAGAA0AAAACAAYAAAAHAA0AAAACAAYAAAAIAA0AAAAEAAUAAAAJAA0AAAACAAYAAAAKAAwAAAACAAEAAAD//wwAAAAFAAAAAAD//wsAAAAEAAAAAAAAAAsAAAAEAAUAAAABAAsAAAACAAYAAAABAAwAAAACAAYAAAAYAAcAAAACAAUAAAAZAAcAAAADAAUAAAAYAAgAAAACAAYAAAAZAAgAAAACAAYAAAAVAAkAAAACAAUAAAADAAsAAAACAAYAAAAEAAsAAAACAAEAAAAFAAsAAAACAAEAAAAGAAsAAAACAAEAAAACAAoAAAACAAEAAAADAAoAAAACAAEAAAARAAcAAAAOAAcAAAASAAcAAAAOAAcAAAAQAAcAAAAOAAcAAAAPAAcAAAANAAcAAAAIAAYAAAANAAcAAAATAAcAAAAPAAcAAAAMAAYAAAAPAAcAAAALAAYAAAAOAAcAAAAKAAYAAAAOAAcAAAABAAgAAAARAAYAAAABAAcAAAAQAAUAAAABAAYAAAARAAUAAAABAAUAAAAQAAUAAAAAAAQAAAASAAIAAAABAAQAAAASAAIAAAACAAQAAAASAAIAAAD//wQAAAARAAIAAAAEAAQAAAATAAIAAAADAAQAAAASAAIAAAAAAAMAAAASAAEAAAABAAMAAAASAAEAAAACAAMAAAASAAEAAAADAAMAAAASAAAAAAAEAAMAAAATAAAAAAD//wMAAAARAAEAAAD//wIAAAARAAEAAAD//wEAAAARAAEAAAAAAAEAAAASAAEAAAAAAAIAAAASAAEAAAABAAEAAAASAAEAAAABAAIAAAASAAEAAAACAAEAAAASAAEAAAACAAIAAAASAAEAAAACAAYAAAATAAQAAAAJAAYAAAAOAAcAAAAJAAwAAAACAAEAAAAIAAMAAAALAAQAAAAHAAMAAAALAAQAAAAGAAMAAAALAAQAAAATAAQAAAALAAQAAAAUAAQAAAALAAQAAAAVAAQAAAALAAQAAAAWAAQAAAALAAQAAAAMAAkAAAALAAQAAAAOAAkAAAALAAQAAAA=")
tile_set = ExtResource("9_yeq8b")
[node name="TerrainDetailsA" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=1513560262]
z_index = 1
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAADAA0AAAADAAYAAAABAAwAAAABAAYAAAABAA0AAAABAAYAAAACAAsAAAACAAEAAAADAAsAAAADAAEAAAACAAwAAAACAAYAAAACAA4AAAACAAYAAAAXAAsAAAADAAUAAAAXAAwAAAADAAYAAAAXAA0AAAADAAYAAAAXAA4AAAADAAYAAAAWAAoAAAAEAAcAAAAWAAsAAAACAAUAAAAWAAwAAAAEAAUAAAAWAA0AAAACAAYAAAAWAA4AAAAEAAUAAAAVAAsAAAABAAUAAAAVAAwAAAABAAYAAAAVAA0AAAABAAYAAAAVAA4AAAABAAYAAAAQAA4AAAADAAYAAAAOAA4AAAAEAAUAAAAPAA4AAAACAAYAAAAQAA0AAAADAAMAAAANAA4AAAABAAYAAAAOAAwAAAAEAAYAAAAPAA0AAAACAAMAAAAOAA0AAAACAAMAAAANAA0AAAABAAMAAAAKAAMAAAARAAAAAAALAAMAAAASAAAAAAAMAAMAAAATAAAAAAAOAAIAAAARAAAAAAAPAAIAAAASAAAAAAADAAwAAAADAAYAAAAQAAIAAAATAAAAAAACAA0AAAAEAAUAAAABAA4AAAABAAYAAAADAA4AAAADAAYAAAABAAsAAAABAAEAAAAUAPz/AAASAAcAAAA=")
tile_set = ExtResource("9_yeq8b")
[node name="TerrainDetailsB" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=478648603]
z_index = 2
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAAGAAwAAAADAAEAAAAGAA0AAAADAAYAAAAFAA4AAAACAAYAAAAFAAsAAAAFAAYAAAAFAAwAAAACAAEAAAAFAA0AAAAEAAUAAAAGAA4AAAADAAYAAAADAAwAAAABAAEAAAADAA0AAAABAAYAAAADAA4AAAABAAYAAAAEAAwAAAACAAEAAAAEAA0AAAACAAYAAAAEAA4AAAAEAAUAAAA=")
tile_set = ExtResource("9_yeq8b")
[node name="ActorsAndCharacters" type="Node2D" parent="Level" unique_id=2036761714]
[node name="Player" parent="Level/ActorsAndCharacters" unique_id=1343528246 instance=ExtResource("10_invaa")]
z_index = 10
position = Vector2(144, 215)
rotation = -3.1415927
scale = Vector2(1, -1)
[node name="PrefabInstances" type="Node2D" parent="Level" unique_id=2013556771]
[node name="Structures" type="Node2D" parent="Level/PrefabInstances" unique_id=1811607442]
[node name="Hazards" type="Node2D" parent="Level/PrefabInstances" unique_id=888595377]
[node name="Spikes_01" parent="Level/PrefabInstances/Hazards" unique_id=1025977450 instance=ExtResource("11_88b2t")]
z_index = 5
position = Vector2(270, 216)
[node name="Spikes_02" parent="Level/PrefabInstances/Hazards" unique_id=1262276584 instance=ExtResource("11_88b2t")]
z_index = 5
position = Vector2(335, 127)
[node name="SpikesGrey_01" parent="Level/PrefabInstances/Hazards" unique_id=214833071 instance=ExtResource("12_0ydwj")]
z_index = 5
position = Vector2(109, 199)
[node name="SpikesGrey_02" parent="Level/PrefabInstances/Hazards" unique_id=458791480 instance=ExtResource("12_0ydwj")]
z_index = 5
position = Vector2(331, 216)
[node name="Enemies" type="Node2D" parent="Level/PrefabInstances" unique_id=93211253]
[node name="BulletRain_01" parent="Level/PrefabInstances/Enemies" instance=ExtResource("20_rain")]
position = Vector2(234, 0)
[node name="Interactables" type="Node2D" parent="Level/PrefabInstances" unique_id=769532488]
[node name="SpringRed_01" parent="Level/PrefabInstances/Interactables" unique_id=34192139 instance=ExtResource("15_usk20")]
visible = false
z_index = 5
position = Vector2(448, 125)
[node name="Door_01" parent="Level/PrefabInstances/Interactables" unique_id=1264725697 instance=ExtResource("16_y8u0o")]
z_index = 5
position = Vector2(288, 37)
[node name="Collectibles" type="Node2D" parent="Level/PrefabInstances" unique_id=2086540192]
[node name="CoinGoldRound_01" parent="Level/PrefabInstances/Collectibles" unique_id=926105584 instance=ExtResource("17_16ffm")]
z_index = 5
position = Vector2(447, 130)
[node name="CoinGoldRound_02" parent="Level/PrefabInstances/Collectibles" unique_id=1674416025 instance=ExtResource("17_16ffm")]
z_index = 5
position = Vector2(67, 184)
[node name="CoinGoldRound_03" parent="Level/PrefabInstances/Collectibles" unique_id=1087706364 instance=ExtResource("17_16ffm")]
z_index = 5
position = Vector2(182, 109)
[node name="KeyGold_01" parent="Level/PrefabInstances/Collectibles" unique_id=2060959680 instance=ExtResource("18_kvrlk")]
z_index = 5
position = Vector2(145, 220)
[node name="Diamond_01" parent="Level/PrefabInstances/Collectibles" unique_id=909237898 instance=ExtResource("19_85fmh")]
z_index = 5
position = Vector2(399, 161)
[node name="Diamond_02" parent="Level/PrefabInstances/Collectibles" unique_id=234727210 instance=ExtResource("19_85fmh")]
z_index = 5
position = Vector2(204, 217)
[node name="Diamond_03" parent="Level/PrefabInstances/Collectibles" unique_id=1261326529 instance=ExtResource("19_85fmh")]
z_index = 5
position = Vector2(288, 125)
[node name="Signs" type="Node2D" parent="Level/PrefabInstances" unique_id=113197076]
+1 -1
View File
@@ -17,6 +17,6 @@ texture_filter = 1
shape = SubResource("Shape_bullet")
[node name="Probe" type="RayCast2D" parent="."]
collision_mask = 1
collision_mask = 3
hit_from_inside = true
target_position = Vector2(-12, 0)
+6
View File
@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/bullet_rain.gd" id="1_rain"]
[node name="BulletRain" type="Node2D"]
script = ExtResource("1_rain")
+64
View File
@@ -0,0 +1,64 @@
extends Node2D
## 子弹雨:从天上随机横坐标往下落子弹,密度可以调得很疏。
##
## 落下来的子弹撞到地形、或撞到你铺的「绳子/木板桥」就会消失 ——
## 绳子的碰撞放在 TileSet 的第 2 物理层,只有子弹的探针(mask = 3)会检测它,
## 玩家(mask = 1)和钥匙(跟着玩家的 mask)都不受影响,能直接穿过去。
const BULLET := preload("res://scenes/prefabs/enemies/bullet.tscn")
@export var enabled: bool = true
## 平均多少秒落一颗(数值越大越稀疏)
@export var interval: float = 1.0
## 间隔随机浮动范围(0 = 完全固定节奏)
@export var interval_jitter: float = 0.6
## 开场后第一次落下的延迟
@export var start_delay: float = 1.5
## 生成高度(相对自己的 y,负数 = 从屏幕上方外面)
@export var spawn_y: float = -30.0
## 水平撒落范围:以自己的 x 为中心,左右各一半
@export var span: float = 468.0
## 下落速度
@export var fall_speed: float = 70.0
@export var damage: int = 1
@export var bullet_color: Color = Color(0.55, 0.8, 1.0)
## 固定随机种子(0 = 每次运行都不同)
@export var random_seed: int = 0
var _timer: float = 0.0
var _rng := RandomNumberGenerator.new()
var dropped: int = 0
func _ready() -> void:
add_to_group("bullet_rain")
if random_seed != 0:
_rng.seed = random_seed
else:
_rng.randomize()
_timer = start_delay
func _physics_process(delta: float) -> void:
if not enabled:
return
_timer -= delta
if _timer > 0.0:
return
_timer = maxf(interval + _rng.randf_range(-interval_jitter, interval_jitter), 0.05)
drop()
func drop() -> void:
var bullet := BULLET.instantiate()
# 这几个属性必须在 add_child 之前设好
bullet.bullet_color = bullet_color
bullet.speed = fall_speed
bullet.damage = damage
get_parent().add_child(bullet)
bullet.global_position = Vector2(
global_position.x + _rng.randf_range(-span * 0.5, span * 0.5),
global_position.y + spawn_y)
bullet.launch(Vector2(0.0, 1.0), fall_speed)
dropped += 1
+1
View File
@@ -0,0 +1 @@
uid://dbpe6r76gao3w