9.背景の描画と関数と参照[関数の説明3] |
|
このページでは、背景の描画と関数の参照渡しを解説します。
参照は簡単で便利ですが、あまり使うのはよくないとされています。
その理由を私なりに解説します。
このページでは、関数を作るときだけ、参照を使うことにします。
|
|
//移動範囲を上下左右で設定
#define MINI_MOVE_PLAYER_RANGE_X 160
#define MINI_MOVE_PLAYER_RANGE_Y 0
#define MAX_MOVE_PLAYER_RANGE_X 480
#define MAX_MOVE_PLAYER_RANGE_Y 480
//背景を入れる変数
int nBackGroundGraph=LoadGraph("BackGroundGraph.png");
//移動範囲を中央に変更
if(player_position_x>MAX_MOVE_PLAYER_RANGE_X-PLAYER_SIZE_HALF)
player_position_x=MAX_MOVE_PLAYER_RANGE_X-PLAYER_SIZE_HALF;
if(player_position_x<MINI_MOVE_PLAYER_RANGE_X+PLAYER_SIZE_HALF)
player_position_x=MINI_MOVE_PLAYER_RANGE_X+PLAYER_SIZE_HALF;
if(player_position_y>MAX_MOVE_PLAYER_RANGE_Y-PLAYER_SIZE_HALF)
player_position_y=MAX_MOVE_PLAYER_RANGE_Y-PLAYER_SIZE_HALF;
if(player_position_y<MINI_MOVE_PLAYER_RANGE_Y+PLAYER_SIZE_HALF)
player_position_y=MINI_MOVE_PLAYER_RANGE_Y+PLAYER_SIZE_HALF;
//背景の画像を移動範囲にあわせて表示
DrawGraph(MINI_MOVE_PLAYER_RANGE_X,
MINI_MOVE_PLAYER_RANGE_Y, nBackGroundGraph,TRUE); |
|
移動範囲をシューティングゲームのように縦長に変更しました。
横X座標(160〜480)の320ドット
縦Y座標(0〜480)の480ドット
背景もそれにあわせて、真ん中に描画
|
背景を描画するプログラム サンプルプログラムソース |
|
//ライブラリ宣言
#include "DxLib.h"
//定数の宣言
#define PLAYER_SIZE 50
#define PLAYER_SIZE_HALF PLAYER_SIZE/2
#define PLAYER_MOVE_SPEED 5
#define MINI_MOVE_PLAYER_RANGE_X 160
#define MINI_MOVE_PLAYER_RANGE_Y 0
#define MAX_MOVE_PLAYER_RANGE_X 480
#define MAX_MOVE_PLAYER_RANGE_Y 480
//自作関数 プロトタイプ宣言
int InputAndChangeX(int player_position_x);
//WinMain関数
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// DXライブラリの設定
// SetOutApplicationLogValidFlag(TRUE);
SetGraphMode(640,480,16);
ChangeWindowMode( TRUE ) ;
if( DxLib_Init()==-1) return-1;
// SetMouseDispFlag( TRUE ) ;
SetDrawScreen(DX_SCREEN_BACK);
//変数の宣言と初期化
int player_position_x=320;
int player_position_y=240;
int player_graph=LoadGraph("Player.png");
int joypad_state=0;
int nBackGroundGraph=LoadGraph("BackGroundGraph.png");
//メインループ
while(ProcessMessage()==0)
{
//ジョイスティック&キー入力
player_position_x=InputAndChangeX(player_position_x);
joypad_state=GetJoypadInputState(DX_INPUT_KEY_PAD1);
if((joypad_state&PAD_INPUT_UP)!=0)player_position_y-=PLAYER_MOVE_SPEED;
if((joypad_state&PAD_INPUT_DOWN)!=0)player_position_y+=PLAYER_MOVE_SPEED;
if(player_position_x>MAX_MOVE_PLAYER_RANGE_X-PLAYER_SIZE_HALF)
player_position_x=MAX_MOVE_PLAYER_RANGE_X-PLAYER_SIZE_HALF;
if(player_position_x<MINI_MOVE_PLAYER_RANGE_X+PLAYER_SIZE_HALF)
player_position_x=MINI_MOVE_PLAYER_RANGE_X+PLAYER_SIZE_HALF;
if(player_position_y>MAX_MOVE_PLAYER_RANGE_Y-PLAYER_SIZE_HALF)
player_position_y=MAX_MOVE_PLAYER_RANGE_Y-PLAYER_SIZE_HALF;
if(player_position_y<MINI_MOVE_PLAYER_RANGE_Y+PLAYER_SIZE_HALF)
player_position_y=MINI_MOVE_PLAYER_RANGE_Y+PLAYER_SIZE_HALF;
//キャラの描画
ClsDrawScreen() ;
DrawGraph(MINI_MOVE_PLAYER_RANGE_X,
MINI_MOVE_PLAYER_RANGE_Y, nBackGroundGraph,TRUE);
DrawGraph(player_position_x-PLAYER_SIZE_HALF,
player_position_y-PLAYER_SIZE_HALF, player_graph,TRUE);
ScreenFlip();
// ESCキーが押されたらループから抜ける
if(CheckHitKey(KEY_INPUT_ESCAPE)==TRUE)break;
}
//終了処理
DxLib_End();
return 0;
}
//X座標の移動 ジョイスティック&キー入力
int InputAndChangeX(int player_position_x)
{
int joypad_state=0;
//ジョイスティック&キー入力
joypad_state=GetJoypadInputState(DX_INPUT_KEY_PAD1);
if((joypad_state&PAD_INPUT_LEFT)!=0)player_position_x-=PLAYER_MOVE_SPEED;
if((joypad_state&PAD_INPUT_RIGHT)!=0)player_position_x+=PLAYER_MOVE_SPEED;
return player_position_x ;
}
|
参照 流し読みゾーン |
参照を理解する必要はありません。
ただ、関数とセットで使うことがありますので、適当に理解しててください。
簡単に言うと、変数に別の呼び名をつけるって感じです。
私の猫の名前は、ミッキーです。
しかし、母はミーちゃんと呼びます。
違う名前ですが、同じ猫を意味しています。
同じ変数を違う名前でも呼び出す(参照する)ことができます。 |
int& a=b ;
で宣言できます。
int b=1;
int& a=b ;
これでaでもbでもbの内容を変更したり呼び出せるようになります。
注意
宣言と同時に初期値を入れなければなりません。 |
上の説明で、参照を使わない理由がわかるのではないでしょうか?
同一プログラム上に、同じ意味を持つ違う名前の変数がゴロゴロ転がっていると、ゾッとするのではないでしょうか。
たとえば、同じホームページの文章中に、自分の猫をミッキーと呼んだりミーちゃんと呼んだりすれば、猫が2匹いてるのかな?とかおもうはずです。実際は一匹です。
理解不能のプログラムになる可能性があります。
参照は使わないようにしましょう。
|
関数と参照 |
参照は使わないようにしましょうと言いながら、参照を使います。
参照を使うことで、ポインタを使わずに関数外の変数とのやり取りができます。
参照は関数との相性がよいので、関数を使うときだけ使います。
グローバル変数と関数の組み合わせを使うぐらいなら、
参照と関数の組み合わせを使うほうが相当ましです。
|
関数は、戻り値をひとつしか持って帰れません。
その上、関数上の変数は関数終了と同時に、破棄されます。
二つ以上結果を残すにはどうすればいいでしょうか?
その時、参照関数を使います。 |
関数の引数を参照にするだけです。
X座標とY座標を変更する関数は戻り値が二ついります。
直接XとYを書き換えればいいのですが、それは関数にはできません。
これは、意地悪でこんなことをしているわけではありません。
カプセル化指向がある(汎用関数を作る)ためです。
そこで
int MovePlaiyer(int& x,int& y)
とするとどうなるでしょう。
前に( )内は、引数処理だと説明しました。初期値は引数です。
int& xは、参照変数を宣言しています。
int& x=引数1;
となります。
引数1がplayer_position_xだとすると、xはplayer_position_xの別の呼び名になります。
player_position_xの内容をxで変更できるようになります。
便利すぎて「使うな!」と言われても使ってしまうでしょう。
注意
参照は、C++で新しくできた文法です。(それまではポインタ値渡し)
[ 例 ]
main()
{
int player_position_x;
MovePlaiyer(player_position_x,y);
}
int MovePlaiyer(int& player_position_x,int& y)
普通同じ名前の変数でも、関数内と関数外では別の変数です。(ローカル変数)
しかし上のように書くと、
int& player_position_x=player_position_x ; //後ろのplayer_position_xは引数1
という意味になり、同じ変数名で関数外の変数に直接アクセスできます。
|
UpdatePlayerPosition()参照関数を作ってみましょう。 |
プレイヤー位置の更新をする関数です。
前回、左右の動きを関数にまとめました。
今回は、上下左右のプレイヤーの動きを処理する関数を作ります。
前のページに書いた関数とは比べ物にならないぐらい実用的な関数になります。 |
3個の引数を使います。
自機位置X、自機位置Y、移動スピード
参照2個・定数1個です。
int UpdatePlayerPosition(int& player_position_x,int& player_position_y,
int PLAYER_MOVE_SPEED);
関数内で使うすべての数字を取得していますので、まったく別のプログラムで使用できます。
ここで問題があります。
PLAYER_MOVE_SPEEDがグローバルなので、宣言するとダブります。
同じ名前で宣言できません。下記に変更します。
int UpdatePlayerPosition(int& player_position_x,int& player_position_y,
int MOVE_SPEED);
|
int UpdatePlayerPosition(int& player_position_x,int& player_position_y,
int MOVE_SPEED)
{
////ジョイスティック&キー入力
int joypad_state=GetJoypadInputState(DX_INPUT_KEY_PAD1);
if((joypad_state&PAD_INPUT_UP)!=0) player_position_y-=MOVE_SPEED;
if((joypad_state&PAD_INPUT_DOWN)!=0) player_position_y+=MOVE_SPEED;
if((joypad_state&PAD_INPUT_LEFT)!=0) player_position_x-=MOVE_SPEED;
if((joypad_state&PAD_INPUT_RIGHT)!=0) player_position_x+=MOVE_SPEED;
//戻り値がいらない時は0を返すのが一般的
return 0;
}
|
|
最後にvoid について説明します。
void を使う必要はありませんが、他人のソースによく出てくるので、少し解説します。
void も型宣言です。戻り値のない型です。
UpdatePlayerPosition()関数には戻り値がありませんので、void型でも大丈夫です。
voidが有効ではないコンパイラがあるらしいので、ここではintを使っていきます。
ここでは説明だけにします。intをvoidに変えるだけです。
void UpdatePlayerPosition(int& player_position_x,int& player_position_y,
MOVE_SPEED)
|
簡単なリファレンスも作りましょう
//int UpdatePlayerPosition(int& PositionX,int& PositionY,int MOVE_SPEED);
//[ライブラリ] #include "DxLib.h"
//[引数] X座標(参照) ,Y座標(参照) ,1ターンで動くドット
//[戻り値] なし
//]解説] ジョイスティック入力によってX座標Y座標を更新する
|
背景の描画と関数と参照 C++サンプルプログラムソース |
//ライブラリ宣言
#include "DxLib.h"
//定数の宣言
#define PLAYER_SIZE 50
#define PLAYER_SIZE_HALF PLAYER_SIZE/2
#define PLAYER_MOVE_SPEED 5
#define MINI_MOVE_PLAYER_RANGE_X 160
#define MINI_MOVE_PLAYER_RANGE_Y 0
#define MAX_MOVE_PLAYER_RANGE_X 480
#define MAX_MOVE_PLAYER_RANGE_Y 480
//自作関数 プロトタイプ宣言
int UpdatePlayerPosition(int& player_position_x,int& player_position_y,
int MOVE_SPEED);
//WinMain関数
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// DXライブラリの設定
// SetOutApplicationLogValidFlag(TRUE);
//SetGraphMode(640,480,16);
ChangeWindowMode( TRUE ) ;
if( DxLib_Init()==-1) return-1;
// SetMouseDispFlag( TRUE ) ;
SetDrawScreen(DX_SCREEN_BACK);
//変数の宣言と初期化
int player_position_x=320;
int player_position_y=240;
int player_graph=LoadGraph("Player.png");
// int joypad_state=0;
int nBackGroundGraph=LoadGraph("BackGroundGraph.png");
//メインループ
while(ProcessMessage()==0)
{
//ジョイスティック&キー入力
UpdatePlayerPosition(player_position_x,player_position_y,PLAYER_MOVE_SPEED);
if(player_position_x>MAX_MOVE_PLAYER_RANGE_X-PLAYER_SIZE_HALF)
player_position_x=MAX_MOVE_PLAYER_RANGE_X-PLAYER_SIZE_HALF;
if(player_position_x<MINI_MOVE_PLAYER_RANGE_X+PLAYER_SIZE_HALF)
player_position_x=MINI_MOVE_PLAYER_RANGE_X+PLAYER_SIZE_HALF;
if(player_position_y>MAX_MOVE_PLAYER_RANGE_Y-PLAYER_SIZE_HALF)
player_position_y=MAX_MOVE_PLAYER_RANGE_Y-PLAYER_SIZE_HALF;
if(player_position_y<MINI_MOVE_PLAYER_RANGE_Y+PLAYER_SIZE_HALF)
player_position_y=MINI_MOVE_PLAYER_RANGE_Y+PLAYER_SIZE_HALF;
//キャラの描画
ClsDrawScreen() ;
DrawGraph(MINI_MOVE_PLAYER_RANGE_X,
MINI_MOVE_PLAYER_RANGE_Y, nBackGroundGraph,TRUE);
DrawGraph(player_position_x-PLAYER_SIZE_HALF,
player_position_y-PLAYER_SIZE_HALF, player_graph,TRUE);
ScreenFlip();
// ESCキーが押されたらループから抜ける
if(CheckHitKey(KEY_INPUT_ESCAPE)==TRUE)break;
}
//終了処理
DxLib_End();
return 0;
}
//ジョイスティック&キー入力
int UpdatePlayerPosition(int& player_position_x,int& player_position_y,
int MOVE_SPEED)
{
//int UpdatePlayerPosition(int& PositionX,int& PositionY,int MOVE_SPEED);
//[ライブラリ] #include "DxLib.h"
//[引数] X座標(参照) ,Y座標(参照) ,1ターンで動くドット
//[戻り値] なし
//]解説] ジョイスティック入力によってX座標Y座標を更新する
int joypad_state=GetJoypadInputState(DX_INPUT_KEY_PAD1);
if((joypad_state&PAD_INPUT_UP)!=0) player_position_y-=MOVE_SPEED;
if((joypad_state&PAD_INPUT_DOWN)!=0) player_position_y+=MOVE_SPEED;
if((joypad_state&PAD_INPUT_LEFT)!=0) player_position_x-=MOVE_SPEED;
if((joypad_state&PAD_INPUT_RIGHT)!=0) player_position_x+=MOVE_SPEED;
return 0;
}
|
追記 (ラッピング・ブラックボックス・カプセル化) |
// int joypad_state=0;
が必要なくなりました。
参照変数にも参照だとわかるように変数名を変えたほうがいいのかもしれませんが、
関数ないだけで使うのであればそのままの名前でいいのではないでしょうか?
なんといってもそのほうがラクです。
この関数は、プログラムから切り離されています。
ですので、この関数にバグがない限り、プログラム変更時にバグが出ても、
この関数の中は調べる必要がなくなります。
(ラッピングやブラックボックス・カプセル化とも呼ばれます)
メインループがすっきりしましたね。
また、プログラムを組む意欲がわいてきます。
関数を作ると、関数を道具として使えるようになります。
この先クラスを覚えると、部品も使えるようになります。 |