Goでゲームを
つ ろ
 く う
小泉 守義 <moriyoshi@php.net>
自己紹介
moriyoshi@php.net

~/src/go$ grep -n 'Moriyoshi' CONTRIBUTORS

$ php -r 'phpcredits();' | grep 'Moriyoshi'

趣味は処理系の魔改造です
http://d.hatena.ne.jp/moriyoshi/20091114/1258204128
<?php
function sub($i, $ch) {
    for (;;) {
        $a = <- [$ch];
        printf("%d:", $i);
        var_dump($a);
    }
}

$ch = thread_message_queue_create();
for ($i = 0; $i < 10; $i++) {
    thread_create('sub', $i, $ch);
}

$i = 0;
for (;;) {
    [$ch] <- $i++;
    usleep(50000);
}
?>
今日はPHPでchannelを利
用する方法は説明しません
goでゲームを作るためのライブラリ

描画

 SDL系: go-sdl / gosdl

 OpenGL系: gl / GoGL

音: portaudio-go / go-openal / pulsego など

入力はSDL系は標準でできる。OpenGL系では Go-
GLUT / glfw などを使える。
独断と偏見

GoGL   https://github.com/chsc/GoGL


go-glfw   https://github.com/go-gl/glfw


この組み合わせがよさそう
SUPER HEXAGON
http://superhexagon.com/

デモ動画

 http://www.youtube.com/&v=2sz0mI_6tLQ

これを題材にゲームを作ってみる
What you ll get
初期化
package main

import (
! gl "github.com/chsc/gogl/gl21"
! "github.com/jteeuwen/glfw"
}

...

func main() {
! if err := glfw.Init(); err != nil {
! ! showError(err) glfwを初期化
! ! return
! }
! defer glfw.Terminate()
                        glfwの後始末
!     launch(Game{ 640, 480, "HexaGOn" })
}
ウィンドウを生成
func launch(game Game) {
! glfw.OpenWindowHint(glfw.WindowNoResize, 1)
                               ウィンドウを生成
!    if err := glfw.OpenWindow(game.width, game.height, 0, 0, 0,
0,   16, 0, glfw.Windowed); err != nil {
!    ! showError(err)
!    ! return
!    }
!    defer glfw.CloseWindow()

!    glfw.SetWindowTitle(game.title)
     ...
ゲーム処理本体
func launch(game Game) {
   ...

!   if err := gl.Init(); err != nil {
        showError(err)OpenGLコンテキストを生成
        return
!   }

    if err := gameMain(game); err != nil {
        showError(err)
        return
    }
}
gameMain
func gameMain(game Game) error {
! if err := initScene(game); err != nil {
! ! return err
! }
! defer destroyScene()

    ...
    game.Run(func () {
        drawScene()            game.Run(func () {
        ...                        drawScene()
    })                             ...
                メインループの中身
    return nil                     if (game.KeyPressed('Z')) {
}                                      myPosition += .06
                                   }
                                   if (game.KeyPressed('X')) {
                                       myPosition -= .06
                                   }
                               })
game.Run
func (game Game) Run(f func()) {
! for glfw.WindowParam(glfw.Opened) == 1 {
! ! f()                        ウィンドウが開かれている間は1
! ! glfw.SwapBuffers()
! }               フレームバッファを入れ替え
}


game.KeyPress
func (game Game) KeyPressed(key int) bool {
    return glfw.Key(key) == glfw.KeyPress
}                     該当キーの押下があった場合 glfw.KeyPressが返る
initScene / destroyScene / drawScene
package main

import (                                                                    1フレーム分の描画処理
    "math"
    "math/rand"

)
    gl "github.com/chsc/gogl/gl21"
                                                                            func drawScene() {
type Wall struct {
    segment int
                                                                            ! gl.Clear(gl.COLOR_BUFFER_BIT |
    position float64
    width float64                                                           gl.DEPTH_BUFFER_BIT)
}

var (
    rotz
    rotSpeed
                float64
                float64
    bgdivision int
                            = math.Pi * 0.02
                            = 6
                                                                            ! gl.MatrixMode(gl.MODELVIEW)
    bgcolor
    bgradius
                []gl.Float = []gl.Float{1, 1, 0}
                float64     = 20                                            ! gl.LoadIdentity()
    hexradius   float64     = .5
    perturbation float64
    walls       []Wall
                           = .04
                            = nil
                                                                            ! gl.Rotatef(gl.Float(rotz), 0, 0,
    pog         float64
    myPosition float64
                            = 0.
                            = 0.                                            1)
    myDistance float64      = .1
)
                                                                            ! gl.Translatef(0, 0, gl.Float(-3. +
func initScene(game Game) (err error) {
    gl.Enable(gl.TEXTURE_2D)
    gl.Enable(gl.DEPTH_TEST)
                                                                            rand.Float64() * perturbation))
    gl.ClearColor(0., 0., 0., 0.)
    gl.ClearDepth(1)
    gl.DepthFunc(gl.LEQUAL)
                                                                            !        rotz += rotSpeed * 180 / math.Pi
    gl.Viewport(0, 0, gl.Sizei(game.width), gl.Sizei(game.height))
    gl.MatrixMode(gl.PROJECTION)
    gl.LoadIdentity()
                                                                                      pog += .02
    gl.Frustum(-1, 1, -1, 1, 1.0, 10.0)
    gl.MatrixMode(gl.MODELVIEW)
    gl.LoadIdentity()

    return
                                                                                    drawBackground()

    何もGoらしいことは
}

func destroyScene() {
                                                                                    drawCentralHexagon()
}
                                                                                    drawWalls()
func drawBackground() {
!        gl.Begin(gl.TRIANGLES)
    defer gl.End()
                                                                                    drawMyTriangle()
                                                                            }
    ありません
    for i := 0; i < bgdivision; i += 1 {
        x1, y1 := gl.Float(math.Cos(float64(i) * math.Pi * 2   /   float64(bgdivision)) * bgradius),
                  gl.Float(math.Sin(float64(i) * math.Pi * 2   /   float64(bgdivision)) * bgradius)
        x2, y2 := gl.Float(math.Cos(float64(i + 1) * math.Pi   *   2/ float64(bgdivision)) * bgradius),
                  gl.Float(math.Sin(float64(i + 1) * math.Pi   *   2 / float64(bgdivision)) * bgradius)

        {
まとめと課題
Goでゲームは作れる

 http://github.com/moriyoshi/gohex

defer便利

画面の更新は同期的に行う必要があるのでGoroutine
などの活用がむずかしい

Gocon2013

  • 1.
    Goでゲームを つ ろ くう 小泉 守義 <moriyoshi@php.net>
  • 2.
    自己紹介 moriyoshi@php.net ~/src/go$ grep -n'Moriyoshi' CONTRIBUTORS $ php -r 'phpcredits();' | grep 'Moriyoshi' 趣味は処理系の魔改造です
  • 3.
    http://d.hatena.ne.jp/moriyoshi/20091114/1258204128 <?php function sub($i, $ch){ for (;;) { $a = <- [$ch]; printf("%d:", $i); var_dump($a); } } $ch = thread_message_queue_create(); for ($i = 0; $i < 10; $i++) { thread_create('sub', $i, $ch); } $i = 0; for (;;) { [$ch] <- $i++; usleep(50000); } ?>
  • 4.
  • 5.
    goでゲームを作るためのライブラリ 描画 SDL系: go-sdl/ gosdl OpenGL系: gl / GoGL 音: portaudio-go / go-openal / pulsego など 入力はSDL系は標準でできる。OpenGL系では Go- GLUT / glfw などを使える。
  • 6.
    独断と偏見 GoGL https://github.com/chsc/GoGL go-glfw https://github.com/go-gl/glfw この組み合わせがよさそう
  • 7.
  • 8.
  • 9.
    初期化 package main import ( !gl "github.com/chsc/gogl/gl21" ! "github.com/jteeuwen/glfw" } ... func main() { ! if err := glfw.Init(); err != nil { ! ! showError(err) glfwを初期化 ! ! return ! } ! defer glfw.Terminate() glfwの後始末 ! launch(Game{ 640, 480, "HexaGOn" }) }
  • 10.
    ウィンドウを生成 func launch(game Game){ ! glfw.OpenWindowHint(glfw.WindowNoResize, 1) ウィンドウを生成 ! if err := glfw.OpenWindow(game.width, game.height, 0, 0, 0, 0, 16, 0, glfw.Windowed); err != nil { ! ! showError(err) ! ! return ! } ! defer glfw.CloseWindow() ! glfw.SetWindowTitle(game.title) ...
  • 11.
    ゲーム処理本体 func launch(game Game){ ... ! if err := gl.Init(); err != nil { showError(err)OpenGLコンテキストを生成 return ! } if err := gameMain(game); err != nil { showError(err) return } }
  • 12.
    gameMain func gameMain(game Game)error { ! if err := initScene(game); err != nil { ! ! return err ! } ! defer destroyScene() ... game.Run(func () { drawScene() game.Run(func () { ... drawScene() }) ... メインループの中身 return nil if (game.KeyPressed('Z')) { } myPosition += .06 } if (game.KeyPressed('X')) { myPosition -= .06 } })
  • 13.
    game.Run func (game Game)Run(f func()) { ! for glfw.WindowParam(glfw.Opened) == 1 { ! ! f() ウィンドウが開かれている間は1 ! ! glfw.SwapBuffers() ! } フレームバッファを入れ替え } game.KeyPress func (game Game) KeyPressed(key int) bool { return glfw.Key(key) == glfw.KeyPress } 該当キーの押下があった場合 glfw.KeyPressが返る
  • 14.
    initScene / destroyScene/ drawScene package main import ( 1フレーム分の描画処理 "math" "math/rand" ) gl "github.com/chsc/gogl/gl21" func drawScene() { type Wall struct { segment int ! gl.Clear(gl.COLOR_BUFFER_BIT | position float64 width float64 gl.DEPTH_BUFFER_BIT) } var ( rotz rotSpeed float64 float64 bgdivision int = math.Pi * 0.02 = 6 ! gl.MatrixMode(gl.MODELVIEW) bgcolor bgradius []gl.Float = []gl.Float{1, 1, 0} float64 = 20 ! gl.LoadIdentity() hexradius float64 = .5 perturbation float64 walls []Wall = .04 = nil ! gl.Rotatef(gl.Float(rotz), 0, 0, pog float64 myPosition float64 = 0. = 0. 1) myDistance float64 = .1 ) ! gl.Translatef(0, 0, gl.Float(-3. + func initScene(game Game) (err error) { gl.Enable(gl.TEXTURE_2D) gl.Enable(gl.DEPTH_TEST) rand.Float64() * perturbation)) gl.ClearColor(0., 0., 0., 0.) gl.ClearDepth(1) gl.DepthFunc(gl.LEQUAL) ! rotz += rotSpeed * 180 / math.Pi gl.Viewport(0, 0, gl.Sizei(game.width), gl.Sizei(game.height)) gl.MatrixMode(gl.PROJECTION) gl.LoadIdentity() pog += .02 gl.Frustum(-1, 1, -1, 1, 1.0, 10.0) gl.MatrixMode(gl.MODELVIEW) gl.LoadIdentity() return drawBackground() 何もGoらしいことは } func destroyScene() { drawCentralHexagon() } drawWalls() func drawBackground() { ! gl.Begin(gl.TRIANGLES) defer gl.End() drawMyTriangle() } ありません for i := 0; i < bgdivision; i += 1 { x1, y1 := gl.Float(math.Cos(float64(i) * math.Pi * 2 / float64(bgdivision)) * bgradius), gl.Float(math.Sin(float64(i) * math.Pi * 2 / float64(bgdivision)) * bgradius) x2, y2 := gl.Float(math.Cos(float64(i + 1) * math.Pi * 2/ float64(bgdivision)) * bgradius), gl.Float(math.Sin(float64(i + 1) * math.Pi * 2 / float64(bgdivision)) * bgradius) {
  • 15.