ゲーム実装で身に付くプログラミング 5

「Go」の「Fyne」でデスクトップゲーム「Color」を開発してみよう

第5回の今回は、プログラミング言語「Go」の「Fyne」モジュールを使ってデスクトップゲーム「Color」を作る解説をします。

大西 武 (オオニシ タケシ)

6:30

はじめに

この連載では、さまざまなプログラミング言語やライブラリで同じゲームを開発することで、容易にそのプログラミング言語に入門します。

今回はプログラミング言語「Go」を使って、次のURLのようなColorゲームを作ります。こちらからアセットをダウンロードしておいてください。

Colorゲームについて

Colorゲームの内容は第1回でも解説した通り、ゲームを開始したら、赤緑青の色ブロックをクリックします。すると背景色にその色が混ぜられるので、どんどんクリックして真っ白な背景にしていこうというゲームです。

プログラミング言語について

第5回の今回はGo言語でFyneモジュールを使ってデスクトップアプリを開発します。Go言語はGoogleが開発したプログラミング言語で、シンプルで読みやすく、コンパイルと実行速度も速く、並行処理が得意です。

ガベージコレクション(GC)を備え、静的型付け言語であるため実行前にエラーを検出しやすいなどの高機能な特徴があります。

Fyneについて

FyneモジュールはGo言語のモジュール(追加機能)で、主に2Dのグラフィックスを描画するためのGUIライブラリです。なおFyneモジュールはOpenGLを使って描画するので、「OpenGL」または「Mesa(OpenGL互換ライブラリ)」が必須です。

開発のために必要となる環境の準備

今回必要となるGo言語と「Visual Studio Code」を準備します。

Go言語の準備

Go言語のセットアップは次のURLからダウンロードしてインストールしてください。WindowsやmacOSは「Installer」を、Linuxはダウンロードより「apt」を使ってください。詳細は割愛します。

Visual Studio Codeの準備

次のURLからVS Codeをダウンロードしてインストールしてください。セットアップの手順は省略します (第1回を参照)。

ファイルを作成しよう

「colorFyne」フォルダを新規作成し、その中でターミナルを使って次のコマンドを実行して「colorFyne」プロジェクトを作ります。

$ go mod init colorFyne

「main.go」ファイルを新規作成します。それから次の階層図のように画像の「images」フォルダなどを配置します。今回もサウンドなしです。

・階層図

colorFyneフォルダ
┠imagesフォルダ(画像フォルダ)
┃┠0.pngファイル(赤の色ブロック画像)
┃┠1.pngファイル(緑の色ブロック画像)
┃┠2.pngファイル(青の色ブロック画像)
┃┠GameOver.pngファイル(ゲームオーバー画像)
┃┠StageClear.pngファイル(ステージクリア画像)
┃┗Title.pngファイル(タイトル画像)
┠gameover.goファイル(ゲームオーバーファイル)
┠main.goファイル(メインファイル)
┠mainloop.goファイル(メインループファイル)
┠stageclear.goファイル(ステージクリアファイル)
┗title.goファイル(タイトル画面ファイル)

次のコマンドを実行してFyneモジュールを取得します。

$ go get fyne.io/fyne/v2

次のサンプルコードをコーディングし、ターミナルで次のコマンドを実行すると、colorFyneプロジェクトの最初に「main」関数からプログラムが実行開始されます。今回は真っ白なウィンドウだけが表示されます(図1)。

・サンプルコード「main.go」ファイル

package main

import (
	"image/color"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
)

const (
	width  = 1800
	height = 900
)

func main() {
	a := app.New()
	w := a.NewWindow("ColorFyne")
	bg := canvas.NewRectangle(color.White)
	bg.Resize(fyne.NewSize(width, height))
	bg.Move(fyne.NewPos(-4, -4))
	root := container.NewWithoutLayout(bg)
	w.SetContent(root)
	w.Resize(fyne.NewSize(width, height))
	w.ShowAndRun()
}

・プロジェクトの実行コマンド

$ go run . 
図1:真っ白なウィンドウを表示

【サンプルコードの解説】
「import」でモジュールを読み込みます。
「const」はウィンドウ幅高さの定数です。
「app」モジュールの「New」コンストラクタでインスタンスを生成し、ウィンドウを作成します。
背景色は「canvas」モジュールの「NewRectangle」関数で真っ白な背景色の矩形を作り「bg」変数に代入します。リサイズしないとうまく表示されません。
「container」モジュールのレイアウトにbgを追加した「root」変数をウィンドウにコンテンツをセットします。リサイズしないとうまく表示されません。
ウィンドウをShowAndRunメソッドで表示して走らせます。

もしimportが足りないようなエラーが出たら、ターミナルで次のコマンドを実行して依存関係を全て取得してください。

・依存関係を全て取得

$ go mod tidy

タイトル画面を作成する

「mainloop.go」ファイルを新規作成します。最初に表示するタイトル画面を作成します。「GameState」「Sprite」「Game」構造体と、「NewGame」コンストラクタを宣言します。

・サンプルコード「main.go」ファイル

package main

import (
	"image/color"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
)

const (
	width  = 1800
	height = 900
	fps    = 20
)

type GameState int

const (
	StateTitle GameState = iota
	StatePlaying
	StateGameOver
	StateStageClear
)

type Sprite struct {
	obj    *fyne.Container
	x, y   float32
	vx, vy float32
}

type Game struct {
	root    *fyne.Container
	bg      *canvas.Rectangle
	state   GameState
	sprites []*Sprite
	stage   int
	rgb     []int
}

func NewGame(root *fyne.Container, bg *canvas.Rectangle) *Game {
	return &Game{
		root:  root,
		bg:    bg,
		stage: 2,
	}
}

func main() {
	a := app.New()
	w := a.NewWindow("ColorFyne")
	bg := canvas.NewRectangle(color.White)
	bg.Resize(fyne.NewSize(width, height))
	bg.Move(fyne.NewPos(-4, -4))
	root := container.NewWithoutLayout(bg)
	w.SetContent(root)
	w.Resize(fyne.NewSize(width, height))
	game := NewGame(root, bg)
	game.SetState(StateTitle)
	game.Start()
	w.ShowAndRun()
}

【サンプルコードの解説】
GameStateはゲーム状態の「enum(列挙型)」に近いものです。
Sprite構造体でコンテナ「obj」プロパティ、(X,Y)座標「x」「y」プロパティ、増分「vx」「vy」プロパティを宣言します。
Game構造体でコンテナ「root」プロパティ、背景矩形「bg」プロパティ、ゲーム状態「state」プロパティ、色ブロック「sprites」プロパティ、面「stage」プロパティ、背景色「rgb」プロパティを宣言します。
NewGame関数はGame構造体のインスタンスを返すコンストラクタです。
「game」変数にGameコンストラクタのインスタンスを代入し、ゲーム状態を「StateTitle」にしてゲームをスタートします。

・サンプルコード「mainloop.go」ファイル

package main

func (g *Game) SetState(s GameState) {
}

func (g *Game) Start() {
}

【サンプルコードの解説】
Game構造体の「SetState」メソッドと「Start」メソッドを空で宣言します。

【コラム】ビッグデータをただで入手する方法

筆者の実家は自動車整備工場を経営しています。例えば、そこで整備履歴管理印刷ソフトを無料で作って配布し、その整備履歴をただで利用できると仮定します。こんな感じで他の職種でも無料で専用ツールを配布して、ビッグデータをただで収集できないでしょうか?

この記事をシェアしてください

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored