MEGAAK SOFT(メガークソフト)はインディーゲーム開発個人プロジェクトですが
今回2Dゲームエンジン「Ebitengine」を使い、シューティングゲームを開発し、
Steamで販売までやってみたので、どういう開発実装したのか等の概要まとめ記事を書いてみました。

なぜEbitengineを使用したのか
Ebitengine (エビテンジン) (旧称 Ebiten) はプログラミング言語 Go のオープンソースなゲームエンジンです。シンプルな API を使って、マルチプラットフォームな 2D ゲームを開発することができます。
今回開発したゲームは、レトロ風ドット絵2Dシューティングゲームです、80年代、90年代の16bitゲーム機やアーケード風のゲームが作りたかったので、当時の画像処理(回転・拡大・縮小)が実現できて、ジョイスティック(ゲームパッド)が使えて、Go言語でプログラミングできるなど、2Dゲーム開発するには個人的に最高のプラットフォームだったのでEbitengineを使用しました。
他のUnity等のゲームエンジンの様にGUIでの開発ではないので、昔ながらにゴリゴリに自前でコーディングしたい人には刺さるゲームエンジンだと思います。
Go言語とは
Go言語(Golang)はGoogleが設計したプログラム言語です、C言語に似たシンプルな言語体系のコンパイル言語で、とても高速に動作します。コンパイル時に出力バイナリを変更することで同一ソースコードでMac,Windows,Linuxで動作するアプリケーションを作成することができます。(つまりEbitengineで作ったゲームはMacやLinuxでも動かせる)
C++やJavaの様にゴリゴリのオブジェクト志向言語ではないので、昔に電波新聞社のBASIC Magazineなどを見ながらでゲームプログラムを組んでた人も割とすんなり実装できると思います。
自前フレームワークを作った
Ebitengineはゲーム開発の基本的な基盤部分は提供されますが、実際にゲームを動かす上では足りない部分や、規模が大きくなってくるとソース間の依存性や拡張性の管理が難しくなってくるので、Ebitengineをラップしたゲームフレームワークを作り開発をすすめていきました。(最初はあまり考えずにスパゲティ的に実装していったら、ステージや敵を追加する場合に拡張性が全くなくて行き詰まったんでフレームワーク化して作り直した)
フレームワークではレトロゲーム仮想ハード的な層(名前だけで実際に仮想化してる訳ではないです)を作り、ゲームロジック層からはAPIを介して仮想ハード処理を呼び出し、直接EbitengineのAPIには触れない様な設計にしました。
(当時のゲームハード上で実装している様な気分を味わえる)
フレームワーク化したことで機能別の処理部分が明確化して、拡張性と依存性のスパゲティを解消することができました。
開発はM4 Mac miniでVisualStudioCodeを使ってプログラムを書いてます。
(M4 Macはビルドがメチャ速い!)
スプライト機能
Ebitengine はキャラ画像表示するAPIを用意されていますが、いわゆるゲーム機の様なスプライトという概念は持っていません、そこでキャラ同士の重ね合わせや当たり判定、アニメーション切替、回転・拡大・縮小、半透明といったスプライト機能を統合的に実装しました。
Ebitengineは画像の回転・拡大・縮小、半透明表示機能が豊富に充実しているので、それらの機能をラップしています。
昔のゲームハードではスプライトを横に何個までは表示できるという様な制限がありましたが(メガドライブだと横に16個までとか)、フレームバッファ方式でキャラは全てグラフィック面に表示するので個数制限はありません。
当たり判定は現状四隅座標判定で対応していて、円形や三角の当たり判定とかは相当難しそうなので対応してないです・・
スプライトのPNG画像はPiskelというフリーソフトを使ってチマチマドット絵を描いていました。
最低限の機能は揃ってますが使い勝手がメチャ良いと言う訳でもないので、他にいいソフトがあれば試してみたいなと(自前でスプライトエディタを作りたい気持ちもある)

タイルマップ描画機能
32✖︎32ドッドのチップ画像を複数用意しておき、バックグラウンド画面にマップデータを元にチップ画像の組み合わせを敷き詰めて表示する機能を実装しました。さらに縦スムーススクロールも可能です。
タイルマップデータを手で作るのは大変なので、自前でチップ画像をドラッグ&ドロップで配置するツールも別途作成しました。

画面レイヤー別描画
Ebitengine では画面描画する際は *ebiten.Image というオブジェクトにイメージ画像を上乗せ描画していくことで画面に表示反映されますが、このままだと描画する順番で「奥に表示する・手前に表示する」が決まってしまうので実装する際に管理しづらくなってしまいます、そこで画面にレイヤー層を持たせて、任意の個別層にスプライトやタイルマップや静止画を表示できる仕組みを実装しました。(例ではレイヤー3個ですが実際はもっと細かい階層指定が可能)
ゲームパッド機能
Ebitengine はゲームパッドが使用できます、ただゲームパッドは「旧来レイアウト」と「Web 標準の標準ゲームパッドレイアウト」があり、使っているゲームパッドによって変わってしまいます、そこで接続しているゲームパッドがどちらのレイアウトなのかを検知して切り替える様なラップ機能を実装しました。
サウンド機能
Ebitengine にはMP3でBGMや効果音を鳴らすAPIが用意されています、あらかじめMP3データを読み込む必要がありますが、シーンの切替時等にBGMのMP3を読み込むと、環境によっては処理が一瞬止まってしまう場合があります、そこで複数のBGMや効果音MP3をまとめて読み込んでメモリ上に保持する仕組みを作り、すぐに鳴らすことができる様にしました。(ただしメモリ消費量が大きくなる)
BGMはMP3をストリーミング再生します。
イントロは1回のみでメイン部分のみループ再生する様な構成の場合、ミリ秒でループポイントを設定しますが、うまく設定しないとループ開始部分でプチノイズが発生するので設定が難しいです。
ゲームロジック内通知機能
ゲームのロジック実装に依存関係が出てくるとスパゲティの様になってしまい大抵うまくいかなくなります、そこでロジック間でデータ通知する仕組みを作り、ロジック同士を疎結合にすることができる様になりました。
例えば敵機が自機に対して弾を撃つという場合に、自機は座標を共有通知に出し、敵機はその通知を拾って、弾ロジックに対して発射座標と自機座標の発射通知を送り、弾ロジックはその座標を拾って自機に向けて弾を発射するという感じで、ロジック間で依存関係を持たせないで処理が行えます。
敵機出現データ作成について
画面に出現する敵機データを手で作成するのは辛いので、別途自作ツールを作りました。
先のタイルマップデータを読み込んで、その上に敵機が出現する種類・座標・簡易スクリプトデータ等をドラッグ&ドロップで配置していきます。

BGM作成について
今回のゲームはレトロ風シューティングということでFM音源8音+ドラムPCMを数音という感じでレトロゲーム環境を想定して作りました。(実際はメロディーにディレイやコーラスかけたり若干の装飾はしてます)
FM音源はUVI Falconという音源プラグインを使ってます、4オペレータのFM音源が鳴ります、本物のYM2151とかに比べると音がクリアすぎる気もしますが。。
ドラムはFalconのドラム音源で昔のドラムマシンのサンプルを鳴らしてます。
出来た曲はMP3化してゲーム内ではストリーミングで鳴らします。
BGM作成はMOTU Digital PerformerというDAWソフトを使ってます。
シューティングゲーム開発で参考にした本
前に古本屋で手に入れた「シューティングゲームアルゴリズムマニアックス」という本が家にあったので、すごく参考になった、自機に向けて弾を発射するとか、ベクトルやら三角関数使用方法もこの本がなければ自力だと無理だったと思う・・
出来上がったゲームをSteam向けビルド
今回のゲームはとりあえずSteamのWindows版のみリリースしました、Ebitengine をSteam対応させるには出来上がったソース実装に若干修正する必要があります(「Steam クライアント経由で開いていなかったら開き直す」処理を追加)
詳しくはEbitengineのSteamページを参照ください
自分はmain.go の最初の処理に入れました
package main
import (
"fmt"
"os"
"github.com/hajimehoshi/go-steamworks"
)
const appID = 480 // ← 実際はSteamworksでゲーム登録時に発行されるID番号
func init() {
if steamworks.RestartAppIfNecessary(appID) {
os.Exit(1)
}
if err := steamworks.Init(); err != nil {
panic(fmt.Sprintf("steamworks.Init failed: %v", err))
}
}
ビルド方法
env GOOS=windows GOARCH=amd64 go build -o yourgame_windows_amd64.exe -ldflags="-X=runtime.godebugDefault=asyncpreemptoff=1 -H=windowsgui" .
Steamworks SDK をダウンロードします
zipを解凍して、下のファイルを取得します(もしかしたらwin64配下だけで良いかも)
sdk/redistributable_bin/steam_api.lib
sdk/redistributable_bin/steam_api.dll
sdk/redistributable_bin/win64/steam_api64.lib
sdk/redistributable_bin/win64/steam_api64.dll
先に出力したyourgame_windows_amd64.exeとsteam_api各ファイルを同じフォルダに配置し、そのフォルダをzipで固めます。
Steamにアップロード提出するファイルはこのzipファイルとなります。
SteamWorks登録とストア開設
SteamWorks登録とストア開設に関しては他サイトで詳しく記載しているところが沢山あるので割愛しますが、かなりやることが沢山あるのと時間がかかります。
自分が参考にしたサイトのリンク
・Setamアカウント登録
・口座、税務情報登録、リリース費用入金&必要書類の提出(書類審査に2日〜7日)
https://marumaro7.hatenablog.com/entry/register_steamworks
・STEAMからメールが来た、提出した運転免許証画像の英訳を送ってくれと言われた場合
https://www.youtube.com/watch?v=CuTx-WJl8DU&t=16s
・STEAMのストアページ作る
https://indiegamesjapan.com/archives/2021/04/132/
・Steamストアページのレビュー依頼
・予告編の動画を提出する(2日〜5日?)
・ゲームバイナリ(zip)をアップロード
・ゲームのレビュー依頼(2日〜5日?)
実際にゲームリリースできる様になるにはストア開設から30日以上経過している事とか
アプリレビュー完了後2週間以上経過していることとか、色々制約があるので
リリースまで結構期間がかかります。
今回リリースしたゲーム
レトロアーケードゲーム風、2D縦スクロールシューティングゲームです、ナムコのゼビウスやタイトーのレイフォースに影響を受けています。ご興味ある方はどうぞ。
作者について
昔、短い期間ゲーム会社で働いてましたが、自分でゼロから商用ゲームを完成させるのは初めてなので、アイディアを実現するロジックを試行錯誤しながら開発しました。
初めてパソコンを触ったのは小学校3年生?くらいの時、日立ベーシックマスターJRという機種ですが、親が電気屋だったのでなぜか家にありました(在庫?)
ただ全然使い方が分からず初日にテープ版のフロッガーというゲームをやった程度でその後全然触らず置物状態でした。
小学校4年?くらいに日立H1というMSXパソコンが家に来ました、面白いゲームが結構あったので遊びまくりでしたが、初めてプログラムを書いたのもこの頃でした、主に雑誌の掲載を見ながらですが、BASICのMMLでPSG音源を鳴らして楽しんでました。
小学校5,6年の頃にPC-8801mk2SRが家に来ました、FM音源が搭載され、電波新聞社のBASICマガジンのゲーム音楽プログラム掲載を見ながらスペースハリアーやらファンタジーゾーンのMMLを打ち込んでました。
中学校以降はBASICプログラムの知識も向上して、固定画面でマリオ風の重力があるゲームもどきを作っては、友人に披露してました。
家のPC-8801mk2SRはフロッピードライブが付いてないモデルだったのが残念でした、毎回テープに保存するのが辛かったです・・
高校以降はプログラムよりシンセサイザーに傾倒してTM NETWORKの譜面を見ながらシーケンサーに打ち込みをしていました。
成人になってからはゲーム会社に入ったり、フリーランスでプレイステーション向けゲームBGMを作曲したり、アルバイトしたり、ゲームとは全く関係ないビスネスアプリ業界に入ったりと紆余曲折。






