週末プログラマの日記

プログラミングメインで日記とかも書きます

Goでゲーム制作_準備

ゲームを作ろうと思ったきっかけ

Paizaでプログラミング問題を解いたこと

がきっかけです。 もともとゲームは好きで制作にも興味があったのですが、改めて作りたくなった感じです。

まずは準備

ゲームを作るのは初めてなので、どのように作るのが良いのか少し調べてみました。 すると以下の3点を主に注意したほうがいいことがわかりました。

気を付けるべきこと3点

いきなり大作を作ろうとしない!!

 ゲーム作りの初心者が陥るポイントNo.1だそうです。
 自分のアイディアを形にしてゲームを作ろうというくらいですから、もちろんゲームへの愛は人一倍。そんな人だからこと「こんなゲームを作りたい!」「このアイディアはいける!!」という熱い情熱をもって開発を始めるのですが、、、個人でのゲーム作りは情熱だけでは乗り越えられない「工数の限界」という壁があります。
 一説では、ゲームを製作するためにかかる時間は
 必要製作時間 = 予想プレイ時間 × 100  だそうです!
 これはある程度、自分のアイディアの大きさと突っ込める工数の間で調整が必要ですね。

可能な限りフリー素材を使う!!(もちろん権利には注意しよう!!)

 これは一つ目の注意点と強く関連するのですが、ゲームを作るために勉強が必要なことは非常にたくさんあります。
 プログラミング(フラグ管理、レベルシステムなど含む)、シナリオ作成、ゲーム音楽作成、イラスト作成などなど。これらすべてを自前で揃えようとすると、おそらく個人では作るのは難しいのではないでしょうか。すべて自分の手作りが、個人製作のゲームとしては理想の形なのかもしれませんが、万能の天才にしか作れない難易度になってしまいます。
 そのため、ゲームの個人製作では権利関係に十分に注意を払いつつ積極的にフリー素材やライブラリを採用する姿勢が、必要となります。

完璧を目指さない!!

 何百人体制で開発された販売されているゲームでさえバグの一つや二つは必ずあります。いわんや個人製作をや、ですね。
 人に迷惑をかけない範囲であれば、個人のゲーム制作では、まずは動くものを作って政策自体を楽しむのもありだと思います。
 そのほうがモチベーションが保ちやすいようですね。

今回作ったもの

f:id:fujita-weekend:20200430130646g:plain
ボールと板
 今回はfyne.ioライブラリ(pure golangGUIアプリ製作用ライブラリ)の調査もかねて簡単なピンポンゲームを作りました。はてぶに挙げることができるGIF画像のサイズを超えないようにするためにフレームレートを落としたのでカクカクですが、、、
 goroutineの挙動の勉強も兼ねています。
 反省点は以下の二つです。

反省点

当たり判定

 当たり判定ムズイ!!当たり判定は、細かく見ていかないといけないですね。

得点の表示がおかしい

 画面左下に現在取得しているポイント数を表示しています。しかし、原因はまだ分かっていませんが、数値の表示が不安定になっています。
 ログを見る限りでは、ポイント数の計算自体は間違っておらず正常に行われているように見えますが、、、さらなる調査が必要な部分です。

ソースコード

package main

import (
    "fmt"
    "image/color"
    "log"
    "time"

    "fyne.io/fyne/widget"

    "fyne.io/fyne/layout"

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

func main() {
    myApp := app.New()
    w := myApp.NewWindow("Ball and Board is not Bored!!")
    w.Resize(fyne.NewSize(800, 600))

    // ボールの生成
    circle := canvas.NewCircle(color.White)
    circle.StrokeWidth = 5

    // 板の生成
    board := canvas.NewLine(color.White)
    board.StrokeWidth = 5
    cont := fyne.NewContainer(board)

    // ポイント表示枠の生成
    pointBox := widget.NewLabel("00 pts")
    pboxCon := fyne.NewContainer(pointBox)

    content := fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(50, 50)), circle, cont, pboxCon)

    w.SetContent(content)
    log.Println(circle.Position1, circle.Position2)

    go func() {
        board.Position1 = fyne.NewPos(250, 550)
        board.Position2 = fyne.NewPos(350, 550)
        board.Refresh()
        pointBox.Move(fyne.NewPos(0, w.Canvas().Size().Height-50))
        pointBox.Refresh()
        // キーボードからの入力に応じて反射板を移動させる
        go func() {
            for {
                w.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
                    log.Printf("key=%s\n", string(ev.Name))
                    if ev.Name == fyne.KeyRight {
                        board.Move(fyne.NewPos(board.Position().X+15, board.Position().Y))
                    }
                    if ev.Name == fyne.KeyLeft {
                        board.Move(fyne.NewPos(board.Position().X-15, board.Position().Y))
                    }
                })
                canvas.Refresh(board)
            }
        }()

        diffX := 1
        diffY := 1
        point := 0
        // ボールを 1 count/msで移動する
        for {
            time.Sleep(time.Millisecond)
            x := circle.Position().X + diffX
            y := circle.Position().Y + diffY
            circle.Move(fyne.NewPos(x, y))

            if x < 0 || w.Canvas().Size().Width < x+50 {
                diffX *= -1
            }
            if y < 0 || w.Canvas().Size().Height < y+50 {
                diffY *= -1
            }
            centerX := (circle.Position1.X + circle.Position2.X) / 2
            if diffY > 0 && board.Position1.X <= centerX && centerX <= board.Position2.X && board.Position().Y <= circle.Position2.Y {
                diffY *= -1
                point++
                pointBox.SetText(fmt.Sprintf("%d pts", point))
                canvas.Refresh(pointBox)
                log.Printf("Ball (x1, y1)=(%d, %d), (x2, y2)=(%d, %d)\n", circle.Position1.X, circle.Position1.Y, circle.Position2.X, circle.Position2.Y)
                log.Printf("Ball Center (x, y)=(%d, %d)\n", circle.Position().X+25, circle.Position().Y+25)
                log.Printf("Board (x1, y1)=(%d, %d), (x2, y2)=(%d, %d)\n", board.Position1.X, board.Position1.Y, board.Position2.X, board.Position2.Y)
                log.Printf("Point:%0d\n", point)
            }
            canvas.Refresh(circle)
        }
    }()
    w.ShowAndRun()
}

まとめ

 今回簡単なゲームを作ってみました。いつものプログラミングが上手くいった時の高揚感とは、また違った達成感がありました。非常に楽しかったです。
 次回以降の記事では、プログラミングのことだけでなくゲーム用に制作した画像なども挙げていきたいと考えています。