Show / Hide Table of Contents

アニメーション

プレイヤーは移動できるようになりましたが、まだアニメーションが実装されていないため、地面を滑るようにしか移動できません。

ここでは、以前に作成したスプライトフレームを切り替えて表示する画像を変化させる、パターンアニメーションを実装します。

実装する動作

以前、スプライトフレームは次のように分割しました。

これらは次のような動作に対応します。

フレーム 動作
0 待機
2, 3 歩行
4 ジャンプ
5 下降

歩行と待機

まずはどのアニメーションを表示すればよいか、現在の状態を判断する必要があります。 今回は、歩行中と待機中のどちらであるかは X 方向の速度によって決めることにします。

if (onGround) {
    if (velocity.x < 0.0) {
        // 左へ移動中
        playerSprite->setFrameIndex(2 + std::fmod(Engine::totalTime(), 0.4) / 0.2);
        playerSprite->setFlippedX(false);
    }
    else if (velocity.x > 0.0) {
        // 右へ移動中
        playerSprite->setFrameIndex(2 + std::fmod(Engine::totalTime(), 0.4) / 0.2);
        playerSprite->setFlippedX(true);
    }
    else {
        // 待機中
        playerSprite->setFrameIndex(0);
    }
}

経過時間と FrameIndex の計算

Engine::totalTime() は、ゲーム起動からの経過時間を秒単位の小数値で返します。これを利用して、0.2 秒間隔で FrameIndex を 2, 3 交互に切り替えて歩行アニメーションを表現します。

※ std::fmod は小数の剰余算を行う、C++ 標準ライブラリの関数です。

左右反転

スプライトは setFlippedX() によって、表示するテクスチャを左右反転することができます。

今回は見た目上の反転だけで充分であるため、このメソッドを使用しています。

左右反転は他に、setScale() というメソッドを使用して X 方向の拡大率を -1.0 にする、という方法があります。 今回のチュートリアルでは触れませんが、これは例えばキャラクターと武器のスプライトが分かれていて、これらが親子関係でリンクされている場合に有効です。

ジャンプと下降

ジャンプと下降の状態判断は、Y 方向の速度によって決めます。 また、これらは空中にいる場合のみ適用するべきですので、先ほどの「歩行と待機」で追加した "if (onGround)" の else 側にコードを追加します。

if (onGround) {
    // ... 歩行と待機のアニメーション処理
}
else {
    if (velocity.y > 0.0) {
        // 上昇中
        playerSprite->setFrameIndex(4);
    }
    else {
        // 下降中
        playerSprite->setFrameIndex(5);
    }
}

今回は空中では時間経過によるアニメーションは用意されていないため、FrameIndex を切り替えるだけです。

コード全体

#include <Lumino.hpp>

static const uint32_t GroundGroup = 0x01;   // 地面の衝突グループ
static const uint32_t PlayerGroup = 0x02;   // プレイヤーの衝突グループ

void Main()
{
    // ワールドの 16x12 の範囲を表示する
    Camera* camera = Engine::mainCamera();
    camera->setProjectionMode(ProjectionMode::Orthographic);
    camera->setOrthographicSize(16, 12);

    // 空
    Ref<Texture> skyTexture = Assets::loadTexture(u"sky");
    Ref<Sprite> skySprite = Sprite::create(skyTexture, 16, 12);

    // 地面
    Ref<Texture> mapTexture = Assets::loadTexture(u"map");
    Ref<Sprite> groundSprite = Sprite::create(mapTexture, 16, 1);
    groundSprite->setSourceRect(0, 0, 80, 8);
    groundSprite->setPosition(0, -5.5, 0);

    // 地面の剛体
    Ref<RigidBody2DComponent> groundBody = RigidBody2DComponent::create();
    groundBody->addCollisionShape(BoxCollisionShape2D::create(16, 1));
    groundBody->setCollisionGroup(GroundGroup);
    groundSprite->addComponent(groundBody);

    // 左の床
    Ref<Sprite> floorSprite1 = Sprite::create(mapTexture, 4, 1);
    floorSprite1->setSourceRect(0, 8, 32, 8);
    floorSprite1->setPosition(-4.5, -2, 0);

    // 左の床の剛体
    Ref<RigidBody2DComponent> floorBody1 = RigidBody2DComponent::create();
    floorBody1->addCollisionShape(BoxCollisionShape2D::create(4, 1));
    floorBody1->setCollisionGroup(GroundGroup);
    floorSprite1->addComponent(floorBody1);

    // 右の床
    Ref<Sprite> floorSprite2 = Sprite::create(mapTexture, 4, 1);
    floorSprite2->setSourceRect(0, 8, 32, 8);
    floorSprite2->setPosition(4.5, 0, 0);

    // 右の床の剛体
    Ref<RigidBody2DComponent> floorBody2 = RigidBody2DComponent::create();
    floorBody2->addCollisionShape(BoxCollisionShape2D::create(4, 1));
    floorBody2->setCollisionGroup(GroundGroup);
    floorSprite2->addComponent(floorBody2);

    // プレイヤー
    Ref<Texture> playerTexture = Assets::loadTexture(u"player");
    Ref<SpriteFrameSet> frameSet = SpriteFrameSet::create(playerTexture, 16, 16);
    Ref<Sprite> playerSprite = Sprite::create(frameSet);
    playerSprite->setFrameIndex(0);

    // プレイヤーの剛体
    Ref<RigidBody2DComponent> playerBody = RigidBody2DComponent::create();
    playerBody->addCollisionShape(BoxCollisionShape2D::create(1, 1));
    playerBody->setMass(2);
    playerBody->setFixedRotation(true);
    playerBody->setCollisionGroup(PlayerGroup);
    playerSprite->addComponent(playerBody);

    // 画面外への移動を制限するための剛体
    Ref<EdgeCollisionShape2D> wallShape = EdgeCollisionShape2D::create();
    wallShape->addPoint(-8, -6);
    wallShape->addPoint(8, -6);
    wallShape->addPoint(8, 6);
    wallShape->addPoint(-8, 6);
    Ref<RigidBody2DComponent> wallBody = RigidBody2DComponent::create();
    wallBody->addCollisionShape(wallShape);
    skySprite->addComponent(wallBody);

    // メインループ
    while (Engine::update())
    {
        Vector2 velocity = playerBody->velocity();

        // プレイヤーの移動処理
        if (Input::isPressed(u"right")) {
            velocity.x = 5.0f;
        }
        else if (Input::isPressed(u"left")) {
            velocity.x = -5.0f;
        }
        else {
            velocity.x = 0.0f;
        }

        // プレイヤーのジャンプ処理
        bool onGround = Physics2D::raycast(playerSprite->position(), -Vector2::UnitY, 0.6, GroundGroup);
        if (onGround) {
            if (Input::isPressed(u"submit")) {
                velocity.y = 10.0;
            }
        }

        // プレイヤーのアニメーション処理
        if (onGround) {
            if (velocity.x < 0.0) {
                // 左へ移動中
                playerSprite->setFrameIndex(2 + std::fmod(Engine::totalTime(), 0.4) / 0.2);
                playerSprite->setFlippedX(false);
            }
            else if (velocity.x > 0.0) {
                // 右へ移動中
                playerSprite->setFrameIndex(2 + std::fmod(Engine::totalTime(), 0.4) / 0.2);
                playerSprite->setFlippedX(true);
            }
            else {
                // 待機中
                playerSprite->setFrameIndex(0);
            }
        }
        else {
            if (velocity.y > 0.0) {
                // 上昇中
                playerSprite->setFrameIndex(4);
            }
            else {
                // 下降中
                playerSprite->setFrameIndex(5);
            }
        }

        playerBody->setVelocity(velocity);
    }
}
In This Article
Back to top Copyright © 2017-2021 lriki