👾

Androidアプリにミニゲームを組み込む!Goでミニゲーム開発 Ebitengine

2024/06/01に公開

既にネイティブでアプリ開発をしていて、なんかミニゲーム入れたくなっちゃった時。
どんな選択肢があるのか調べていて、これやっぱ Ebitengine じゃないの!と思って楽しくなってしまった話です。


https://ebitengine.org/en/documents/mobile.html

Ebitengine に関しては Zenn にも色々と紹介記事があって、すごいのだとどんなゲームが作れちゃう、とかはそちらをご覧いただくとして。。2Dの可愛いゲームが作りたければ良き選択肢のようです。

以下 Androidネイティブアプリ に対して組み込むときにたどった道筋を紹介します。
(iOS のことはわからない・・ iOSに関しては こちらの記事 が参考になる)


大まかな作業のロードマップ

  • Ebitengine でゲームを作る
    • mobile ターゲットにビルドする
  • AndroidStudio 側のセットアップ(NDKなど)
    • 初回設定(aabを配置するディレクトリの設定など)
    • ビルドしたゲームの aab をAndroidプロジェクトの方のディレクトリにコピー
    • Android Viewとして組み込む

やっていきましょう!

Ebitengine でゲームを作る

公式のサンプルを参考にディレクトリ構成
https://github.com/hajimehoshi/go-inovation

.
├── hello
│   └── hello.go                           # ゲーム部分のロジック
├── main.go      # 普通にターミナルからゲームを実行するエントリポイント
├── mobile                     # mobileビルドを書き出すディレクトリ
│   ├── android
│   │   ├── hello-sources.jar        # mobile build で出力される
│   │   └── hello.aar                # mobile build で出力される
│   └── mobile.go       # モバイル用のゲームを実行するエントリポイント
└── resources          # あとで画像リソースを配置するためのディレクトリ

go mod init する

公式サンプルのインストール手順に従い、作業するディレクトリでモジュールを宣言
https://ebitengine.org/en/documents/install.html

go mod init github.com/yourname/yourgame

私の場合は自分のgithub account で github.com/maripiyoko/my-first-mobile-game を使ってみることに。(結局この記事書くのにサンプルコードアップしたのでgithub にアップしましたが、この時点では別にGithub に repository なくても別に大丈夫)

hello/hello.go (hello world のサンプル)

package hello

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)

type Game struct{}

func (g *Game) Update() error {
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	ebitenutil.DebugPrint(screen, "Hello, World!\n")
	ebitenutil.DebugPrint(screen, fmt.Sprintf("\nTPS: %0.2f", ebiten.CurrentTPS()))
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return 320, 240
}

func NewGame() (*Game, error) {
	game := &Game{}
	return game, nil
}

main.go

package main

import (
	"log"

	"github.com/hajimehoshi/ebiten/v2"

	"github.com/maripiyoko/my-first-mobile-game/hello"
)

func main() {
	game, err := hello.NewGame()
	if err != nil {
		panic(err)
	}

	ebiten.SetWindowSize(640, 480)
	ebiten.SetWindowTitle("Hello, World!")

	if err := ebiten.RunGame(game); err != nil {
		log.Fatal(err)
	}
}

コンソールで普通に実行する時用なので、通常の RunGame でゲームを開始している

mobile/mobile.go

package mobile

import (
	"github.com/hajimehoshi/ebiten/v2/mobile"

	"github.com/maripiyoko/my-first-mobile-game/hello" // 自分が作ったモジュールの hello を読みこんでるとこ
)

func init() {
	game, err := hello.NewGame()
	if err != nil {
		panic(err)
	}

	mobile.SetGame(game)
}

func Dummy() {}

ゲームスタートに mobile 用の SetGame でゲームを開始している
公式のモバイル対応のページ に記載があったやつ)

一旦コンソールで動くことを確認する

go mod tidy
go run main.go

mobile ターゲットにビルドする

公式記載だとこのコマンド

ebitenmobile bind -target android -javapkg your.package.name -o path/to/yourgame.aar .

Go 初心者的にはここで ebitenmobile ないよーというエラーが出てきてかなりつまづく。。
ebitenmobile をローカルに正しくインストールするためにはもう少しGoのモジュール周りの知識が必要そうです・・Githubにある本体の方を指定して実行することで回避できるので一旦そちらで・・)

私の場合、以下の指定でAndroid用にビルド

go run github.com/hajimehoshi/ebiten/v2/cmd/ebitenmobile bind \
    -target android \
    -javapkg app.chestnuts.mobile.hello \
    -o ./mobile/android/hello.aar ./mobile

-javapkg の後に指定したパッケージ名はあとで Android 側でインポートするときに出てくるやつです。自分のドメイン名とかを入れとけばいいやつ。

./mobile/android の下に hello.aar, hello-sources.jar が作成されていれば大成功です!

Android NDK セットアップ

このときに Android NDK ないよーというエラーが出たので、NDKダウンロードして入れればいいっぽい。
私の場合、AndroidStudio のJDKを使っていたので、空のAndroid project で C++ のやつとか作ればいいのでは、と思って作ってみたら色々インストールされてOKでした。

AndroidStudio で
ファイル > New... > New project > 右下の方にある Native C++
を選んでプロジェクトを作る

一旦出来上がったプロジェクトをビルドして実行しておく。
(ここで出来上がったAndroid projectをこの後ゲームの組み込みサンプルとして使おうとすると、色々いらないライブラリが入ってたりして邪魔くさいのでこいつはここでオサラバの方が良いと思います)


AndroidStudio 側のセットアップ

https://qiita.com/izuki_y/items/bd61a2cf8473bf6bd9fb#新しいプロジェクトでaarを使用する

他の人が作ってくれたaarファイルを自分のプロジェクトで使用する方法

でこちらの記事が参考になります。

今回はこれに倣って、以下のディレクトリ構成にしました。

.
├── app
│   ├── build
│   ├── build.gradle.kts
│   ├── proguard-rules.pro
│   └── src
├── build.gradle.kts
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
├── gradle.properties
├── gradlew
├── gradlew.bat
├── libs                          // 新規作成(ビルドしたゲームを入れるディレクトリ)
│   ├── build                // ゲームを組み込んで動かしてるといつの間にかできてるやつ
│   ├── build.gradle         // 新規作成(ターゲットの aar を読み込む指定を追加)
│   ├── hello-sources.jar
│   └── hello.aar 
├── local.properties
└── settings.gradle.kts

ディレクトリとファイルを作る

mkdir libs
cd libs
touch build.gradle

libs/build.gradle

configurations.maybeCreate("default")
artifacts.add("default", file("hello.aar"))

作成した aar を読み込む設定を追加している

libs を外部ライブラリとして読み込む設定を追加する

settings.gradle.kts
一番下のあたり

...
include(":app")
include(":libs")   // 追加する

app/build.gradle.kts にライブラリを読み込む指定を追加

app/build.gradle.kts
dependencies ブロックのところ

dependencies {
    implementation(project(":libs")) // 追加する
    ...
}

諸々変更したので gradle sync しとく。

ビルドした aar をコピーして配置

hello.aar, hello-sources.jarlibs に配置する

AndroidStudio で読みこみ

外部ライブラリとして組み込む。

このサンプルではいきなり MainActivity のなかに入れちゃってるけど、
普通の Android View として組み込めるので任意の場所に入れ込むことができるはず。
EbitenView やるね!

MainActivity.kt

package app.chestnuts.game.firstgameapplication

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import app.chestnuts.mobile.hello.mobile.EbitenView
import go.Seq

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Seq.setContext(applicationContext)
    }

    private fun getEbitenView(): EbitenView {
        return this.findViewById(R.id.ebitenview)
    }

    override fun onPause() {
        super.onPause()
        this.getEbitenView().suspendGame()
    }

    override fun onResume() {
        super.onResume()
        this.getEbitenView().resumeGame()
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:keepScreenOn="true"
    tools:context=".MainActivity">

    <app.chestnuts.mobile.hello.mobile.EbitenView
        android:id="@+id/ebitenview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:focusable="true" />

</RelativeLayout>

完成したゲームを起動

Hello World!

続き記事予定

ゲームっぽく画像のリソースを読み込んで、Tile map を書くところもやってみたので
次の記事に分けて書きたい。
次のはGoのゲームコードの方を修正するけど、今回指定したAndroid側の設定は変えなくて大丈夫なので良き。
誰かがゲームを開発してくれたものを自分のアプリに組み込みたい、みたいな用途で使いやすそう。

ソースコード

Ebitengin

https://github.com/maripiyoko/my-first-mobile-game

Android

https://github.com/maripiyoko/FirstGameApplication

ハートレイルズ

Discussion