チュートリアル2 — 動いてアニメーションするキャラクター
動くものはエンティティ(src/Entity/)で、シーンがnewで作ります。エンティティは、位置($this->x、$this->y)、速度(vx、vy)、サイズ、向き(facing)、状態(state)を持ちます。トレイトを使うと機能を追加できます:Sprite(アニメーション)、Gravity(落下)、TileCollision(地面に触れているかの判定)。
PHPで描くスプライト
画像は不要です。createTextureFromPixels()が、文字の並びからテクスチャを作ります。1文字が1ピクセルで、.は透明です。
private const SHEET = [ // 12 × 14の画像が3枚:停止、歩行、ジャンプ
'...####........####........####.....',
'..######......######......######....',
'..##o#o#......##o#o#......##o#o#....',
// ...
];
public function __construct(Sdl $sdl, float $x, float $y)
{
$this->x = $x;
$this->y = $y;
$this->width = 10;
$this->height = 14;
if (!$sdl->hasTexture('player')) {
$sdl->createTextureFromPixels('player', self::SHEET, ['#' => '#ffd23f', 'o' => '#202020']);
}
$this->initSprite($sdl, 'player', 'idleRight');
}
本物のスプライトシートを使う場合:$sdl->loadTexture('player', 'assets/sprites/player.png')。
アニメーション
アニメーションは、スプライトシートの中の画像のリストです。エンジンは<状態><向き>の名前のアニメーションを選びます:walkRight、idleLeftなど。reverseWalkRightは、反転した画像を再利用します。
protected array $animations = [
'idleRight' => ['coords' => [['x' => 0, 'y' => 0, 'w' => 12, 'h' => 14]]],
'walkRight' => ['coords' => [['x' => 12, 'y' => 0, 'w' => 12, 'h' => 14], ['x' => 0, 'y' => 0, 'w' => 12, 'h' => 14]], 'duration' => 0.12],
'jumpRight' => ['coords' => [['x' => 24, 'y' => 0, 'w' => 12, 'h' => 14]]],
'idleLeft' => 'reverseIdleRight',
'walkLeft' => 'reverseWalkRight',
'jumpLeft' => 'reverseJumpRight',
];
勢いのある動き
use Sprite, Gravity, TileCollision {
Sprite::update as updateSprite;
Gravity::update as updateGravity;
}
public function control(int $move, bool $jumpPressed, bool $jumpHeld, float $dt): void
{
// 目標の速度に向かってなめらかに加速します
$this->vx = Math::approach($this->vx, $move * 110, 900 * $dt);
if ($move !== 0) {
$this->facing = $move < 0 ? 'left' : 'right';
}
if ($jumpPressed && $this->onGround) {
$this->vy = -290;
}
if (!$jumpHeld && $this->vy < -100) {
$this->vy = -100; // ボタンを早く離すと、ジャンプが低くなります
}
}
public function update(float $dt): void
{
$this->updateGravity($dt);
$this->state = !$this->onGround ? 'jump' : (abs($this->vx) > 10 ? 'walk' : 'idle');
$this->pickAnimation(); // "<state><Facing>"を選びます
$this->updateSprite($dt); // 次の画像へ
}
public function render(Renderer $r, float $camX = 0, float $camY = 0): void
{
$this->drawAt($r, $this->x - 1 - $camX, $this->y - $camY);
}
シーンでは、$player->control($this->actions()->axis('left', 'right'), $this->actions()->isPressed('jump'), $this->actions()->isHeld('jump'), $dt)を呼んでから、$player->update($dt)を呼びます。壁や地面との衝突の処理は、衝突判定システムの役目です:チュートリアル3をご覧ください。
このコードは、JumPHPのスケルトン(bin/jumphp new)のsrc/Entity/Player.phpのものです。