sijcdu 8127c787f8 Bullet rain is denser and its bullets are no longer stopped by anything
Interval dropped from 1.0s +- 0.6 to 0.4s +- 0.25 (about 2.5 bullets per second instead of 1), which measured 13 drops in 6 seconds versus 6 before. bullet.gd gained stop_on_obstacle (default false): the probe raycast is disabled, so rain bullets fall straight through terrain and ropes and are only recycled by their lifetime or by dropping past despawn_y. Verified a bullet falling from y=190 goes through the floor at y=225 and despawns at y=340. Also picks up the editor changes: the rope tiles were removed from level 3 and a few nodes gained unique ids.
2026-10-07 23:01:00 +08:00

平台闯关 · Pixel Platformer

一个用 Godot 4.7 制作的 2D 像素风平台跳跃游戏。


运行方式

  1. 用 Godot 4.7 打开本目录(选择 project.godot)
  2. 按 F5 运行

主场景:scenes/main.tscn


操作

按键 作用
A / D 左右移动
空格 跳跃(单段,空中不能二段跳)
S 按住 显示投掷抛物线预览
S 松开 投出钥匙
W 传送到扔出去的钥匙位置,并把它捡回来
Z 消耗一个回血道具:回一颗心 + 5 秒无敌盾
F 切换飞行(仅编辑器 / 调试版可用,见下方说明)
R / Enter 死亡后重新开始

玩法

  • 生命值:共 3 颗心。碰到尖刺扣 1 颗心并被打回出生点,生命归零后进入死亡界面。
  • 收集物:地图上有 3 个金色小球,走过去自动拾取,变成"回血道具"。
  • 回血:按 Z 消耗一个道具,回一颗心并获得 5 秒无敌盾。
  • 钥匙:走过去自动捡起;按住 S 时实时显示抛物线轨迹和落点;松开投出。按 W 可以直接瞬移到钥匙处并收回它。
  • 弹簧:踩上去弹起约 57px(普通跳跃约 20px)。
  • 隐藏通道:关卡里有几处"看着是石壁、其实能直接穿过去"的暗道。

对应作业要求

① 玩家移动控制代码

scripts/player_controller.gd

  • 节点类型 CharacterBody2D,每帧结束调用 move_and_slide()
  • 重力:get_gravity(),只在 not is_on_floor() 时累加到 velocity.y
  • 水平移动:Input.get_axis("move_left", "move_right") 取方向,用 move_toward() 做加速 / 减速插值,手感比直接赋值柔和
  • 跳跃:Input.is_action_just_pressed("jump") 配合 is_on_floor(),因此只有踩在地面上才能起跳,天然不会二段跳
  • 朝向:根据 velocity.x 的正负翻转 Sprite2D.flip_h

② 瓦片地图

  • 用了 3 层 TileMapLayer:
    • TerrainBase — 主体地形
    • TerrainDetailsA / TerrainDetailsB — 前景装饰(z_index 1 / 2)
  • TileSet 使用 18×18 的图集(assets/game/tilesets/),逐个图块配置了物理碰撞多边形;草地单向平台用 one_way 实现
  • 隐藏通道的做法:在地形层上用 erase_cell() 把指定格子挖空,再在那个位置盖一张完全没有碰撞的 Sprite2D(贴图借用旁边墙块),于是画面上仍然是完整石壁、人却能穿过去

③ 碰撞体的区别与使用场景

项目里四种碰撞方式都用到了:

类型 用在哪 为什么选它
CharacterBody2D 玩家 需要主动移动、被地形阻挡、能踩在地面和箱子上
StaticBody2D 木箱、弹簧、树干、路牌、传送箱 不动的实体障碍,玩家能站上去、撞得到
Area2D 金币、尖刺、弹簧触发区、钥匙 只需要"碰到就触发",不产生物理推挤,所以能穿过去
TileMapLayer 物理层 大面积地面 重复地形太多,用瓦片自带碰撞最省事,不用摆几百个 CollisionShape2D

碰撞层约定:

层号 用途
1 世界实体(地形、木箱、树干、路牌、弹簧、传送箱)
2 玩家
4 危险物(尖刺)
8 交互触发区(弹簧、传送箱的检测区)
16 收集物(金币、宝石、钥匙)

玩家的 collision_mask 只有 layer 1,所以只和"世界实体"发生物理碰撞,不会被金币、尖刺挡住。

④ 角色技能

技能 A —— 投掷钥匙 + 传送(scripts/key.gd)

  • 走过去自动捡起钥匙
  • 按住 S:用与玩家完全相同的碰撞掩码做抛物线模拟,实时画出轨迹和落点标记
  • 松开 S:把钥匙按同样的初速度扔出去
  • 按 W:瞬移到钥匙所在位置并收回它
  • 因为预测和实际飞行调用的是同一个 simulate_trajectory() 函数,"弧线显示能落到的地方"就等于"钥匙真正落到的地方"(实测偏差 0.000 px)

技能 B —— 飞行(player_controller.gd)

  • 按 F 切换飞行状态,W / S 控制升降,A / D 平移
  • 为了不影响正式玩法的难度平衡,只在编辑器 / 调试版生效(OS.is_debug_build()),导出正式版按键无效
  • 如果想在导出版里也开放,把 fly_available() 里的 OS.is_debug_build() 去掉即可:
    func fly_available() -> bool:
        return debug_fly
    

目录结构

├─ project.godot            引擎配置(输入映射、窗口、渲染)
├─ scripts/
│   ├─ player_controller.gd 玩家:移动、跳跃、生命值、投掷、飞行
│   ├─ key.gd               钥匙:拾取 / 飞行 / 落地、轨迹模拟
│   ├─ spring.gd            弹簧:踩上去弹起
│   ├─ hazard.gd            危险物:碰到扣血
│   ├─ collectible.gd       收集物:走过去拾取
│   ├─ hidden_passages.gd   隐藏通道:挖空地砖 + 盖假墙
│   ├─ hud.gd               左上角:心 + 道具数 + 护盾倒计时
│   └─ death_screen.gd      死亡界面:渐黑 + 重新开始
├─ scenes/
│   ├─ main.tscn            主场景(关卡)
│   ├─ player.tscn          玩家
│   ├─ hud.tscn             状态显示
│   ├─ death_screen.tscn    死亡界面
│   └─ prefabs/             预制件:金币、钥匙、弹簧、尖刺、木箱、树、路牌
└─ assets/
    ├─ game/                实际使用的素材
    └─ kenney/              Kenney 原始素材库

素材来源

  • 图形素材:Kenney — Pixel Platformer (1.2)
  • 授权:CC0(可商用、可修改,无需署名)
  • 来源:https://kenney.nl
  • 详见 LICENSE_KENNEY.txt

版本控制

本项目使用 git 进行版本管理。

S
Description
No description provided
Readme 227 KiB
Languages
GDScript 100%