1 / 3 範囲制限処理の関数化と戻り値
座標を画面内に収めるクランプ処理を、自作関数にまとめて再利用します。関数の定義、引数の値渡し、returnで値を返す方法を学びます。呼び出し元で戻り値を代入する理由も確認します。
共通パターンの抽出と関数の定義
xとyに共通する範囲制限の抽出
第04回までのコードでは、青いロボットのx座標とy座標を次のif文で制限していました。二つの処理は、ほぼ同じ形です。
// xの制限(上限は 640 - 48 = 592)
if (x < 0) x = 0;
if (x > 592) x = 592;
// yの制限(上限は 480 - 48 = 432)
if (y < 0) y = 0;
if (y > 432) y = 432;どちらも、値が0未満なら0に戻し、上限を超えたら上限値に戻します。範囲内なら値を変えません。処理の違いは、判定する値と上限値の二つだけです。
処理を一つにまとめ、名前を付けて再利用できるようにしたものが関数です。英語ではfunctionと呼びます。今回はxとyに共通するクランプ処理を関数にします。
KeepInsideの定義と配置
#include <DxLib.h>の直後、WinMainの手前に、次の関数定義を追加してください。
#include <DxLib.h>
int KeepInside(int value, int limit)
{
if (value < 0)
return 0;
if (value > limit)
return limit;
return value;
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)関数定義の文法構成
関数の定義では、戻り値の型、関数名、仮引数をこの順序で書きます。
int KeepInside(int value, int limit)int:戻り値の型KeepInside:関数名int value, int limit:仮引数
戻り値の型は、関数が呼び出し元へ返すデータの種類です。今回は制限後の座標を整数で返すので、intを指定します。
関数名は、呼び出すときに使う名前です。KeepInsideは「内側に保つ」という処理の目的を表しています。
仮引数は、呼び出し元から値を受け取る変数です。英語ではparameterと呼びます。今回はint value, int limitを宣言し、関数内のローカル変数として使います。
return文で値を返す
関数内では、上から順に条件を調べます。value < 0なら、return 0;で0を返して終了します。次にvalue > limitなら、return limit;で上限値を返して終了します。どちらにも当てはまらなければ範囲内なので、最後のreturn value;で受け取った値をそのまま返します。
returnを実行すると、その時点で関数の処理は終わります。以降の文は実行せず、呼び出し元へ戻ります。
関数の配置場所
C++のコンパイラは、ファイルを上から順に読みます。そのため、KeepInsideを呼び出すWinMainより前に関数を定義します。呼び出しを読む時点で、コンパイラが引数と戻り値の型を確認できる配置です。
関数の呼び出しと戻り値の代入(値渡し)
定義した関数を、WinMainのメインループから呼び出します。
青いロボットの座標を制限していた四つのif文を、次の2行に置き換えてください。
x = KeepInside(x, 640 - 48);
y = KeepInside(y, 480 - 48);実引数と仮引数
呼び出し元から渡す値を実引数と呼びます。英語ではargumentです。今回の実引数はxや640 - 48で、受け取る側の仮引数がvalueとlimitです。
KeepInside(x, 640 - 48)の第1引数はxの値で、受け取る仮引数はvalueです。第2引数の計算結果592は、limitへ渡します。呼び出しと定義の引数は、同じ順序で対応しています。
値渡し(Pass by Value)と代入の必要性
1行目のx = KeepInside(x, 640 - 48);を、変数を箱に例えてたどります。xの値が594なら、その数値だけを関数側のvalueへコピーします。valueとxは、別々の箱です。
KeepInsideは594が上限を超えていると判定し、592を返します。呼び出し元では、その戻り値を=でxに書き込みます。ここで初めて、xの値が594から592に変わります。
[呼び出し元] [KeepInside関数]
箱 x (594) ──(値 594 をコピー)───> 仮引数 value の箱 (594)
▲ │ 判定・計算
│ ▼
└──────(戻り値 592 を代入)────── return 592今回のint型の引数は、数値をコピーする値渡しです。仮引数valueと、呼び出し元のxは別の変数なので、関数を呼ぶだけではxは変わりません。
KeepInside(x, 640 - 48);とだけ書くと、戻り値は捨てられます。計算後の座標をゲームに反映するには、必ず=で戻り値をxへ代入してください。
ステップイン(F11)による実行フローの追跡
デバッガーで、処理がWinMainからKeepInsideへ移り、呼び出し元へ戻る様子を観察してください。
関数の呼び出しから復帰までの観察
x = KeepInside(x, 640 - 48);の行にF9でブレークポイントを設定し、F5でデバッグ実行してください。その行で停止したら、F11でステップインします。実行カーソルがKeepInsideの先頭へ移るので、「ローカル」ウィンドウでvalueとlimitの値を確認してください。
続いて、F10でステップオーバーを数回行います。ifで条件を判定し、returnを実行してからWinMainの呼び出し元へ戻る流れを確認してください。
確認を終えたらブレークポイントを解除してください。
入力値と戻り値の対応検証
関数の判定が正しいか、次の三つの入力で確認してください。
| 呼び出し | 判定 | 戻り値 |
|---|---|---|
KeepInside(-5, 592) | 0未満 | 0 |
KeepInside(200, 592) | 範囲内 | 200 |
KeepInside(600, 592) | 上限を超える | 592 |
ビルドして実行し、青いロボットがこれまでどおり四方の画面端で止まることを確認してください。
- 関数の戻り値の型、関数名、仮引数、
return文の文法役割を説明できる - 引数が値渡し(コピー)であるため、戻り値を変数に代入し直す必要がある理由を説明できる
- F11(ステップイン)を用いて関数の内部へステップ実行できる