Connexion
FR
games/satisfying-bounce / src / Scene / GapCircleScene.php · commit 8bdd6e4
<?phpnamespace App\Scene;use App\Audio\Scale;use JumPHP\Physics2d\Ball;use JumPHP\Physics2d\Ring;use JumPHP\Renderer\Renderer;/** * A ball bounces inside a circle that is closed on three quarters: a quarter of it is open and turns round. Each bounce * plays the next note and gives it a little energy; when it finds the exit a new ball starts and the opening is a little * narrower, the circle a little faster (tension up, round after round). */final class GapCircleScene extends VideoScene{    public const SECONDS = 26.0;    public const GAP = M_PI / 2;    public const MIN_GAP = 0.55;    protected float $punch = 0.025;    private float $gap = self::GAP;    private int $escapes = 0;    protected function melodyNotes(): string    {        // Ode to Joy (Beethoven, public domain)        return 'E4 E4 F4 G4 G4 F4 E4 D4 C4 C4 D4 E4 E4 D4 D4 E4 E4 F4 G4 G4 F4 E4 D4 C4 C4 D4 E4 D4 C4 C4';    }    protected function headingText(): string    {        return 'CAN IT FIND THE EXIT?';    }    protected function hookText(): string    {        return 'WAIT FOR THE ESCAPE';    }    protected function captionText(): string    {        return $this->t('BOUNCES {n}   ESCAPES {m}', ['n' => $this->bounces, 'm' => $this->escapes]);    }    public function enter(array $params = []): void    {        $this->gap = self::GAP;        $this->escapes = 0;        parent::enter($params);    }    protected function build(): void    {        $this->world->reset($this->seed + $this->round * 7919);        $this->world->gravity = 420;        $this->world->restitution = 1.045;       // each bounce gives a little energy: it climbs until it finds the exit        $rand = $this->world->random;        $this->gap = $this->round === 0 ? self::GAP : max(self::MIN_GAP, $this->gap * 0.85);        $this->world->addRing(new Ring(self::CX, self::CY, 112, gap: $this->gap, angle: -M_PI / 2,            spin: min(2.4, 0.9 + 0.15 * $this->round) * ($this->round % 2 ? -1 : 1), thickness: 4, color: self::hue($this->round * 0.13)));        $this->world->addBall(new Ball(self::CX + $rand->between(-20, 20), self::CY + 60, $rand->between(-110, 110), 0, 7, '#ff8fc0', 120));    }    protected function onHit(array $hit): void    {        /** @var Ball $ball */        $ball = $hit['ball'];        if ($hit['type'] === 'escape') {            $this->escapes++;            $this->banner = $this->t('FOUND THE EXIT!');            $this->roundEnd = $this->time + 1.7;            $this->burst($ball->x, $ball->y, '#ffe27a', 34, 130);            foreach ([0, 2, 4] as $k) {                $this->recorder->note($this->time + $k * 0.07, Scale::freq(Scale::MAJOR_PENTA, 10 + $k), 1.0, 'bell', 0.2);            }            return;        }        $this->bounces++;        /** @var Ring $ring */        $ring = $hit['body'];        $ring->data['flash'] = 1.0;        $ring->color = self::hue($this->round * 0.13 + $ring->hits * 0.02);        $ball->color = self::hue($ring->hits * 0.02 + 0.55);        $this->squash($ball, (float)$hit['x'], (float)$hit['y']);        $this->burst((float)$hit['x'], (float)$hit['y'], $ring->color, min(14, 4 + (int)($hit['speed'] / 70)), 80);        $this->note();    }    protected function drawWorld(Renderer $r): void    {        foreach ($this->world->rings() as $ring) {            $this->drawRing($r, $ring);        }        foreach ($this->world->balls() as $ball) {            $this->drawBall($r, $ball);        }    }}