15
Go でゲー つくろう 小泉 守義 < [email protected] >

Gocon2013

Embed Size (px)

Citation preview

Page 1: Gocon2013

Goでゲームをつくろう小泉 守義 <[email protected]>

Page 2: Gocon2013

自己紹介[email protected]

趣味は処理系の魔改造です

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

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

Page 3: Gocon2013

http://d.hatena.ne.jp/moriyoshi/20091114/1258204128<?phpfunction 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);}?>

Page 4: Gocon2013

今日はPHPでchannelを利用する方法は説明しません

Page 5: Gocon2013

goでゲームを作るためのライブラリ

描画

SDL系: go-sdl / gosdl

OpenGL系: gl / GoGL

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

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

Page 6: Gocon2013

独断と偏見

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

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

この組み合わせがよさそう

Page 7: Gocon2013

SUPER HEXAGONhttp://superhexagon.com/

デモ動画

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

これを題材にゲームを作ってみる

Page 8: Gocon2013

What you’ll get

Page 9: Gocon2013

初期化package main

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

...

func main() {! if err := glfw.Init(); err != nil {! ! showError(err)! ! return! }! defer glfw.Terminate()

! launch(Game{ 640, 480, "HexaGOn" })}

glfwを初期化

glfwの後始末

Page 10: Gocon2013

ウィンドウを生成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) ...

ウィンドウを生成

Page 11: Gocon2013

ゲーム処理本体func launch(game Game) { ...

! if err := gl.Init(); err != nil { showError(err) return! }

if err := gameMain(game); err != nil { showError(err) return }}

OpenGLコンテキストを生成

Page 12: Gocon2013

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

... game.Run(func () { drawScene() ... }) return nil}

メインループの中身

game.Run(func () { drawScene() ... if (game.KeyPressed('Z')) { myPosition += .06 } if (game.KeyPressed('X')) { myPosition -= .06 } })

Page 13: Gocon2013

func (game Game) Run(f func()) {! for glfw.WindowParam(glfw.Opened) == 1 {! ! f()! ! glfw.SwapBuffers()! }}

ウィンドウが開かれている間は1

フレームバッファを入れ替え

game.Run

game.KeyPressfunc (game Game) KeyPressed(key int) bool { return glfw.Key(key) == glfw.KeyPress} 該当キーの押下があった場合 glfw.KeyPressが返る

Page 14: Gocon2013

package main

import ( "math" "math/rand" gl "github.com/chsc/gogl/gl21")

type Wall struct { segment int position float64 width float64}

var ( rotz float64 rotSpeed float64 = math.Pi * 0.02 bgdivision int = 6 bgcolor []gl.Float = []gl.Float{1, 1, 0} bgradius float64 = 20 hexradius float64 = .5 perturbation float64 = .04 walls []Wall = nil pog float64 = 0. myPosition float64 = 0. myDistance float64 = .1)

func initScene(game Game) (err error) { gl.Enable(gl.TEXTURE_2D) gl.Enable(gl.DEPTH_TEST)

gl.ClearColor(0., 0., 0., 0.) gl.ClearDepth(1) gl.DepthFunc(gl.LEQUAL)

gl.Viewport(0, 0, gl.Sizei(game.width), gl.Sizei(game.height)) gl.MatrixMode(gl.PROJECTION) gl.LoadIdentity() gl.Frustum(-1, 1, -1, 1, 1.0, 10.0) gl.MatrixMode(gl.MODELVIEW) gl.LoadIdentity()

return}

func destroyScene() {}

func drawBackground() {! gl.Begin(gl.TRIANGLES) defer gl.End()

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)

{ var brightness gl.Float if i % 2 == 0 { brightness = .5 } else { brightness = .8 } gl.Color4f(bgcolor[0] * brightness, bgcolor[1] * brightness, bgcolor[2] * brightness, 1) }

gl.Normal3f(0, 0, 1) gl.TexCoord2f(0, 0) gl.Vertex3f(0, 0, 1) gl.TexCoord2f(1, 0) gl.Vertex3f(x1, y1, 1) gl.TexCoord2f(1, 1) gl.Vertex3f(x2, y2, 1) }}

func drawCentralHexagon() {! gl.Begin(gl.LINE_LOOP)! defer gl.End() gl.Color4f(bgcolor[0], bgcolor[1], bgcolor[2], 1.) gl.LineWidth(16.)

for i := 0; i < bgdivision; i += 1 { x, y := gl.Float(math.Cos(float64(i) * math.Pi * 2 / float64(bgdivision)) * hexradius), gl.Float(math.Sin(float64(i) * math.Pi * 2 / float64(bgdivision)) * hexradius) gl.Vertex3f(x, y, 1) }}

func drawWalls() {! gl.Begin(gl.QUADS)! defer gl.End()

gl.Color4f(bgcolor[0], bgcolor[1], bgcolor[2], 1.)

for i := 0; i < len(walls); i += 1 { wall := walls[i] segment := wall.segment offset := wall.position - pog width := wall.width if offset < bgradius && offset + width >= 0. { x1, y1 := gl.Float(math.Cos(float64(segment) * math.Pi * 2 / float64(bgdivision)) * math.Max(offset, hexradius)), gl.Float(math.Sin(float64(segment) * math.Pi * 2 / float64(bgdivision)) * math.Max(offset, hexradius)) x2, y2 := gl.Float(math.Cos(float64(segment + 1) * math.Pi * 2/ float64(bgdivision)) * math.Max(offset, hexradius)), gl.Float(math.Sin(float64(segment + 1) * math.Pi * 2 / float64(bgdivision)) * math.Max(offset, hexradius)) x3, y3 := gl.Float(math.Cos(float64(segment) * math.Pi * 2 / float64(bgdivision)) * math.Max((offset + width), hexradius)), gl.Float(math.Sin(float64(segment) * math.Pi * 2 / float64(bgdivision)) * math.Max((offset + width), hexradius)) x4, y4 := gl.Float(math.Cos(float64(segment + 1) * math.Pi * 2/ float64(bgdivision)) * math.Max((offset + width), hexradius)), gl.Float(math.Sin(float64(segment + 1) * math.Pi * 2 / float64(bgdivision)) * math.Max((offset + width), hexradius)) gl.Vertex3f(x1, y1, 1) gl.Vertex3f(x3, y3, 1) gl.Vertex3f(x4, y4, 1) gl.Vertex3f(x2, y2, 1) } }}

func drawMyTriangle() {! gl.Begin(gl.TRIANGLES)! defer gl.End()

p := myPosition o := hexradius + myDistance s := .05 x1, y1 := math.Cos((p - s) * math.Pi * 2 / float64(bgdivision)) * o, math.Sin((p - s) * math.Pi * 2 / float64(bgdivision)) * o x2, y2 := math.Cos((p + s) * math.Pi * 2 / float64(bgdivision)) * o, math.Sin((p + s) * math.Pi * 2 / float64(bgdivision)) * o z := math.Sqrt(math.Pow(x1 - x2, 2) + math.Pow(y1 - y2, 2)) x3, y3 := math.Cos(p * math.Pi * 2 / float64(bgdivision)) * (o + z), math.Sin(p * math.Pi * 2 / float64(bgdivision)) * (o + z)

gl.Color4f(bgcolor[0], bgcolor[1], bgcolor[2], 1.) gl.Vertex3f(gl.Float(x1), gl.Float(y1), 1) gl.Vertex3f(gl.Float(x2), gl.Float(y2), 1) gl.Vertex3f(gl.Float(x3), gl.Float(y3), 1)}

func drawScene() {! gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

! gl.MatrixMode(gl.MODELVIEW)! gl.LoadIdentity()! gl.Rotatef(gl.Float(rotz), 0, 0, 1)! gl.Translatef(0, 0, gl.Float(-3. + rand.Float64() * perturbation))

! rotz += rotSpeed * 180 / math.Pi pog += .02

drawBackground() drawCentralHexagon() drawWalls() drawMyTriangle()}

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

walls = make([]Wall, 100) var j int = 0 for i := 0; i < len(walls) / 2; i += 1 { walls[j] = Wall { i % 6, float64(i) * .7, .15 }; j += 1 walls[j] = Wall { (i + 3) % 6, float64(i) * .7, .15 }; j += 1 } game.Run(func () { drawScene() // rotate against the rotation so the triangle virtually pauses // at the same position myPosition -= rotSpeed * 0.95 if (game.KeyPressed('Z')) { myPosition += .06 } if (game.KeyPressed('X')) { myPosition -= .06 } }) return nil}

initScene / destroyScene / drawScene

func drawScene() {! gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

! gl.MatrixMode(gl.MODELVIEW)! gl.LoadIdentity()! gl.Rotatef(gl.Float(rotz), 0, 0, 1)! gl.Translatef(0, 0, gl.Float(-3. + rand.Float64() * perturbation))

! rotz += rotSpeed * 180 / math.Pi pog += .02

drawBackground() drawCentralHexagon() drawWalls() drawMyTriangle()}

1フレーム分の描画処理

何もGoらしいことはありません

Page 15: Gocon2013

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

http://github.com/moriyoshi/gohex

defer便利

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