教程 3 — 平台跳跃游戏:关卡、碰撞、摄像机
用文本行写成的关卡
起步时,关卡可以直接写在代码里:每个字符代表一块 16 × 16 像素的图块。
private const LEVEL = [
'..........................C.C.C.........',
'.........................=======........',
'...P....................................',
'#########............C......######......',
'#########..........#####...########.....',
'########################################',
];
$this->map = ArrayLoader::fromRows(self::LEVEL,
legend: ['#' => 1, '=' => 2, '/' => 3, '\\' => 4], // 图块
objects: ['P' => 'start', 'C' => 'coin'], // 放置在关卡中的物体
tileWidth: 16, tileset: 'generated:tiles');
$tileset = $this->map->tilesets[0];
$tileset->masks[1] = CollisionMask::oneWay(); // '=':可以从下方跳穿过去
$tileset->masks[2] = CollisionMask::slope(16, 16, 1, 16); // '/':一个斜坡
当游戏变大后,请用 Tiled 来绘制关卡:
TiledJsonLoader::load($this->engine->config->path('assets/maps/niveau1.tmj'))。
碰撞
在场景的构造函数中注入各项服务(它们会被自动提供):
public function __construct(
private TileMapRenderer $tiles,
private CollisionSystem $collisions,
private EntityManager $entities,
) {
}
每一帧,先设置速度,再通过碰撞系统移动:它会逐像素前进,在墙前停下,落在地面上,爬上斜坡,并填充 $player->onGround。
$this->player->control($move, $jump, $held, $dt);
$this->player->update($dt);
$this->collisions->move($this->player, $this->map, $dt);
掉出关卡:$this->collisions->on('fell', fn () => $this->respawn());(写在 enter() 中,位于
$this->collisions->removeAllListeners() 之后)。
金币
关卡中的物体会变成实体:
$this->entities->clear();
$this->entities->register('coin', Coin::class);
$this->entities->spawnFromMap($this->map);
// 在 update() 中:
$this->entities->update($dt, $this->camera);
foreach ($this->entities->overlapping($this->player->getBoundingBox()) as $entity) {
if ($entity instanceof Coin) {
$entity->kill(); // 自动移除
$this->coins++;
$this->sound()->playSfx('coin');
}
}
摄像机
$this->camera = new Camera($this->width(), $this->height());
$this->camera->setBounds(0, 0, $this->map->pixelWidth(), $this->map->pixelHeight());
$this->camera->follow($this->player, 0.15, 0.3); // 屏幕中央有一块“死区”
// update():$this->camera->update($dt);
// render():所有内容都按摄像机的偏移来绘制
$cx = $this->camera->getX();
$cy = $this->camera->getY();
$this->tiles->renderBackground($r, $this->map, $cx, $cy, $this->time);
$this->entities->render($r, $this->camera);
$this->player->render($r, $cx, $cy);
$this->tiles->renderForeground($r, $this->map, $cx, $cy, $this->time);
$this->camera->shake(4, 0.3) 可以让屏幕抖动。完整的游戏就是 JumPHP 的骨架项目
(src/Scene/PlayScene.php):bin/jumphp new mon-jeu 会把它交给你,可以直接修改。