ZH

pipobimbo

作者:matSchluthfeld · 平台跳跃
暂无评分
▶ 开玩 💬 反馈 源码 🕘 版本 编辑 GitHub
games/pipobimbo / src / Scene / PlayScene.php · commit ce63150
<?phpnamespace App\Scene;use App\Entity\Coin;use App\Entity\Player;use JumPHP\Audio\Synth;use JumPHP\Entity\EntityManager;use JumPHP\Physics\CollisionSystem;use JumPHP\Renderer\Renderer;use JumPHP\Scene\AbstractScene;use JumPHP\Scene\Camera;use JumPHP\Tile\ArrayLoader;use JumPHP\Tile\CollisionMask;use JumPHP\Tile\TileMap;use JumPHP\Tile\TileMapRenderer;/** * A small level: tiles with slopes and a jump-through platform, a player, coins, a camera. * Replace the rows by a Tiled map when the game grows: TiledJsonLoader::load($this->engine->config->path('assets/maps/level1.tmj')). */final class PlayScene extends AbstractScene{    private const LEVEL = [        '..................................................................',        '..................................................................',        '..........................C.C.C...................................',        '.........................=======...............C.C................',        '..........C.C.........................................######......',        '........=======..........................C.........##.........C..',        '.....................................====.............../...######',        '...P...........................C.....................//#..........',        '#########............C......######...............///####..........',        '#########..........#####...########.........C..////#####...C......',        '#########\\.......#######../########\\......########.#####..####...',        '##########\\.....########./#########\\.....#########.#####..####...',        '###########################################################.....##',    ];    private TileMap $map;    private Player $player;    private Camera $camera;    private int $coins = 0;    private bool $demo = false;    private float $time = 0.0;    public function __construct(        private TileMapRenderer $tiles,        private CollisionSystem $collisions,        private EntityManager $entities,    ) {    }    public function enter(array $params = []): void    {        $this->demo = !empty($params['demo']);        $this->coins = 0;        $this->time = 0.0;        // '#' solid, '=' jump-through, '/' and '\' slopes; 'P' start, 'C' coins        $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);        $tileset->masks[3] = CollisionMask::slope(16, 16, 16, 1);        $this->makeTileset();        $this->entities->clear();        $this->entities->register('coin', Coin::class);        $this->entities->spawnFromMap($this->map);        $start = $this->map->object('start');        $this->player = new Player($this->sdl(), $start['x'], $start['y']);        $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);        $this->collisions->removeAllListeners();        $this->collisions->on('fell', fn () => $this->respawn());        if (!$this->sound()->hasSfx('jump')) {            $this->sound()->synth('jump', Synth::sweep(250, 600, 0.12));            $this->sound()->synth('coin', Synth::sequence([[988, 0.05], [1319, 0.12]]));            $this->sound()->synth('fall', Synth::sweep(500, 80, 0.4, 'triangle'));        }    }    public function update(float $dt): void    {        $this->time += $dt;        if ($this->actions()->isPressed('pause')) {            $this->scenes()->push(PauseScene::class);            return;        }        if ($this->demo) {            // headless test (game.yaml smoke): a bot runs right and jumps            $move = 1;            $jump = fmod($this->time, 1.2) < 0.05;            $held = fmod($this->time, 1.2) < 0.4;        } else {            $move = $this->actions()->axis('left', 'right');            $jump = $this->actions()->isPressed('jump');            $held = $this->actions()->isHeld('jump');        }        if ($jump && $this->player->onGround) {            $this->sound()->playSfx('jump');        }        $this->player->control($move, $jump, $held, $dt);        $this->player->update($dt);        $this->collisions->move($this->player, $this->map, $dt);        $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->update($dt);    }    private function respawn(): void    {        $this->sound()->playSfx('fall');        $start = $this->map->object('start');        $this->player->setXY($start['x'], $start['y']);        $this->player->setVelocity(0, 0);        $this->camera->centerOn($start['x'], $start['y']);    }    public function render(Renderer $r): void    {        $r->clear([92, 148, 252]);        $cx = $this->camera->getX();        $cy = $this->camera->getY();        // far hills (parallax: they move at a quarter of the camera speed)        for ($i = -1; $i < 8; $i++) {            $x = $i * 80 - fmod($cx * 0.25, 80);            $r->fillCircle($x + 40, $this->height() + 10, 50, [70, 120, 200]);        }        $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);        $font = $this->font();        $font->draw($r, \JumPHP\Util\Lang::t('COINS') . ' ' . $this->coins, 6, 6, 1, [255, 255, 255], 'left', [0, 0, 0]);    }    /** The 4 tiles of the level drawn with GD (a real game loads a PNG: TiledJsonLoader / TileSet image). */    private function makeTileset(): void    {        if ($this->sdl()->hasTexture('tileset:generated:tiles')) {            return;        }        $img = imagecreatetruecolor(64, 16);        imagesavealpha($img, true);        imagealphablending($img, false);        imagefill($img, 0, 0, imagecolorallocatealpha($img, 0, 0, 0, 127));        $dirt = imagecolorallocate($img, 150, 90, 50);        $grass = imagecolorallocate($img, 70, 190, 70);        $wood = imagecolorallocate($img, 200, 140, 70);        imagefilledrectangle($img, 0, 0, 15, 15, $dirt);                     // 1: block        imagefilledrectangle($img, 0, 0, 15, 2, $grass);        imagefilledrectangle($img, 16, 0, 31, 4, $wood);                     // 2: jump-through plank        imagefilledpolygon($img, [32, 15, 47, 0, 47, 15], $dirt);            // 3: slope up        imageline($img, 32, 15, 47, 0, $grass);        imagefilledpolygon($img, [48, 0, 63, 15, 48, 15], $dirt);            // 4: slope down        imageline($img, 48, 0, 63, 15, $grass);        $this->sdl()->createTextureFromGd('tileset:generated:tiles', $img);    }}