日本ソフトウェア科学会第 31 回大会 (2014 年度) 講演論文集

LablGtk2 を用いた universe teachpack の実装
上原 千裕

浅井 健一

この研究では，LablGtk2 を用いて Racket の universe teachpack を実装する．universe teachpack には，簡単にゲームを作るための基本的な関数が一通り定義されている．Racket は静的型付けの言語でなく，動的な型付けを行う．そこで，これと同じ働きをして，静的な型付けを行うものを OCaml で実装しようと考えた．今回は，画像の表
示やイベント処理に優れている GTK+へのインターフェースである，LablGtk2 を使用した．LablGtk2 を使ったことによって生じる universe teachpack との違いは多少はある．しかし，本質的な動きは universe teachpack と同じであり，静的な型付けが行われる状態でゲームを簡単に作ることができる環境を実装できた．

We implement universe teachpack of Racket using LablGtk2 in this research. The basic functions to makegames easily are defined in the universe teachpack. We implement the same library as the universe teachpackof Racket in the statically-typed language OCaml. In this research，we use LablGtk2 that is an Objective
Caml interface to GTK+ because that is superior to show images and event processing. There are some
diﬀerences between universe teachpack of Racket and the universe library we made and they are causedby using LablGtk2. However，the essential behavior of the universe library is the same as that of universe
teachpack of Racket. We could implement the environment to make games easily with static typing.

pack の説明をする．それを踏まえて，第 3 節では

1 はじめにLablGtk2 を用いて universe teachpack と同じ働きをRacket の universe teachpack [2] を使うと，簡単にする OCaml の universe library を実装をした手順をゲームを作ることができる．universe teachpack に説明する．第 4 節では，一人で遊ぶゲームと複数人では，ゲームを作るときに必要となる基本的な関数が全通信して遊ぶゲームを実装し，我々が作った OCamlて定義されているため，ゲームの作製者はゲームの内の universe library によりゲームのプログラムが実際容を作ることだけに専念できるからである．universeにどう書けるかを示す．第 5 節で関連研究について述

teachpack を OCaml で実装したのが今回の研究でべ、第 6 節でまとめと今後の課題を述べる．

ある（以後，今回実装したライブラリを OCaml のuniverse library と呼ぶ）．静的型付けの言語で実装

2 universe teachpack の説明することで，ゲームプログラミングにおいて，コンパ2. 1 ゲームを作るための Racket の関数や型

イル時に型が合っているかどうかの確認を行うことをRacket の universe teachpack にはゲームを作るた可能にした．これにより，実行前にプログラムの間違めの基本的な関数や型が定義されている．今回の研いを見つけられる可能性が大幅に増えた．

究では，それらを LablGtk2 を用いて実現するので，本稿では，まず第 2 節で Racket の universe teach-

まずはそれらを理解することから始める．
説明のために，白いウィンドウの上を，image1 といImplementation of universe teachpack using LablGtk2
Chihiro Uehara,Kenichi Asai, お茶の水女子大学理学部
情報学科, Dept. of Information and Computer Science，Ochanomizu University.

う画像が 0.3 秒ごとに x 軸方向に 10 進んでいくゲーム
画面（これを game1 とする）を universe teachpackで実装することを考える．すると，

1. 画像の型

この関数には，引数として配置したい画像のリス

2. 画像を重ねる関数

ト，画像を置く場所の座標のリスト，画像が置かれる

3. 現在の状態に基づいた画面を描画する関数

scene を受け取る．3 番目に受け取った scene の，24. 一定時間ごとに現在の状態を変化させる関数

番目の引数である座標のリストの一番前の座標に，15. 3. ，4. の関数を受け取ったら，その通りにゲー

番目の引数である画像のリストの一番前の画像を配ムを進める関数

置する．これを繰り返すことによって画像のリストにが必要になる。image1 という画像が前の位置から 10入っている全ての画像を重ねて，全て重ねてできた

進んだ画像に 0.3 秒ごとに切り替えることにより，動scene を返す．

いているように見せる．

2. 1. 3 現在の状態に基づいた画面を描画する関数

1. ∼ 5. の説明に入る前に，「世界の情報」である

この関数はもともと用意されている関数ではworld について説明する．
「世界の情報」とは「現在のなく，作りたいゲームに合わせて empty-scene やゲームの状況」のことである．game1 では，image1

place-images を使って自分で作る．

の座標を常に把握しておく必要があり，逆に言えばそ通常 draw 関数と名付けるこの関数は，現在の状態，

れさえ把握していれば今作りたいゲーム画面は書ける．

つまり world を受け取ったらそれに基づいた画像をよって，game1 の world は image1 の座標になる．こ返す．

の world を変化させることによってゲーム画面が変

draw : world -> scene

化し，world を見てゲーム画面が描かれる．game1

game1 だと，world（image1 の座標）を受け取ってだと，0.3 秒ごとに，画面を表示する前に world の x

そこに image1 を出すので以下のようになる．

座標のみ 10 増やして，更新された world をもとに

(define (draw world)

ゲーム画面を作り表示する．

(place-images (list player)

2. 1. 1 画像の型

(list world) background))

画像の型には，image 型と scene 型がある．

また，player と background は，Racket では，数字などの他に，画像にも名前を与(define width 300) ; ウィンドウの横の大きさえることができ，(define height 200) ; ウィンドウの縦の大きさ(define image1 ***)

(define player "image1.jpg") ; 画像を読み込むと書く．***の部分に画像をペーストすると，以後(define backgroundimage1 という名前でこの画像を指すようになり，
image1 は image 型になる．
Racket では，画像（image）を重ねて scene と言われる風景を作る．つまり，image 型を重ねて作られるのが scene 型である．

2. 1. 2 画像を重ねる関数
scene を作るために，universe teachpack に定義さ(empty-scene width height)) ; 白い長方形などと定義しておく必要がある．

2. 1. 4 一定時間ごとに状態を変化させる関数
この関数ももともと用意されている関数ではなく，自分で作る．
今，仮に move-x-on-tick 関数と名付ける．この関数は呼ばれるたびに，world（game1 だと image1

れている以下の二つの関数を使う．

の座標）を受け取って更新して返す．具体的には，座

empty-scene : num * num -> scene

標を受け取ったら x 座標のみ 10 足した座標を返すた

この関数には，引数として scene の縦と横の長さをめ，以下のように書ける．なお，posn-x と posn-y受け取り，その大きさの真っ白い長方形の scene を

は Racket に定義されている関数で，座標を受け取っ返す．

たらそれぞれ x 座標の値，y 座標の値を返す．また

place-images :make-posn も Racket に定義されている関数であり，image list * posn list * scene -> scene

引数を 2 つ受け取ってそれらを座標にする．

(define (move-x-on-tick world)(make-posn (+ (posn-x world) 10)(posn-y world)))型の制限はない. 以後，このやり取りする情報のこと
をメッセージと呼ぶ.
例えば，2 人（クライアント 1 とクライアント 2 と

2. 1. 5 ゲームを進める関数

する）で game1 をそれぞれ動かし，相手の image1

これは big-bang 関数と言い，universe teachpackの x 座標を自分のゲーム画面の左上に表示することに定義されている関数である．

を考える．これは，お互いが相手の image1 の x 座標

big-bang : world clause ...

を知ることができれば実現できる．クライアント 2 が

この関数は，一つ目の引数として world の初期値をクライアント 1 の x 座標を得る手順を考えると受け取り，二つ目以降の引数としてゲームの動作に必要な関数などを受け取る．game1 では，

1. 第 2. 1. 3 節の関数 draw
2. 第 2. 1. 4 節の関数 move-x-on-tick
を big-bang に登録することが必要である．実際に 1.
と 2. を big-bang に登録すると以下のようになる．

(big-bang (make-posn 20 0)

1. クライアント 1 が自分の image1 の x 座標（x座標 1 とする）をメッセージとしてサーバに送る

2. サーバはクライアント 2 に x 座標 1 をメッセー
ジとして送る3. クライアント 2 はクライアント 1 の x 座標を
サーバからのメッセージとして得る．
となる．逆のクライアント 1 がクライアント 2 の x(to-draw draw width height)

座標を得る手順も同じ考え方である．先ほど述べたよ(on-tick move-x-on-tick 0.3))

うに，メッセージは型が決められていないので，数字

on-tick に登録された move-x-on-tick 関数は 0.3

以外でももちろん送ることができる．そのため，メッ秒ごとに呼ばれる. world が更新されると，to-draw

セージとして x 座標を送るのではなく，自分の x 座に登録された draw 関数に world が渡されて，draw

標が入った world をサーバに送り，相手はサーバか関数が返した picture がゲーム画面として表示されらメッセージを受け取ってそのメッセージの中から x

る．その結果，image1 が動いて見えるゲーム画面に座標を取り出しても同じことができる．

なる．

big-bang 関数には今まで，world を返す関数を登

big-bang 関数には，他にも登録できることがた

録していたが，通信をする時はその代わりに「world

くさんあり，例えばマウスイベントが起きるごとにか world とメッセージの組」（これを universe teach-world を更新する関数や，world の更新を停止する条

pack では Package という）を返す関数を big-bang

件（つまりゲーム終了の条件）を設定する関数などが関数に登録する．もし登録された関数が world だけ登録できる．

を返したら，メッセージは送られず world が更新されるだけであり，world とメッセージの組を返したら，

2. 2 通信する環境を作る方法
2. 2. 1 universe teachpack の通信の概要

world が更新されてメッセージがサーバに送られる．
また，big-bang 関数の on-receive には，「メッ

第 2. 1 節で，ゲーム画面を作る説明をしたが，作セージを受け取るごとに呼ばれて Package 型の値をれるようになったのは１人で遊ぶゲームだけである．

返す」という関数が登録できる．これを登録すると，複数人で対戦するためには，サーバとクライアントをサーバからメッセージが送られて来るたびにこの関数作ることが必要である．

が呼び出されるので，world とメッセージの組を返す

universe teachpack では，プレイヤーのプログラ関数を登録すればメッセージがサーバに送られる．

ム，すなわち big-bang 関数を使って書いたプログラ

big-bang 関数に必要な関数を登録することによっ

ムは全てクライアントである. クライアントとは別にて，ユーザのプログラムがクライアントになることはサーバを１つ作り，クライアントとサーバが情報を送分かった．後はサーバの作り方が必要である．

受信することで通信を行う．このやり取りする情報に

2. 2. 2 サーバを作る関数 universe
universe teachpack には universe という関数があり，これがサーバを作るときに使う関数である．ク
ライアントのプログラムを作る時に使う big-bang と同じように，関数などを universe に登録することでサーバとしての役割を果たし，通信を可能にする．

universe : state-expr clause ...
state-expr は，big-bang の world と同じような役割を果たす．すなわち，サーバの現在の情報を持っていて，universe に登録された関数が呼ばれるた
びに state-expr が変更される．world と同様に，

state-expr の型は何でもよい．簡単なサーバの例を
挙げる．

図1

通信が始まる時の様子の例(universe init-state(on-new new-expr)

通信の時のみ使われるプログラム）を作るための(on-msg msg-expr))

関数

通信に新たなクライアントが参加したら，new-exprである. 順に説明する.

関数が呼ばれ，クライアントからメッセージを受信したら msg-expr が呼ばれる．

3. 1 クライアントのプログラム作成用の関数と型

2. 2. 3 通信をするゲームを作る時にやるべきこと

クライアントのプログラムを作るためには，

通信ゲームを作るときは，実装を始める前に，クラ1. 画像の型と画像を読み込む関数（image 型）

イアントとサーバの通信がどのように成り立っている

2. 重なった画像の型と画像を重ねる関数（scene

かを図にすることが大切である．図を書いてから実装

型，place-images 関数）

しないと，メッセージのやり取りがどのように起こっ

3. ゲーム画面を作る関数（big-bang 関数）ているのか分からなくなり，いざプログラムを実行し

4. world か，world とメッセージの組を表す型

た時に思い通りに通信ができないことがある．
例えば，図 1 は，二人対戦のゲームの通信が始まる(Package 型)が必要とされる. それぞれ，Racket の universe teach-

時の様子の一例である．二人のプレイヤーが通信に参pack だと何に対応するかを横に書いた. universe

加したことをサーバが把握したら，二人のプレイヤー

teachpack では，画像は直接プログラムに貼っていたにそれぞれ「start」というメッセージを送っている．

ので，画像を読み込む関数に対応する関数はない. 以
下 1. ∼ 4. を説明した後，3. の実装について説明

3 OCaml の universe library の実装
Racket の universe teachpack が一通り分かったとする.

3. 1. 1 画像の型と画像を読み込む関数

ころで，それを LablGtk2 を用いて実装する. これをuniverse teachpack では，image 型という画像を表

OCaml の universe library と呼んでいる. これに実す型があり，プログラムに直接画像を張ってその画像

装すべきものは大きく分けて 2 つあり，を image 型として読み込んでいた．OCaml の標準ラ

1. クライアントのプログラム（big bang 関数をイブラリだけでは，画像を柔軟に扱うことができな使ったゲームの画面を作るプログラム）を作るた

い．後々，マウスのクリックイベントなどを扱いたいめの関数と型ことも考慮して，画像の表示やイベント処理に優れて

2. サーバのプログラム（universe 関数を使ったいる GTK+へのインターフェースである，LablGtk2

を使用することにした．

Lablgtk2 でアンチエイリアスをかけて表示するた(*read_image : string -> picture*)
let read_image name =めには，表示する時ごとに画面を全て書き直さなくて

let buf = GdkPixbuf.from_file name inはいけない．そのため，表示する時に必要となる情報(*ファイル名から画像データを読み込む*)を持った画像の型である gnopixbuf 型を定義した．

(PIXBUF {pixbuf = buf ;x = 0;y = 0;

type gnopixbuf = {(*とりあえず座標は原点にしておく*)

pixbuf : GdkPixbuf.pixbuf;(*画像のデータ*)

width = GdkPixbuf.get_width buf;

x : int; y : int;(*画像を表示する座標*)(*画像の横軸の長さをとる*)

width : int; height : int;(*画像の大きさ*)

height=GdkPixbuf.get_height buf(*画像の縦軸の長さをとる*)})}
他にも，円や文字などの，Racket では image 型とし画像を表す型が書けたので，第 2. 1. 2 節で示したて扱われているものはそれぞれ型を定義した．以下はempty scene 関数などの画像を作る関数は作れるよ

円とテキストの型である．

うになった.

type gnocircle = {3. 1. 2 重なった画像の型と画像を重ねる関数

color : string;(*円の色*)

universe teachpack では，image 型の値を重ねて

x1 : int; y1 : int;(*円を表示する座標*)

scene 型の値を作っていたが，画像データであること

x2 : int; y2 : int;(*円の横軸と縦軸の長さ*)は変わりなく，型を分ける意味がないように思えたの}で，Racket の image 型と scene 型は picture 型にtype gnotext = {統一した．画像のリストと座標のリストと一番下の画

color : string;(*文字の色*)像をもらってきたら全てを重ねた画像を作る関数を

text : string;(*文字*)作った．

x : int; y : int;(*文字を表示する座標*)

place_images :size : int;(*文字の大きさ*)

picture list -> (int * int) list -> picture-> picture}
そして，これらの型をまとめて一つの型に入れること

これは，再帰関数として実装する．第一引数であるにより，見かけ上は universe teachpack の image 型

画像リストのそれぞれの座標を，第二引数である座と同じ，画像を示す型ができた．この型に picture

標リストを使って変更し，座標を変更した画像リスと名付けた．

トの頭にコンストラクタとして第 3. 1. 1 節の IMAGE

type picture =をつけることによって，表示したい画像のリストがRECT of gnorect (*長方形*)値である picture 型を返している. 画像のリストに|CIRCLE of gnocircle (*円*)

しておくことによって，draw 関数にそれが渡された|TEXT of gnotext (*文字*)

時に画像を順番に重ねることができ，ユーザからは|PIXBUF of gnopixbuf (*取り込んだ画像*)

place images 関数によって重なった画像が作られて|IMAGE of picture list (*画像のリスト*)

いたように見える.

なお，IMAGE of picture list については第 3. 1. 2

3. 1. 3 ゲーム画面を作る関数

節で説明する.

world の初期値以外は，オプション引数として実装

あとは，画像を読み込んだときに picture 型を返することで，universe teachpack と同じように使いたす関数を作ればよい．LablGtk2 では，画像をプログ

い関数のみ登録すること，また関数の順番を気にせず

ラムに直接貼ることはできないため，画像の名前を書に登録することができる．

いて読み込むようにした．

big bang 関数の動き方は以下である．

1. 登録された draw 関数を使って，初期 world を描写する

2. 一定時間ごとに world を変化させる関数を呼んで，world を更新する

3. キーイベントやマウスイベントが起きたときも，対応する関数を呼んで world を更新する

4. world が更新されるたびに，新しい world を元に draw 関数を使って描写する

3. 1. 5 big bang 関数の実装
第 3. 1. 3 節と第 3. 1. 4 節を踏まえて，big bang 関数の実装の主な流れを考える．big bang 関数は最初に world の初期値を受け取る．仮に firstworld という名前で受け取ったとする．big bang 関数内の何時でもどこでもただ 1 つの world を使うため，big bang関数は最初に ref 関数を使って world を定義する．

let world = ref firstworld

このように動くと，登録した関数が呼ばれる条件が満次に，各イベントに対する処理を登録していく．例

たされるたびに world が変更され，world が変更さ

として，一定時間ごとに world を書き換える関数をれるたびにゲーム画面が変更されるという big bang

考える．仮にこの関数を on tick という名前で受けの目的が達成されている．なお，1. と 4. の draw 関

取ったとする．on tick 関数を，タイマーイベントが数は，第 2. 1. 3 節で説明した draw 関数と同じ働きを発生するたびに呼び，world を更新してそれに合わせする．draw 関数はユーザが書くものであり，クライて画面を更新し，必要ならばメッセージをサーバに送アントのプログラムを作るための関数ではないので，る．なお，タイマーイベントの発生する時間の間隔もここではなく後に実装の方法を説明する．

big bang 関数の引数として受け取っている．

また，第 2. 2. 1 節で示したように，ゲーム画面を作る関数 big bang は，サーバとメッセージを送受信できるように実装しなければならない．OCaml で通信(*get_result : handleresult -> unit*)
let get_result result =match result with World w ->を行うために，今回は Unix モジュールを使ってサー(world := w; (*world の更新*)

バ・クライアント通信ができるように実装した．

draw_window ()) (*画面を描く*)

3. 1. 4 world か，world とメッセージの組の型|Package (w,m) ->通信をする時のために，第 2. 2. 1 節で述べた Racket(world := w; (*world の更新*)の universe teachpack の Package 型 に 対 応 す る ，

draw_window (); (*画面を描く*)「world か world とメッセージの組」を表す型を作

send m) (*メッセージを送る*)る必要がある．world の具体的な型はユーザしか知

get result 関数はユーザの返答を処理する関数であり得ないため，world の型は決められない．メッセー

る. draw window 関数と send 関数は，画面を描く関ジもまた同じである．よって，world の型を’a，メッ数とメッセージを送る関数であり，別途定義した.

セージの型を’b とする多相型として定義する．

let time_event _ =type (’a,’b) handleresult =(let result = on_tick !world inWorld of ’a(*更新された world（とメッセージ）を受け取る*)|Package of ’a * ’b

get_result result;

このように定義することで，world のみの場合は World

true)

world，world とメッセージの組の場合は Package

登録されている on tick 関数に world を渡し，ユー(world,message) と書くことができ，型が定義できザが返した返事を処理する. 下のようにタイマーイベ

た．ゲーム画面を作る関数 big bang は，登録されたントの登録をする時に，コールバック関数は bool 値

handleresult 型を返す関数が World world というを返すようになっており，true を返すとこのコール値を返したら world を変更して画面を描き，Package

バックの処理後，再びタイマーイベントを起こすよう(world,message) という値を返したらそれに加えてになる.

サーバに message を送信するように実装する．

GMain.Timeout.add ~ms:rate~callback:time_event(*タイマーイベントの登録. rate は時間の間隔*)
他のイベント処理もこのように登録する.

3. 2 サーバのプログラムを作るための関数
サーバのプログラムを作るためには universe 関
数だけが必要である．第 3. 1 節でも述べたように，

OCaml で通信を行うために，今回は Unix モジュールを使ってサーバ・クライアント通信ができるように実装した．サーバの状態の初期値以外は，オプション
引数としてもらうように実装し，big bang 関数と同じように使いたい関数のみ登録できるようにする．

図2

ボールゲームサーバ・クライアント通信をする際に，送受信するメッセージの型の制限をしていないため，クライアン

信できる仕組みと第 3. 1. 4 節で述べた型を加える．

トがメッセージを送ろうとした時にそのまま送ろうと

4 ゲームの実装

すると送れなかったりデータが壊れてしまう場合がある．そのため，今回はメッセージを marshalling して

第 3 節で作った OCaml の universe library を使っ

string 型に変換し，その文字列を送受信した．その際，て，実際にゲームを作る．まずは通信なしの一人で遊Unix モジュールの read 関数と write 関数を使った．

ぶゲームを作って，ゲーム画面の表示に関する理解を

また，big bang 関数には，サーバから文字列を受け深め，その次に通信ゲームを作って通信に対する理解

取ったらそれを unmarshalling する処理を書いた．こを深める．通信ゲームは, 理解しやすいように二人でのようにメッセージを marshalling することで，デー通信するゲームを作った. 通信ゲームを作る時も，第タを確実に送受信することができた．なお，ユーザは

3. 3 節同様，最初に通信に関わらない部分を実装し，ゲームを作る際に，メッセージを送る側と受け取る側

いったんゲームが思い通りに動くことを確かめてからで同じ型を扱うように設計しなければならない。

通信に関する部分の実装をするべきである．

3. 3 実装の順番

4. 1 １人で遊ぶゲーム

OCaml の universe library の実装は，まず通信にゲーム画面には色の違うボールが３個動いている．

関わらないことを全て実装してから通信に関わるこ

ボールをクリックするとボールは小さくなり，３個のとを付け加えていくべきである．前者と後者は独立しボール全てが決められて大きさよりも小さくなったらており，それらを同時平行で進めようとするとプログ

クリアというルールである（図 2）．

ラミングをしていてややこしく感じるからである．
具体的には，最初はクライアントのプログラムを作るために必要な関数や型だけを実装し，かつ第 3. 1. 4world の定義と big bang 関数の呼び出しは，以下になる．

type ball_t = {節で述べた型を作らないで world のみを扱うように

center : int * int; (*中心の座標*)実装する．そこまで実装できたら，それを使って実際

vector : int * int; (*方向ベクトル*)にゲーム画面を作ってみて，思い通りに動くか確かめ

radius : int; (*半径*)る．その後，サーバのプログラムを作るために必要な関数を作り，クライアントのプログラムにサーバと通color : color_t; (*色*)}type world = {lob : ball_t list;(*ボールの情報のリスト*)}
big_bang initial_world~name:"Ball Game" (*ウィンドウ名*)~to_draw:draw~x:width (*ウィンドウの横の大きさ*)~y:height (*ウィンドウの縦の大きさ*)~on_tick:change_world_on_tick~rate:0.3~on_mouse:change_world_on_mouse~stop_when:game_over

図3

シューティングゲーム~last_scene:draw_game_overchange world on tick 関 数 は ，登 録 さ れ た rate

4. 2 複数人で通信して遊ぶゲーム

時 間 ご と に 呼 ば れ ，そ の 都 度 world を 更 新 す る.

相手と向かい合って，相手に弾を当てたら勝ちになchange world on mouse 関数は, カーソルがウィンるシューティングゲームを作る（図 3）．左右の矢印

ドウ内にある状態でマウスが押されるか離されるか

キーで自分を左右に動かし，上の矢印キーで弾を発射したら呼ばれ，world を更新する. game over 関数

する．

は，world をもらってきたら真偽値を返す. もしこれ通信ゲームなので，まずは，第 3. 3 節で述べたようが true を返せば，もうゲームは終了して画面を更新に，通信をしないときにクライアントの画面がどうなしない. draw game over 関数は，ゲームが終了した

るかを考えてから通信に関する実装をする. 通信をしらこの関数が呼ばれ，world が渡されて picture をない場合，クライアントができることは，左右の矢印

返す. つまりゲームの終了画面を作る.

キーで自分のキャラクターを左右に動かすことと，上draw 関数は，world を受け取ったら，それに合わの矢印キーで弾を発射することのみである. よって，

せて place images 関数を使って画像を重ねて新しい

クライアントの world の定義は以下になる.

画像を作るように実装する．以下のように定義した.

type world = {let draw {lob = lst} =posn : int * int; (*自分の座標*)(place_images (List.map ball_image lst)

ball_posn_lst : (int * int) list;(*自分が打った弾の座標のリスト*)(List.map ball_center lst)
background)}

ここで，ball image 関数は，world から描きたいボー

あとは，キーのクリックを受け取る関数と一定時間ごルの画像のリストを作り，ball center 関数は，ボーとに ball posn lst のすべての座標を増やす関数を

ルの中心の座標のリストを作る.

big bang 関数に登録すれば，通信をしない場合のク

また，change world on tick 関数は以下のように定義した.

let change_world_on_tick {lob = lst} =ライアントのゲーム画面は完成である.
通信をしない場合についての実装ができたので，次は通信に関する実装をする. 具体的には，サーバを作World {lob = List.map move_ball_on_tick lst} ることと，通信をすることに伴うクライアントのプロ
ここで，move ball on tick 関数は，ボールの方向ベ

グラムの変更が必要になる. まず，通信がどのようにクトルに応じてボールの座標を変更する関数である.

行われているかの図を考える. 二人のクライアントが

図5

図4

変更後の通信ゲームの様子

rival_posn : int * int; (*相手の座標*)

通信ゲームの様子

rivalball_posn_lst : (int * int) list;
同じ時間軸で動くために，状況の変更は全てサーバが(*相手が打った弾の座標のリスト*)

行う [4]. また，自分の画面に相手のキャラクターや相}手が打った弾を表示させるため，クライアントは対戦

これで，サーバに送るものも受け取るものも，自分相手の最新の状況も自分の最新の状況とともに常にの world だけでよくなった. 通信の様子は図 5 のよ

持っておく必要がある. よって，通信の様子は図 4 のうに書けるようになる. player1 と player2 ではどように書ける.

ちらが「自分」でどちらが「rival」なのかが異なる

では，具体的な通信に関する実装に移る. まずクラだけなので，player1’s world と player2’s world

イアントのプログラムの変更について考える. 通信のでは，「自分」の情報と「rival」の情報が入れ替わっ図を見て分かる通り，キーが押されたら自分の world

たものになっている. また，big bang 関数への登録をサーバに送り，サーバからは常にクライアント二人も少し変更し，以下のようになった.

分の world を受け取りたい. ここで，第 3. 2 節で述

big_bang initial_world

べた通り，メッセージを送る側と受け取る側で同じ型~name:"Shooting Game"を扱うようにしなければならない. 送るものと受け取~to_draw:draw

るものの型をそろえるため，クライアントの world~x:widthが自分の状況も相手の状況も含むものに変更する.

~y:height

type world = {~on_key_press:key_draw

posn : int * int; (*自分の座標*)~rate:0.3

ball_posn_lst : (int * int) list;~register:client_sockfd(*自分が打った弾の座標のリスト*)~on_receive:getmessage

~stop_when:game_over~last_scene:draw_game_over

5 関連研究サーバに on tick 関数を任せるため，クライアント

5. 1 関数型言語とゲーム作成の big bang 関数への登録から on tick 関数をなく

本稿は、自分の書いたプログラムが間違っていない

した. また，クライアントは，キーが押された時のみかチェックしやすいという理由で，静的型付けの言語

サーバにメッセージを送りたい．よって，key draw 関である関数型言語の OCaml を選択した. ゲームと関数は Package コンストラクタがついた world とメッ

数型言語の関係についてさらに追求して考えている

セージの組を返す. getmessage 関数は，更新されたのが松島，上野，森畑，大堀 [3] である。彼らは型シ

world をもらうので，

ステムの他に，高階関数を用いたプログラムの構造

let getmessage world mail = World mail化，明瞭な意味論に基づいたプログラム変換などが関となる. これで world が最新のものに更新される. ま

数型言語では深く議論されていて，これらの技術はた，client sockfd は，サーバ・クライアント通信に

ゲームプログラムに共通する定石やゲームに関する必要な，既に起動したサーバプログラムのポート番号

論理を記述するのに有効である可能性があると述べと IP アドレスの組である．

る. そして彼らは、関数型言語におけるプログラミン

次にサーバを考える. サーバがクライアントのグ手法の内主要なものの 1 つである，宣言的な記述

world の変更を行うため，state の中に各クライア

によるプログラミングに注目し，ゲームのルールのントに送った world，つまり各クライアントの最新の宣言的な分析と記述に基づいたゲームプログラミン

world を持っている. よってサーバの state はクライグ手法を述べた. 彼らは簡単なゲームでの実際の例を

アントの名前と world の組のリストになる. サーバ

見せており，今後は複雑なルールへの応用をしたいとは，新しいクライアントが加わったときに呼ばれる関述べていた. 彼らの研究から，関数型言語でのゲーム

数 onnew を，プログラミングは便利なプログラミング環境であり，

let onnew state iworld = ...

まだまだ発達できることが伺える.

と定義しており，iworld という引数がクライアントの区別をするもの，つまり名前になっている. よって

5. 2 初心者のゲーム作成

今回の場合なら state は，

HtDP [1] は学んでいて，ゲームプログラミングに{[(player1’s iworld，player1’s world);

関しては初心者である学生が，Racket の universe(player2’s iworld，player2’s world)]}

teachpack を使ってゲームを作る授業を通して，どのとなる. universe 関数の呼び出しは以下になる．

ような考え方やプログラムを生み出して理解していっuniverse init_state

たのかが Morazan [4] に述べられている. この論文に~on_new:onnew

は，学生が最初に考えついた一見合ってるように見え~on_msg:onmsg

るが実は避けた方がよい考え方なども書かれており，~rate:0.3 (*tick 関数が呼ばれる間隔*)

ゲームプログラミングの初心者がどのようにして理解~on_tick:tick (*画面の弾を進める*)を深めていくのかが興味深かった. この論文中では初

onmsg 関数は，クライアントからメッセージが来た

心者が使う基本的な関数のみが出てきてゲームを作っときに呼ばれる関数である．クライアントからメッていたため，私はこの研究を本格的に始める前に一度セージが来るのはキーが押された時だけである. メッ

読み，最低限なくてはいけない関数や仕組みを理解しセージを送ってきたクライアントにはメッセージとした. 今後様々な人に今回実装した universe library をて world を返すとともに，メッセージを送って来な使ってゲームを作ってもらおうと考えているが，ゲー

かった方のクライアントには，world の rival posn

ムプログラミングの初心者に参加してもらう予定でと rivalball posn lst を変更したものを送る.

あるから，この論文はその時の手助けになると思う.

5. 3 関数型言語と画面の表示

はエラーを出すことなく思い通りに動き，universe

ゲームを作る上で，表示される画面がどうなるかは

library は実際に使える段階まで進んだ．

とても重要である. 本稿では，LablGtk2 を用いるこ

現段階では，image 型と scene 型を picture 型にとによって，図形をアンチエイリアスをかけて描ける

統一したこと以外は Racket の universe teachpack のことに加えて，jpg 画像や png 画像などの画像データ

動きを真似することしかしていないが，自分たちが使も扱えるように実装することで，表示する画面をなる

いやすいように改良していきたい．

べくきれいでユーザの思い通りになるようにした.

今後は，universe library を使って様々な人にゲー

画面の表示の関連で，興味深いのが Winter [5] で紹ムを作成してもらい，たくさん出てくるであろう問題

介されている Bricklayer である. これは，SML で書を解決していきたい．今までで作ったゲームは，このかれた API で，LEGO R 風の 3D 画面を表示できる研究の中身を知っている人が試しに作った簡単なゲー

ものである. 論文 [5] 中に，Bricklayer は初心者の関ムしかない．なので，様々な人に使ってもらうことで数型言語への導入にちょうど良いと書かれている通初めて見える欠点やバグがあると思う．それらを改善

り，少ない分かりやすい記述で色々な図形の表示がでして，静的型付けの言語でのゲーム作成の環境をよりきる. 関数型言語で書かれていて，ユーザの入力が比整えていきたい．
参 考 文 献較的少なくてユーザを楽しませる画面を表示できることは本研究の目標の 1 つであるため，Bricklayer は参考になる.

6 まとめと今後の課題
Racket の universe teachpack とまったく同じではないものの，本質的な部分はほとんど同じ働きをするものが実装できた．違いの例を述べると，画像をプ
ログラムに直接貼れないことなどである．実際に今回作った OCaml の universe library を用いて作った二つのゲーム（一人で遊ぶものと複数人対戦のもの）[1] Felleisen, M., Findler, R. B., Flatt, M., and Krishnamurthi, S.: How to Design Programs: An Introduction to Programming and Computing, MIT
Press, Cambridge, MA, USA, 2001.
[2] Felleisen, M., Findler, R. B., Flatt, M., and Krishnamurthi, S.: A Functional I/O System or, Funfor Freshman Kids, ICFP 2009, (2009), pp. 47–58.
[3] 松島勇介, 上野雄大, 森畑明昌, 大堀淳: 宣言的記述からの関数型言語によるゲームプログラムの導出, 第13 回プログラミングおよびプログラミング言語ワーク
ショップ（PPL2011）論文集, (2011), pp. 193–207.
[4] Morazan, M. T.: Functional Video Games in CS1
III, TFP 2013, (2013), pp. 149–167.
[5] Winter, V.: Bricklayer: An authentic introduction to the FPL SML, TFPIE 2014, (2014).