とことんF#よぷよ

9.1K Views

July 28, 22

スライド概要

Oct. 16, 2011 第63回CLR/H勉強会にて発表したスライド


関連ブログ記事
https://zecl.hatenablog.com/entry/20111004/p1
https://zecl.hatenablog.com/entry/20111016/p1

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

とことんF#よぷよ! F# + XNA ゲームプログラミング入門 10.15.2011 #CLRH63 ぜくる

2.

自己紹介       ぜくる (1978年生まれ) Twitter : @zecl , はてな id:zecl 旭川市から来ました。 F#歴3年くらいの睡眠丌足なプログラマ。 プレイ中のゲーム : DARK SOULS(心が折れそうだ… 好きなパズルゲーム 倉庨番, ぷよぷよ, Dr.マリオ, ミスタードリラー, etc…

3.

つくったことのあるもの        ブロック崩しゲーム 2Dシューティングゲーム(横/縦) リバーシ(オセロ) ・・・ テトリス風ゲーム (F# + WindowsForm) 倉庨番風ゲーム (F# + Silverlight) ぷよぷよ風ゲーム (F# + XNA) ← New!

4.

きょうお話しする内容

5.

ぷよぷよ  株式会社コンパイルから発売されていた落ち物パズル ゲームの人気シリーズ。  第1作目は1991年10月25日に発売されたMSX2版と ファミコン ディスクシステム版。  2003年以降はセガが販売を行っている。

6.

ぷよぷよ  ファミコン版のぷよぷよ  人型のオブジェクトでプレイすることが可能。 とてもシュールです…

7.

いきなりですが F# + XNA 「とことんF#よぷよ!」Demo

8.

おことわり  わたしはゲームプログラマではありません。  XNA4.0で初めてXNAに触れました(初心者)  とことんF#よぷよ!にテストコードはございません。 とてもレガシーコードです(>_<)  モナドとか高度なお話はしません。怖くないです。  F#わからなくても大丈夫。雰囲気を感じてください。

9.

XNAとは  Microsoft 社の簡単かつ本格的なゲーム開発環境(無償)  マルチプラットフォーム Windows, Xbox360, WindowsPhone  XNA開発に使用できる言語 一般的には、C# で開発します。 が、 .NET 上で動作する言語であれば、基本的になんでもOK。 (F#, VB, IronPython, IronRuby ...)

10.

XNAのここが嬉しい!  開発環境が無料である。  .NET Framework を軸とした移植性の高いコードを書ける  ゲーム開発に必要な枠組みや部品を提供してくれるので、ゲームを つくることだけに集中できる。すぐに作り始められる。  簡単なゲームであれば、難しいことを知らなくても割と大丈夫。  C#で書くことができるよ!

11.

XNAのここが嬉しい! カンスウガタゲンガー歓喜  開発環境が無料である。  .NET Framework を軸とした移植性の高いコードを書ける  ゲーム開発に必要な枠組みや部品を提供してくれるので、ゲームを つくることだけに集中できる。すぐに作り始められる。  簡単なゲームであれば、難しいことを知らなくても割と大丈夫。  C#もいいけど、F#で書くこともできるよ!

12.

ゲーム作りに必要なこと 19れんさ 絵が出せる。 動かせる。 音が出せる。 入力を受け取 れる(操作)。 文字が出せる。 ゲームデータの 保存と読込み

13.

XNAのお膳立て ウィンドウの表示 ゲームループの仕組み(FPSの管理) 絵や音等、素材をロードする仕組み 描画の仕組み(SpriteBatch, SpriteFont) 19れんさ データの圧縮, リソースの管理 入力を受け取る仕組み(キーボード、マウス、ゲームパッドなど) ストレージを扱う仕組み 多くの補助的なクラスや関数 (Vector2, Color, Math…)

14.

ゲームの流れ 開始 終了 初期化 Initialize LoadContent データ読込 データ開放 BeginRun EndRun Update Draw UnloadContent ゲームループ プレイヤーの入力受付 ゲーム状態の更新 ゲーム状態に応じた 画面への描画

15.

ゲームの流れ 開始 終了 初期化 Initialize LoadContent データ読込 データ開放 BeginRun EndRun Update Draw UnloadContent ゲームループ プレイヤーの入力受付 ゲーム状態の更新 ゲーム状態に応じた 画面への描画

16.

ゲームの流れ 開始 終了 初期化 Initialize LoadContent データ読込 データ開放 BeginRun EndRun Update Draw UnloadContent ゲームループ プレイヤーの入力受付 ゲーム状態の更新 ゲーム状態に応じた 画面への描画

17.

絵はどやって出すの? なんかオーバーロードいっぱいあるけど、必要な ときに必要なやつを適当に使えばいいよ! SpriteBatch.Draw (Texture2D, Vector2, Color)

18.

絵はどやって出すの? なんかオーバーロードいっぱいあるけど、必要な ときに必要なやつを適当に使えばいいよ! SpriteBatch.Draw (Texture2D, Vector2, Color)

19.

文字はどやって出すの? なんかオーバーロードいっぱいあるけど、必要な ときに必要なやつを適当に使えばいいよ! SpriteBatch.DrawString (SpriteFont, String, Vector2, Color)

20.

文字はどやって出すの? なんかオーバーロードいっぱいあるけど、必要な ときに必要なやつを適当に使えばいいよ! SpriteBatch.DrawString (SpriteFont, String, Vector2, Color)

21.

素材はどうやって読み込むの? Content.Load<アセットの種類>(アセット名) 例えば ・ this.Content.Load<Texture2D>(@"Content¥image¥player") ・ this.Content.Load<SoundEffect>(@"Content¥sound¥bgm")

22.

素材はどうやって読み込むの? Content.Load<アセットの種類>(アセット名) 例えば ・ this.Content.Load<Texture2D>(@"Content¥image¥player") ・ this.Content.Load<SoundEffect>(@"Content¥sound¥bgm")

23.

音はどうやって出すの? let bgm = this.Content.Load<SoundEffect>(@"Content¥sound¥bgm") bgm.CreateInstance() .Play() CreateInstanceメソッドで、サウンドの単一 再生、一時停止、または停止を制御可能 なSoundEffectInstanceインスタンスが作ら れる。そして、あとはPlay()するだけ。

24.

音はどうやって出すの? let bgm = this.Content.Load<SoundEffect>(@"Content¥sound¥bgm") bgm.Play()

25.

ゲームロジックはどう書けば? type Game1 () as this = inherit Game() override this.Update(gameTime) = … override this.Draw(gameTime) = … Update : プレーヤーやゲームの状態を更新する処理を書く Draw :プレーヤーやゲームの状態に応じた描画処理を書く

26.

ゲームロジックはどう書けば? type Game1 () as this = inherit Game() override this.Update(gameTime) = … override this.Draw(gameTime) = … Update : プレーヤーやゲームの状態を更新する処理を書く Draw :プレーヤーやゲームの状態に応じた描画処理を書く

27.

ウィンドウを出す方法は? module Program = [<EntryPoint>] let main (args : string[]) = use game = new Game1() game.Run() 0 GameクラスのRun メソッドを呼ぶだけ!

28.

ウィンドウを出す方法は? module Program = [<EntryPoint>] let main (args : string[]) = use game = new Game1() game.Run() 0 GameクラスのRun メソッドを呼ぶだけ!

29.

入力を受取りたいんだけど? Keyboard.GetState().IsKeyDown (Keys) Keyboard.GetState().IsKeyUp(Keys) Keyboard.GetState().GetPressedKeys() さまざまなメソッドで、キーボードの入力状態を容易に取得できる。 マウス、ゲームパッド(Xbox360コントローラー)、タッチや加速度セン サー(WindowsPhone)なども、プラットフォームや環境に応じて容易 に取得できす。

30.

入力を受取りたいんだけど? Keyboard.GetState().IsKeyDown (Keys) Keyboard.GetState().IsKeyUp(Keys) Keyboard.GetState().GetPressedKeys() さまざまなメソッドで、キーボードの入力状態を容易に取得できる。 マウス、ゲームパッド(Xbox360コントローラー)、タッチや加速度セン サー(WindowsPhone)なども、プラットフォームや環境に応じて容易 に取得できす。

31.

ちいさなサンプル  Demo

32.

F#  Microsoft社製のマルチパラダイム言語。  OCaml, Haskellなどの関数型言語に強い影響を受け、さま ざまなアイディアの元に作られた最先端の言語。  .NET Framework上で動作し、.NET の最新テクノロジ を利用できる関数型言語。  オープンソース  C#er, VBerが関数型をはじめるならコレできまり!

33.

マルチパラダイム 関数プログラミング 言語指向 プログラミング メタプログラミング オブジェクト指向 手続型 プログラミング

34.

マルチパラダイム 関数プログラミング オブジェクト指向 メタプログラミン グ 言語指向 手続型 プログラミング プログラミング

35.

02関数プログラミングを学ぶことの重要性 エドワード・ガーソン(Edward Garson)

36.

関数プログラミング 関数(機能/式)の集まりとして表現する 関数は第一級(ファーストクラスオブジェクト)として扱う 副作用がない(あるいは、少ない) 操作は入力を出力にマップ(写像)するのが基本

37.

関数型言語の利点 並列/並行プログラムの容易性

38.

関数型言語の利点 並列/並行プログラムの容易性

39.

関数型言語って難しいんでしょう? 簡単だというと嘘になりますが、 「泣きたいくらい難しい。」ということはないので、 まずは書き始めてください、F#を。 それが第一歩です。

40.

.NETのテクノロジ WindowsForm ASP.NET WPF Azure Silverlight XNA

41.

.NETのテクノロジ WindowsForm ASP.NET WPF Azure Silverlight XNA

42.

F# + XNA +

43.

なぜF# + XNAなのか

44.

なぜF# + XNAなのか

45.

なぜF# + XNAなのか

46.

F# + XNA  FRIENDLY F# with game development and XNA  著者:Giuseppe Maggiore ジュゼッペ ・マジョーレ  販売:Smashwords.com  販売形式:EBookのみ  内容:シミュレーション(物理、AI等) ゲーム開発に焦点を当て、関数型言 語F#2.0とXNA4.0を使って解説。  http://goo.gl/i9pRa

47.

関数型言語で 手軽にゲームが作れる時代  関数型言語でゲームが手軽に作れちゃう時代が来てた。 (意識していなかったけど、とっくに来てた!) → せっかくだから、俺はこのF#でゲームを書くぜ!

48.

F#ってゆーか、関数型言語で ゲームプログラミングとか無理ゲーじゃね?  ゲームプログラミング(ゲームループ)は状態が変化し続ける ことに着目したパラダイムです。  「状態をなるべく持たない。」、「破壊的代入を好まない」 関数型言語との相性はわるいのでは?

49.

F#ってゆーか、関数型言語で ゲームプログラミングとか無理ゲー?  ゲームプログラミング(ゲームループ)は状態が変化し続ける ことに着目したパラダイムです。  「状態をなるべく持たない。」、「破壊的代入を好まない」 関数型言語との相性はわるいのでは?

50.

開発準備  Visual Studio 2010 Shell (Integrated) 再配布可能パッケージ http://goo.gl/6MsC2  Visual C# 2010 Express http://www.microsoft.com/japan/msdn/vstudio/express/  F# CTP http://goo.gl/NMmCU  XNA Game Studio 4.0 APP HUB – [関連情報] – [ダウンロード]より http://create.msdn.com/ja-JP

51.

模倣で学ぶゲームプログラミング       新しいアイディア、新しいゲームデザインは難しい。 真似って大事。 どこまで真似をできるか。 もっとよくできそうな部分はないか探す。 自分のアイディアを盛り込む。 絵や音楽づくりはプログラミングとはまた違ったおしごと。

52.

ぷよぷよのざっくりとしたルール ・2つでひと組で、ぷよの組が落下してくる。 ・落下してくるぷよの組の色の組合せはランダム。 ・落下中のぷよの組は、プレイヤーにより移動・回転の操作ができる。 ・同じ色のぷよが4つ以上つながると消える(ぷよの消去)。 ・ぷよの下に障害物がない場合、障害物に接地するまで落下する。 ・ぷよの消去と落下の繰り返しにより連鎖が発生する場合がある。 ・ぷよを配置できない状態になったら窒息(ゲームオーバー)。 ・フィールド上のすべてのぷよを消去すると「全消し」となり得点を得る。

53.

ぷよぷよのゲームの状態 代表的なゲームの状態         ゲームオーバーかどうか ポーズ状態かどうか 現在の得点 消したぷよの数 落下中のぷよの組の状態 NEXTぷよの状態 フィールドの状態 ...

54.

ぷよぷよのゲームの状態  ぷよぷよのゲーム状態を表すレコード型

55.

フィールドの表現 フィールド上のぷよ配置 状態を表現するには?

56.

フィールドの表現  横6×縦13の〼に区切る

57.

フィールドの表現  X座標とY座標で表せる  2次元配列で表現できそう

58.

フィールドの表現 色ごとに異なる値を設定することで、 フィールドのぷよの配置を表現する

59.

フィールドの表現 ぷよが配置されていない場所も埋める。

60.

色の表現

61.

色の表現

62.

色の表現  enum (列挙型)で色違いのぷよを表現する。フィールドに ぷよが配置されていない状態 n も定義する。

63.

フィールドの表現 落下中のぷよは、確定していないので、 フィールド上は、配置がないものする。

64.

フィールドの表現(余談)  実は、1次元配列で表現できる ※数字は配列のindexを表す  数字が黄色のindexは壁を表す

65.

落下中のぷよの表現  3×3〼で考えて、配列の配列として表現

66.

落下中のぷよの回転  右回転(360度回転の流れ)  左回転(360度回転の流れ) ※ 赤ぷよを回転軸とした場合。

67.

落下中のぷよの回転  右回転(90度回転)  3×3〼のそれぞれの位置に数字で名前を付けたと します。  回転に関係のある数字のみに注目して考えます。 ※ 赤ぷよ(4の位置)を回転軸としています。

68.

落下中のぷよの回転  右回転(90度回転)  90度回転  1と5を入替える。  5と3を入替える。  7と5を入替える。

69.

落下中のぷよの回転  右回転(90度回転)

70.

落下中のぷよの回転  右回転(90度回転)

71.

落下中のぷよの回転  右回転(90度回転)  図はイメージ

72.

落下中のぷよの回転  右回転(90度回転)  ぷよの回転方向に壁やぷよなど障害物が存在する場合、 そのまま 回転させることが出来ないので回避する処理が必要。

73.

ぷよ回転時の障害物の回避  右回転(90度回転)後の位置に障害物がある場合  赤ぷを回転軸とした場合、障害物があるので右回転ができない。  障害物を回避して回転させる必要がある。

74.

ぷよ回転時の障害物の回避  右回転(90度回転)後の位置に障害物がある場合  90度右回転すると障害物に衝突 するので、左に移動する。  左に移動した後、90度右回転する。

75.

ぷよの移動 判別供用体(Discriminated Union、DU)  ぷよが移動可能な方向は、左右と下。  ぷよの落下もmove関数を利用。 パターンマッチ match direction with | Right -> {ps.current with position = x + 1, y } | Left -> {ps.current with position = x - 1, y } | Down -> {ps.current with position = x , y + 1 } というように、書くのが一般的。

76.

そもそも回転ができない場合  回避丌可能な障害物 があり、右回転も左回 転もできない。  回避および回転可能 かどうかを判断する必 要がある。  実装例ではavoidance 関数で行っている。 ※ 赤ぷよを回転軸とした場合。

77.

ぷよの生成とNEXTぷよ 現在落下中のぷよ 次に落ちてくるぷよ 次の次に落ちてくるぷよ  ぷよの組はNEXTぷよの表示のめ にも3組生成しておく必要がある。

78.

ぷよの生成とNEXTぷよ  ぷよの組はNEXTぷよの表示のめ にも3組生成しておく必要がある。  ぷよの組の色の組合せはランダム とする。  レベルに応じて生成するぷよ組の 色数を変える。 レベル001~レベル002 ・・・ レベル003~レベル004 ・・・ レベル005~レベル999 ・・・

79.

ぷよの生成とNEXTぷよ  消去したぷよに応じたレベルを返す関数を定義 消したぷよの数  40個ぷよを消すごとに「+1」したレベルを返す。  ただし、最大レベルは999とする。

80.

ぷよの生成とNEXTぷよ 雑なランダム生成 ぷよの組(puyoObj)の生成

81.

ぷよの組  ぷよの組を表すレコード型

82.

ぷよの接地と確定

83.

ぷよの接地と確定

84.

ぷよの接地と確定  フィールド上の「0」は省略していま す。落下中ぷよはフィールド上で は「0」。  GameクラスのUpdateメソッドでゲームの 状態を更新するときに、落下中のぷよ組の 座標Yに1を加算する。つまり、move関数に Direction.Downを指定して呼び出すことで 表現できる。  床あるいはぷよに接地した場合、はじめてフ ィールド上に確定させる。 床に接地(Yは12まで)するか、ぷよに接地(Y+1の位 置が「0」ではない場合)するまでは落下し続ける。

85.

重力によるぷよの落下

86.

重力によるぷよの落下

87.

重力によるぷよの落下

88.

重力によるぷよの落下

89.

ぷよの落下  フィールドの座標において、Y+1の位置が「0」であるも のすべてについて、ぷよをY+1へひとつずつずらす。

90.

ぷよの連結と消去判定

91.

ぷよの連結と消去判定  ぷよがいくつ連結しているかを調べる関数が 必要。  最も簡単な連結ぷよの個数チェックと消去ア ルゴリズムは、フィールド上の座標ごとに同色 ぷよの連結を再帰的に探索し保持しておきま す、連結ぷよが4つ以上隣接していれば消す というものです。その際、1度調べた座標につ いてはチェック済みとして、再度チェックしな いようにするという方法です。  左の絵の赤ぷよがいくつつながっているかを 例に考えてみます。

92.

ぷよの連結と消去判定

93.

ぷよの連結と消去判定 探索部分に着目してみます。

94.

ぷよの連結と消去判定 再帰関数を表す。

95.

ぷよ連結の描画 b はビットを表す。  上下左右の4方向で繋がる可能性があるので、4桁の2進数で表せる。  つまり単独の時も含めて、 16パターンの絵が必要なことがわかる。  Union型は、C#やVBで言うところの FlagsAttributeでマークした列挙型。

96.

ぷよ連結の描画  ぷよがいくつ連結しているかを調べる関数が必要。

97.

ぷよ連結の描画

98.

連鎖  ぷよの消去処理と、重力による落下処理の繰り返しで表現できます。

99.

得点計算 n は連鎖数を表す(連鎖ごとに計算が行われる) n  得点 = Σ { A n × (B n + C n + D n ) } k=1     A = 基本得点(消したぷよの数 × 10 ) B = 連鎖ボーナス C = 連結ボーナス D = 複色ボーナス

100.

連鎖ボーナス  連鎖ボーナス B は、連鎖数に応じたボーナス。 連鎖ボーナス表 連鎖数 1 2 3 4 5 ボーナス 0 8 16 32 64 6 7 8 9 10 11 12 13 14 15 16 17 18 19 96 128 160 192 224 256 288 320 352 384 416 448 480 512  連鎖数が多いほど得点が多く得られる。  最大連鎖の理論値は19連鎖。

101.

連結ボーナス  連結ボーナス Cは、同時に消したぷよの数に応じたボーナス。 連結ボーナス表 同時消しぷよ数 ボーナス 4 0 5 2 6 3 7 4 8 5 9 6 10 7  同時消し数が多いほど得点が多く得られる。  11以上連結した場合のボーナスは10。 11 10

102.

複色ボーナス  複色ボーナス Dは、同時に消したぷよの色数に応じたボーナス。 複色ボーナス表 消した色数 ボーナス 1 0 2 3 3 4 5 6 12 24  同時に消した色の数が多いほど得点が多く得られる。

103.

落下ボーナスと全消しボーナス 落下ボーナス  ぷよの組が落下中にDownキーを押しっぱなしにすることで得ら れる得点のことで、 フィールド1マスごとに1点得ることができる。 全消しボーナス  全消しボーナスとは、ぷよを消去する処理が行われた後、フィール ド上にぷよが1つも存在しない場合に得られるボーナス。  3600点 + (現在のLEVEL × 5)点

104.

全消し判定  フィールドのすべてがPuyoColors.n (「0」)である かどうかをチェックするだけです。  全消しの場合、得点にボーナス点を加算する。

105.

LEVEL判定 40個消すごとにLEVELが1上がる  計算量のことを考えると、ぷよぷよのゲーム状態 としてLEVELを持たせて、都度状態を変更した方 がよさそうな気もしなくもないですが、レベルに応 じて生成するぷよの色数を変えたりする都合上、 このように、現在のLEVELを毎回計算するように、 ステートレスな仕組みにしておいた方がしっくりく る場合も。  計算量も少ないので今回は許容する。

106.

スコアの保存  XNAではストレージで保存/読込する機能があらかじめ用意されていま  Windowsゲームでは、マイドキュメントのSavedGamesフォルダにデータが 格納されます。  XBOX360の場合はストレージの選択処理なども絡んできます。

107.

スコアの保存

108.

スコアの読込  保存とは逆に、デシリアライズして読み込むだけです(ぇ  XNAでストレージを扱う仕組みを用意してくれているのは嬉しいのです が、どうしても記述が冗長になってしまうんですよねえ…。  nullとか書きたくないですし…。

109.

ゲームの状態を描画する とことんF#よぷよ!で 使用した画像は7枚  Gameクラスの派生クラスのUpdateメソッドで更新したPuyoState(ゲーム の状態)に応じて、Drawメソッドで描画するだけ!

110.

アニメーション  C#で書く場合は、アニメーション専用のクラスを記述するのが一般的なよ うですが、F#ではいちいちクラスつくのメンドイよねってことで、ちょっとした 副作用の表現であれば、クロージャーで書いたほうがシンプルです。  えろい人はStateMonadとか言い出すので注意しましょう。

111.

おすすめの書籍

112.

おすすめの書籍

113.

おすすめの書籍

114.

ご清聴ありがとうございました。