Android Game Develop C++ #002

AndroidStudioでC++でゲームを作るシリーズ第2回です。

今回はシーン切り替えについてやっていきます。

こちらの画像が、画面をタップすることにより

このような画像に切り替わるプログラムです。

シーン切り替え

Scene1クラスを作っていきます。

#include "Model.h"
#include <vector>

class Scene1 {
public:
    Scene1(AAssetManager *assetManager) {
        std::vector <Vertex> vertices = {
                Vertex(Vector3{1, 1, 0}, Vector2{0, 0}), // 0
                Vertex(Vector3{-1, 1, 0}, Vector2{1, 0}), // 1
                Vertex(Vector3{-1, -1, 0}, Vector2{1, 1}), // 2
                Vertex(Vector3{1, -1, 0}, Vector2{0, 1}) // 3
        };
        std::vector <Index> indices = {
                0, 1, 2, 0, 2, 3
        };

        auto spAndroidRobotTexture = TextureAsset::loadAsset(assetManager, "logo.png");

        models_.emplace_back(vertices, indices, spAndroidRobotTexture);
    }
    ~Scene1() {}

    std::vector<Model> getModel() {
        return models_;
    }
private:
    std::vector<Model> models_;
};

上記のようなScene1.hというヘッダーファイルを作りました。

このScene1クラスのインスタンスをRenderer.cppの中で作ってあげて、画面がタップされたら、このScene1クラスのインスタンスに描画を切り替えるという方法をとっています。

Renderer.cppの中で画面がタップされたときの処理

void Renderer::handleInput() {
    // handle all queued inputs
    auto *inputBuffer = android_app_swap_input_buffers(app_);
    if (!inputBuffer) {
        // no inputs yet.
        return;
    }

    // handle motion events (motionEventsCounts can be 0).
    for (auto i = 0; i < inputBuffer->motionEventsCount; i++) {
        auto &motionEvent = inputBuffer->motionEvents[i];
        auto action = motionEvent.action;

        // Find the pointer index, mask and bitshift to turn it into a readable value.
        auto pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
                >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
        aout << "Pointer(s): ";

        // get the x and y position of this event if it is not ACTION_MOVE.
        auto &pointer = motionEvent.pointers[pointerIndex];
        auto x = GameActivityPointerAxes_getX(&pointer);
        auto y = GameActivityPointerAxes_getY(&pointer);

        // determine the action type and process the event accordingly.
        switch (action & AMOTION_EVENT_ACTION_MASK) {
            case AMOTION_EVENT_ACTION_DOWN:
            case AMOTION_EVENT_ACTION_POINTER_DOWN:
                aout << "(" << pointer.id << ", " << x << ", " << y << ") "
                     << "Pointer Down";

                     sceneState_ = 1;
                break;

            case AMOTION_EVENT_ACTION_CANCEL:
                // treat the CANCEL as an UP event: doing nothing in the app, except
                // removing the pointer from the cache if pointers are locally saved.
                // code pass through on purpose.
            case AMOTION_EVENT_ACTION_UP:
            case AMOTION_EVENT_ACTION_POINTER_UP:
                aout << "(" << pointer.id << ", " << x << ", " << y << ") "
                     << "Pointer Up";
                break;

            case AMOTION_EVENT_ACTION_MOVE:
                // There is no pointer index for ACTION_MOVE, only a snapshot of
                // all active pointers; app needs to cache previous active pointers
                // to figure out which ones are actually moved.
                for (auto index = 0; index < motionEvent.pointerCount; index++) {
                    pointer = motionEvent.pointers[index];
                    x = GameActivityPointerAxes_getX(&pointer);
                    y = GameActivityPointerAxes_getY(&pointer);
                    aout << "(" << pointer.id << ", " << x << ", " << y << ")";

                    if (index != (motionEvent.pointerCount - 1)) aout << ",";
                    aout << " ";
                }
                aout << "Pointer Move";
                break;
            default:
                aout << "Unknown MotionEvent Action: " << action;
        }
        aout << std::endl;
    }
    // clear the motion input count in this buffer for main thread to re-use.
    android_app_clear_motion_events(inputBuffer);

    // handle input key events.
    for (auto i = 0; i < inputBuffer->keyEventsCount; i++) {
        auto &keyEvent = inputBuffer->keyEvents[i];
        aout << "Key: " << keyEvent.keyCode << " ";
        switch (keyEvent.action) {
            case AKEY_EVENT_ACTION_DOWN:
                aout << "Key Down";
                break;
            case AKEY_EVENT_ACTION_UP:
                aout << "Key Up";
                break;
            case AKEY_EVENT_ACTION_MULTIPLE:
                // Deprecated since Android API level 29.
                aout << "Multiple Key Actions";
                break;
            default:
                aout << "Unknown KeyEvent Action: " << keyEvent.action;
        }
        aout << std::endl;
    }
    // clear the key input count too.
    android_app_clear_key_events(inputBuffer);
}

ACTION_EVENT_ACTION_POINTER_DOWN のところでsceneState = 1 にしています。

そして、Renderer.cppのrender() 描画関数の下の方でsceneStateによってシーンを切り替えています。

void Renderer::render() {
    // Check to see if the surface has changed size. This is _necessary_ to do every frame when
    // using immersive mode as you'll get no other notification that your renderable area has
    // changed.
    updateRenderArea();

    // When the renderable area changes, the projection matrix has to also be updated. This is true
    // even if you change from the sample orthographic projection matrix as your aspect ratio has
    // likely changed.
    if (shaderNeedsNewProjectionMatrix_) {
        // a placeholder projection matrix allocated on the stack. Column-major memory layout
        float projectionMatrix[16] = {0};

        // build an orthographic projection matrix for 2d rendering
        Utility::buildOrthographicMatrix(
                projectionMatrix,
                kProjectionHalfHeight,
                float(width_) / height_,
                kProjectionNearPlane,
                kProjectionFarPlane);

        // send the matrix to the shader
        // Note: the shader must be active for this to work. Since we only have one shader for this
        // demo, we can assume that it's active.
        shader_->setProjectionMatrix(projectionMatrix);

        // make sure the matrix isn't generated every frame
        shaderNeedsNewProjectionMatrix_ = false;
    }

    // clear the color buffer
    glClear(GL_COLOR_BUFFER_BIT);

    // Render all the models. There's no depth testing in this sample so they're accepted in the
    // order provided. But the sample EGL setup requests a 24 bit depth buffer so you could
    // configure it at the end of initRenderer

    if (sceneState_ == 0) {
        if (!models_.empty()) {
            for (const auto &model: models_) {
                shader_->drawModel(model);
            }
        }
    } else if (sceneState_ == 1) {
        if (!pScene_->getModel().empty()) {
            for (const auto &model: pScene_->getModel()) {
                shader_->drawModel(model);
            }
        }
    }
    // Present the rendered image. This is an implicit glFlush.
    auto swapResult = eglSwapBuffers(display_, surface_);
    assert(swapResult == EGL_TRUE);
}

まとめ

今回はシーン切り替えのためにScene1クラスを作成しました。画面がタップされたら、これのインスタンスに描画を切り替えるプログラムを組みました。

では次回パート3をお楽しみに!


投稿日

カテゴリー:

投稿者:

タグ:

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です