プログラミング教室 Tetrisの実装 K.Yonezawa オブジェクト指向プログラミングとは オブジェクト(object):振る舞いを伴ったデータ構造 ソフトウェアを、オブジェクトとその間のやり取りが 集まったものと捉え、実装するということ ソフトウェアの設計哲学 これから大規模化していくソフトウェア開発には、必須の 概念と言える C++などの、オブジェクト指向言語を用いると実装 しやすいが、それ以外の言語(C言語など)でも十 分実現可能 あくまで設計哲学 …とはいうものの… オブジェクト指向設計を良く理解している開発者 は非常に少ないのが現状 オブジェクト指向設計技術の習得は非常に難し い 本を少々読んだり、授業を受けるくらいで習得するこ とは出来ない.何度もソフトウェア設計の経験、失敗 を積み重ねることが必要 UML図を描くだけの研修がよくあるが、図と、実装(ソ ースコード)との対応がイメージ出来ていない段階で は効果が低い→実際に実装してみる事が重要! 今回の目的 オブジェクト指向設計、実装を一通り体験してイ メージを掴む 同じ課題に取り組み、各自の設計、実装をレビュ ーしあう事で、自分の設計、実装の問題点を把 握しやすくする UML(クラス図など)の習得 オブジェクト指向言語の習得 (Windows GUIの知識を習得) Tetrisとは? ソビエト連邦の科学者アレクセイ・パジトノフら3 人が教育用ソフトウェアとして開発した、落ち物 パズルの元祖 実装が比較的容易 今回の課題 Tetrisを、C++あるいはその他(出来るだけオブジェクト 指向に適応していそうな言語)で実装すること 少なくともクラス図を描いて設計を行い、ソースの構造を 説明出来るようにすること 他からソースをもらってきて使っても良い.ただし、そのソースを 完全に理解し、説明可能な状態にする 適宜必要であれば、他の図も追加して説明してよい 将来の拡張性を考えて設計すること 縦横サイズの増減は簡単に可能? 落ちてくるブロックの種類を増やすことは簡単に可能? 何故そのような設計にしたのか?を明確に説明できるように なぜクラス図? オブジェクト指向設計されたソフトウェアの静的な構造(つま り、ソースコード上の構造)を、一番良く表した図 設計者が、どのようなことを意図して設計したかがとてもよく分かる オブジェクト指向設計の理解度もとても良く分かってしまう 開発手順 1. クラス図やその他の図を描いて設計する ⇒レビュー 2. 3. ヘッダファイルを作成する クラス定義、メンバ関数/変数定義を先に決定 する→必要ならば、クラス図を変更しながら ⇒レビュー ソースファイルを実装し、実際に動作させる ⇒ソースコードレビュー 方針・注意点 レビューは週一度時間を決めて行う 成果物の作成は各自自分のペースで行う 途中で抜けたり、途中から参加することは自由、レビューの み参加することも自由とする 業務時間中の作業については、上長に各自調整のこと 本業に支障が出ないように注意 ただし、実際に手を動かさないと、C++とオブジェクト指向の習得は難 しい レビューで他の人の構造が良いと思ったら、真似するのは全 く問題は無い 相手の許しがあれば、作成途中のソースコードをもらうことも自由.た だし、すべてを理解してから使用する必要がある 基礎知識の注意点 この企画では、C++や基本的なUMLの言葉につい て、ある程度知っていることが前提となっている 基本について知らない人でも参加は可能だが、な るべく早い段階で理解できるように努力すること 理解できていないと、後半のほうのレビューについてい くことは難しい 分からないところがあれば、米澤までどうぞ 基礎知識の確認 下記の言葉について、意味が分かっているかど うかを確認しておく クラス(class) メンバ変数 メンバ関数 Staticメンバ変数(クラス変数) Staticメンバ関数(クラス関数) new/delete インスタンス 仮想関数、純粋仮想関数 STL 継承 関連 集約、コンポジション集約 依存 基本クラス、抽象クラス 派生クラス、実装クラス インタフェースと純粋仮想関数 デザインパターン GUIに不慣れな人のために GUIプログラミングに慣れていない人用に、GUI部 分は作成済みで、ほぼ手を入れなくて良い状態の ソースコードをご提供 フリーの開発環境、Visual Studio Expressでビルド可能 GUIプログラミングは全く気にせず、ブロックの配置 を正しく管理するコードを作成すればOK もちろん、バグを見つけたり、問題解決のために必 要であれば、GUI部にもどんどん手を入れてよい 参考とするソース 参考にするソースは、 http://voidmain.org/niconicoprogram/?C%2BWin32API%E3%8 1%AE%E3%83%86%E3%83%88%E3%83%AA%E3 %82%B9 で、これは、以前ニコニコ動画で話題になっていた、「一 時間でテトリスを作ってみた」の動画で作られていたもの http://itpro.nikkeibp.co.jp/article/Interview/20091104/ 340019/ 一時間で作れるテトリスを、我々が時間をかけて作れな いはずは無い! GUIコードの大まかな構造(1) メッセージループと呼ばれるループを持ち、Windowに 届く各種メッセージに対する応答を記述していく イベントドリブンである 組み込みソフトウェアとも共通する構造 初期化イベント処理 GUI 各種イベント 描画イベント処理 マウスイベント処理 メッセージ ループ タイマーイベント処理 終了イベント処理 while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } GUIコードの大まかな構造(2) .NET Framework, MFCなどのフレームワークを使用する場合、メッセージ ループはフレームワーク内で実装されているので、アプリケーション実装者は、 そのハンドラだけを記述することになる 自分で記述することが無くても、この構造が隠れていることは意識していなければ ならない フレームワーク内で 実装 初期化イベント処理 各種イベント 描画イベント処理 マウスイベント処理 メッセージ ループ タイマーイベント処理 終了イベント処理 while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } GUIコードの大まかな構造(3) Window描画処理の例(Win32 API) WM_PAINTメッセージを受けた時の処理として記述する LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { ... case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); HDC hMemDC = CreateCompatibleDC(hdc); SaveDC(hMemDC); SelectObject(hMemDC, g_hMemBitmap); for(int x = 0; x < TETRIS_BLOCKNUM_X; x++) { for(int y = 0; y < TETRIS_BLOCKNUM_Y; y++) { ImageList_Draw(g_hImageList, (int)g_pTetrisMgr->GetBlockState(x, y), hMemDC, x * BLOCK_SIZE, y * BLOCK_SIZE, ILD_NORMAL); } } BitBlt(hdc, 0, 0, BLOCK_SIZE * TETRIS_BLOCKNUM_X, BLOCK_SIZE * TETRIS_BLOCKNUM_Y, hMemDC, 0, 0, SRCCOPY); RestoreDC(hMemDC, -1); DeleteObject(hMemDC); EndPaint(hWnd, &ps); break; } ... } GUIコードの大まかな構造(4) Window描画処理の例(.NET Framework + C#) FormクラスのOnPaint()関数をオーバーライド protected override void OnPaint(PaintEventArgs e) { for (int i = 0; i < TetrisBlockNumX; i++) { for (int j = 0; j < TetrisBlockNumY; j++) { blockImageList.Draw(e.Graphics, i * BlockSize, j * BlockSize, (int)tetrisMgr.GetBlockState((uint)i, (uint)j)); } } base.OnPaint(e); } 提供するソースの構造 GUI部は、テトリスのGUI部分を実装し、適切なタイミングでITetrisMgrクラスで定義され るIFを呼び出す 各ボタンが押された/GUIの描画の際、各マスの状態を知りたい場合 GUI部は、Win32APIで記述したものと、C#で記述したものを用意 各メンバーは、ITetrisMgrを継承したクラスを実装すればOK こちらはC++で実装するようになっている C#版では、TetrisMgr部は別DLLとして実装 提供するソースの シーケンス図 各種イベントの際、ハンド ラを呼び出す(On**) 初期化指示 キーが押された 降下処理を行うためのタイ マーが発火した GUIの求めに応じて適切 な情報を提供する 各座標のブロックの状態 ゲームオーバーになったか どうか 降下処理を行う時間を決め るためのタイマー時間間隔 拡張ネタ(簡単なの) (提供ソースを使った人のみ) GUI部分のソースコードも 見て、GUIのコードはどのような構造になっているのか をチェックする ⇒仕事で扱うコードでも、(GUI以外でも)同じような構造 は山ほどある ブロックの大きさ/デザインを変える、縦横のブロック数を 変えるなどの改造を行ってみる 公式なサイズは、縦20行 × 横10列らしい ゲームが進むと、だんだん落ちる速度を高速化させるよ うに実装する 拡張ネタ(少々面倒) 壁際やブロックが迫っている時に回転させたときに、回 転軸をずらして回転するようにする (スーパーローテー ションというらしい) ラインが消える時に消えるブロックが点滅するなどの効 果を入れる 点数が表示されるようにする 一度に消すと高得点になるようにする、全消しで高得点 になるようにする これから落ちてくる予定のブロックが表示されるようにす る Appendix: ダブルバッファリング ウインドウを直接描画すると、描画の過程がユーザーに見えてしまい、見栄 えが悪くなる ⇒内部メモリに一度描画した後、その絵をウインドウに一度に描画するように 実装 .NET Frameworkでは、ダブルバッファリングはフレームワーク内で行ってく れるため、ユーザーは実装する必要が無い .NET FrameworkでのOnPaint()関数はとてもシンプル SetStyle()で、ダブルバッファリングを有効にする設定は行う必要がある メモリ上で描画 コピー ウインドウ BitBlt() ImageList_Draw() Appendix: ImageList 同じ大きさの複数の画像を扱う機能 Indexでアクセス可能 登録する画像を横に並べた画像を一つ用意すれば、 それを自動分割してImageListに登録できる機能が ある Win32API: ImageList_Create .NET Framework: AddStrip GUIのアイコンのような場合に便利 0 1 2 3 4 5 6 7
© Copyright 2024 ExpyDoc