ラビットガーデン

主に自作ボードゲーム、自作デジタルゲームの情報 Live2D、Unity3D、自作ゲームについての解説など

2011年09月

■関数の定義の仕方


第1回でサラっと流しておいてなんだが、今さら関数の定義の仕方を簡単に説明する。
教科書通りのベタな内容で特にたいしたことは解説しない。
既知ならサラっと読み流してくれていい。
次回が本題

#include <stdio.h>
#include <iostream>
// プロトタイプ宣言
int 三角形の面積(int 底辺, int 高さ);

int main()
{
printf("三角形の面積①は%dです\n", 三角形の面積(3, 4));

int ていへん, たかさ;
std::cout << "テイヘンを入力してください" << std::endl;
std::cin >> ていへん; // ※ユーザー入力を受け付ける

std::cout << "タカサを入力してください" << std::endl;
std::cin >> たかさ; // ※ユーザー入力を受け付ける

std::cout << "三角形の面積②は" << 三角形の面積(ていへん, たかさ) << "です" << std::endl;

system("pause");
}

// 関数定義
int 三角形の面積(int 底辺, int 高さ)
{
int 答え = 底辺 * 高さ / 2;
return 答え;
}

※printfやcin、coutについては詳しく解説しない。ググるか入門書を見ればすぐにみつかるし、読めばなんとなくわかるように書いた。

■プロトタイプ宣言


こんな関数がありますよ!という宣言。関数のカタログ的なものになる。
本当は宣言せずにいきなり定義を書いてもいいのだが、色々と面倒な問題があるので必須だと思って差し支えない。
(何が問題かは面倒なので、今は解説しない)

本当は
int 三角形の面積(int, int);
と、引数の型だけでもいいのだが、筆者の趣味で必ず『仮引数名』を入れている
int 三角形の面積(int 底辺, int 高さ) ;
※お尻のセミコロン「 ; 」を忘れないこと

■関数定義


その関数がどんな動作をするのか具体的に書く。
『仮引数名』(例では、「底辺」 と 「高さ」)は必須
{ }内に中身の処理を書く。
※( )や{ }の後ろにセミコロンは要らない

■仮引数名について第1回の補足


実際の処理を書くための仮の名前
第1回では
// 定数
const int 窓横幅 = 640;

// 関数のプロトタイプ宣言
int ライブラリ初期化(bool ウィンドウモード, int 窓横幅, int 窓縦幅);

偶然同じ名前になってしまっていますが、
たまたま同じであることに注意してください。
※決して「ライブラリ初期化関数が 窓横幅 しか受け付けない」わけではないです!

if(ライブラリ初期化(true, 窓横幅, 窓縦幅) == -1) { return -1;}

if(ライブラリ初期化(true, 500, 窓縦幅) == -1) { return -1;}
などと任意の数字や変数に変えても、もちろん動作します。

if(ライブラリ初期化(true, , ) == -1) { return -1;}
などと逆に書けば、
if(ライブラリ初期化(true, 480, 640) == -1) { return -1;}
と書いたことと同じになり、縦長のウィンドウのゲームが立ち上がります

■名前の大切さ


適切な名前を付けることは、非常に大切であることは第1回でも説きました。
プログラムとは名前を付けることそのものであると。
コードの読み易さの9割以上は「名前の良し悪し」で決まります。

■英語名 VS 日本語名


第1回では、読み易さの都合で日本語名を使用しました。
でも、大半のプログラマは日本語名を嫌がります。

欠点として
・半角全角の切替が面倒
・全角スペースが不用意にコードに混じってエラー
・全角数字が不用意に入ってエラー
・日本人にしか読めないコード
・何かカッコ悪い
・VisualStudioのインテリセンス(入力補完)が利かず不便
・表記揺れが激しく自由に書け過ぎる
・大文字小文字がなく、型名や定数の命名に不便
などがあります。

利点としては
・漢字は視認性が高い
・英語、プログラムに疎い人にでも読み易い
・業務用アプリの固有名詞などは日本語の方が分り易い
・ライブラリや言語の予約語と衝突しない、自作であることが自明
などがあります。

■でも、世の中のサンプルって英語名だよね


ググッてサンプルを探すと99%が英語名です。
他人のコードもある程度読めないと不便ですよね。
それに自分が書いたコードを他人に評価して貰うときに俺俺ルールで書いてしまうと
非常に読み難くなります。
プログラムに個性は求められません。(特に命名には)
一般的な命名規則に軽く目を通しておきましょう。
関数名の命名規則
プログラミングに出る!英単語
良いネーミングをするために覚えておきたい英語のルール5つ
この辺に目を通しておけば取り敢えず大丈夫でしょう。

■ローマ字変数名が許されるのは小学生までだよね?


99.9%のプログラマにダサいと評判のローマ字変数名ですが一応利点があります。
・予約後と被らない。任意に付けることが可能な名前だと自明になる(初学者にやさしい)
それっぽい英語名が入門書に書かれていると、初心者の場合
「この名前じゃないと動かないのかな?」
と悩むことになります。
ローマ字名なら
「あ、好きな名前を付けていいんだ」
とわかりやすくなります。筆者の体験談です。
あと、無理に英語を捻り出すよりローマ字にした方がどうしても分り易いときがあります。
「自機」筆者にはいい名前が思い付きません。
下手に紛らわしい英語名を付けるより「jiki]の方が自明だと思います。

■規則に従うよりも大切なことは、紛らわしくないこと


杓子定規に規則に従うあまり、読み難くなったら本末転倒です。
1番大切なことは「1意に解釈できる」ことです。
頭を1度空っぽにして名前をもう1度みてください。
「知らないひとが見たら、別の意味に誤解しないか?」
とその可能性をよく考えてみてください。
「1週間すれば、他人のコード」
という格言があります。
未来の他人である自分に向けて、分り易い名前を残しましょう。

■記憶喪失になっても、コメントがなくても分かるように名前を付けよ


第1回でも同じことを言いましたが、大事なことなので2回言いました。
名前は本当に大切です。
適切な名前さえ付ければ醜いコメントは99%カット出来ます。
これはプログラマ以外の職業でも大切なことです。
不適切なメール件名、ファイル名、書類名は余計な仕事を増やす種になります。
不適切な子供の命名は、その子の人生を台無しにします。
是非、適切な名前を付けて快適な人生を!

初心者向け講座なので、難しい記述法は避けたいのですが、
頻出する便利な記述については、覚えて欲しいので解説します。

■ x = x + 1; は x += 1; と書けます


player.x = player.x + speed;
とか、読むのも書くのも面倒なので
player.x += speed; と。

+=の他にも
-=
*=
/=
とかが大抵の言語では使えます。

■ if( func() ) とか while(1)


if(条件式)やwhile(条件式)は真のときに実行されます。
例えば if (2<3) は実行され、if(2>3) は実行されません。

また、C++やその他多くの言語では0(false)以外は真扱いになるものが多いです。
「if (1)」は必ず実行されますし、「while(1)」はよく書かれる無限ループの記述です。
※0以外なら何でもいいので、3とか-1でも同じ結果になります

「if (a)」は aが0(false)以外なら実行されます。
例えば、if (残機) { 自機を表示(); }と書けば残機が0以外なら自機を表示()が実行されます。

※「if (!a)」は「if (a == false)」や「if (a == 0)」の意味になります

従って、戻り値のある関数を条件式として使うことも可能です。
if( func() ) はfuncが0(false)以外を返しくれば、実行されます。
※if( func() == true)とも書けます
※if( !func() )はif( func() == false)やif( func() == 0)

■ if( funcA() && funcB() ) の注意点


funcA()が偽を返すとifが成立しないので、funcB()の実行を諦めてしまいます。
諦めんなよ!どうしてそこでやめるんだよ!

if( funcA() || funcB() )
だとfuncA()が真を返した時点でifが成立してしまうので、やっぱりfuncB()をサボっちゃいます。
サボるなよ!

両方とも実行してくれないと困るときは注意しましょう。

最近、プログラムを始めた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座標);
}

続きを読む

↑このページのトップヘ