スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ウサギでもわかるプログラミング初心者講座 01 構造化

最近、プログラムを始めたS氏へ


ゲームに必要と思われる最低限のサンプルコードを書いた。
敢えて解説はしない。意図を汲み取ること。
一応、C++のコードだが、なるべく処理系に依存しないように書いたつもり。
(※プロジェクトのプロパティでDXライブラリのセッティングがしてあればコピペで動く)
追記:こっそりとY座標の表示バグを修正
追記2:※コードの中に全角スペースを入れるとエラーになるので注意!
追記3:ちょっとした解説を追加 続きを読むをクリック!

//***********************************
// 方向キーで自機を動かすだけのサンプル
//***********************************

#include "DxLib.h"

// 定数
const int 窓横幅 = 640;
const int 窓縦幅 = 480;

// 関数のプロトタイプ宣言
int ライブラリ初期化(bool ウィンドウモード, int 窓横幅, int 窓縦幅);
bool メインループ脱出チェック(int キーコード);
void 画面クリア();
void 背景描画();
void 自機描画();
void 描画バッファを実画面に反映();
void 入力検出();
void 終了処理();
void デバッグ表示();

// グローバル変数(※非推奨だが、取り敢えず)
int X座標 = 窓横幅 / 2;
int Y座標 = 窓縦幅 / 2;
int 移動速度 = 2;

// プログラムは WinMain から始まります
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
if(ライブラリ初期化(true, 窓横幅, 窓縦幅) == -1) { return -1;}

while(メインループ脱出チェック(KEY_INPUT_ESCAPE))
{
画面クリア();
入力検出();
背景描画();
自機描画();
デバッグ表示();
描画バッファを実画面に反映();
}

終了処理();
return 0;
}

//------------- 以下自作関数の定義部分----------------------------------------------
int ライブラリ初期化(bool ウィンドウモード, int 窓横幅, int 窓縦幅)
{
ChangeWindowMode(ウィンドウモード);
SetGraphMode(窓横幅, 窓縦幅, 16);// 16 or 32 ビットカラー
if( DxLib_Init() == -1 ){ return -1; }
SetDrawScreen(DX_SCREEN_BACK);
return 0;
}

bool メインループ脱出チェック(int キーコード)
{
if(!ProcessMessage() && !CheckHitKey(キーコード)){
return true;
}else{
return false;
}
}

void 画面クリア()
{
ClearDrawScreen();
}

void 背景描画()
{
int 赤 = 0, 緑 = 0, 青 = 128;
int RGB色 = GetColor(赤, 緑, 青);
bool 塗潰し = true;
DrawBox(0,0, 窓横幅,窓縦幅, RGB色, 塗潰し);
}

void 自機描画()
{
int 半径 = 16;
bool 塗潰し = false;
DrawCircle(X座標, Y座標, 半径, GetColor(0, 255, 0), 塗潰し);
}

void 描画バッファを実画面に反映()
{
ScreenFlip();
}

void 入力検出()
{
if(CheckHitKey(KEY_INPUT_LEFT) ) { X座標 -= 移動速度; } // ←方向キー
if(CheckHitKey(KEY_INPUT_RIGHT)) { X座標 += 移動速度; } // →方向キー
if(CheckHitKey(KEY_INPUT_UP) ) { Y座標 -= 移動速度; } // ↑方向キー
if(CheckHitKey(KEY_INPUT_DOWN) ) { Y座標 += 移動速度; } // ↓方向キー
}

void 終了処理()
{
DxLib_End();
}

void デバッグ表示()
{
DrawFormatString(0,0, GetColor(0,255,0), "X位置は%dでY位置は%dです", X座標, Y座標);
}
つづき

かっ勘違いしないでよね!


ベっ別に『自機動かすだけのサンプル』そのものを提供して上げたくて
こんなサンプルコードを書いたわけじゃないんだからね!

まずは、↓に書いたコードを見て欲しい。一般的な入門書ならおそらくこう書く筈。(※内容は全く同じ)

//***********************************************************
// 方向キーで自機を動かすだけのサンプルb (一般的な入門書の書き方)
//***********************************************************
#include "DxLib.h"

// 定数
const int 窓横幅 = 640;
const int 窓縦幅 = 480;

// グローバル変数(※非推奨だが、取り敢えず)
int X座標 = 窓横幅 / 2;
int Y座標 = 窓縦幅 / 2;
int 移動速度 = 2;

// プログラムは WinMain から始まります
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
ChangeWindowMode(true);
SetGraphMode(窓横幅, 窓縦幅, 16);// 16 or 32 ビットカラー
if( DxLib_Init() == -1 ){ return -1; }
SetDrawScreen(DX_SCREEN_BACK);

while(!ProcessMessage() && !CheckHitKey(KEY_INPUT_ESCAPE))
{
ClearDrawScreen();

if(CheckHitKey(KEY_INPUT_LEFT) ) { X座標 -= 移動速度; } // ←方向キー
if(CheckHitKey(KEY_INPUT_RIGHT)) { X座標 += 移動速度; } // →方向キー
if(CheckHitKey(KEY_INPUT_UP) ) { Y座標 -= 移動速度; } // ↑方向キー
if(CheckHitKey(KEY_INPUT_DOWN) ) { Y座標 += 移動速度; } // ↓方向キー

DrawBox(0,0, 窓横幅,窓縦幅, GetColor(0, 0, 128), true);
DrawCircle(X座標, Y座標, 16, GetColor(0, 255, 0), false);
DrawFormatString(0,0, GetColor(0,255,0), "X位置は%dでY位置は%dです", X座標, Y座標);

ScreenFlip();
}

DxLib_End();
return 0;
}

どっちが読み易いでショー


さて、どちらが貴君にとって読み易いコードだっただろうか?
こっちのが方短くて、むしろ読み易い?
まぁこのサンプルは短いから、そう思うかもしれない。
だが、もう少し長大になったときのことを想像してみて欲しい。

それぞれの命令文が何をしようとしているのか?
追い難いんじゃないだろうか?
DrawBox?DrawCircle?何の為に呼んでいるの?
ScreenFlipって何?DXライブラリってよく知らないんだけど…
(※別に日本語名を付けたから読み易い、英語名は読み難いって言いたいわけじゃないよ)

今は仮画像だけど、将来的に画像を読み込んで表示する処理に変更するだろう。
パターンにアニメも追加するだろう。
スクロールさせたり、敵を動かしてたりすれば、xを足したり引いたりといった記述が
どんどん追記されていくだろう。
長すぎるwhile文は近い将来、メンテする気が失せるものになる。

コメント文無しで、記憶喪失になっても読めるように書け


// 敵表示する処理
などとコメントで補ってもいいがスマートじゃない。
読み難いコードをコメントで説明するのは三流のやることだ。
コメントなんか無くても、例えプログラムに明るくなくても「//----自作関数~」より上の幹さえ読めばわかるように書いた。
貴君にも、コメントを書かなければ読めないようなコードは極力避けて欲しい。
全く書かなくても読めるコードを目指そう。
(※でも、どうしても書いた方が分り易いときは、書いてね)

お前に名前を付けてやる~プログラムとは名前を付けることである~


「何をしようとしているのか?」
を明確にし、適切な粒度で切り出すことは重要になる。
ClearDrawScreen(); とか ScreenFlip(); とか DxLib_End(); とか
なんでわざわざ1つの関数を呼び出すためだけに、関数を作っていたのか。
意図を明確にする為だ。

もしも、君が他のライブラリや言語に浮気したら


上位レイヤーの幹となる部分は殆ど弄る必要がない。
//------------- 以下自作関数の定義部分---------
よりも上の部分にはDXライブラリが用意した命令文が全く使われていないことに気付いただろうか?
(※KEY_INPUT_ESCAPEというライブラリ特有のキーコードを使っているのは勘弁して欲しい。いい案が思い付かなかった。)
枝葉のライブラリに依存した部分と幹となる処理の流れを別のレイヤーにすることで、移植性が高まる。
サンプルではシンプルな2層構造だが、実際の大規模なアプリだと3~4層くらいに及ぶことが多い。
スポンサーサイト

コメントの投稿

非公開コメント

最新記事
最新コメント
検索用タグ

ゲーム プログラム 15年前のノート Unity3D C# ハイドライド DXライブラリ C++ WizOn Kuin 

プロフィール

@kurisaka_konabe

Author:@kurisaka_konabe
作ったフリーソフトの補足説明サイト

あと、凡人による凡人の為の講座を書き始めました。
(※コメントを可能に変更)

メールフォーム

名前:
メール:
件名:
本文:

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。