27:画像を動かす

◆PROCESSINGで始めるゲーム作りとコンピュータ

Thunder 27:画像を動かす

今日の話題

 

06:画像を動かす

シューティングゲームではUFOや飛行機が、そしてパズルゲームではカラフルな珠やカードが、画面を動き回ります。

前回の記事で画像を画面に表示させることができるようになりましたが、今回は画像を動かす方法について考えてみたいと思います。

前回のSample03-2では、描画処理関数(draw)の中で image( rocket, 0, 0 ); とプログラムする事で、ロケットの画像を画面左上隅に表示しました。

imageSample(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

image()命令は第1引数にPImage型の変数を、第2引数と第3引数に画像を表示する座標を与えます。

実行結果ウィンドウの左上隅(0,0)に表示されていた画像が、次の瞬間(100,100)の位置に表示されたなら、これは(0,0)から(100,100)に向かって画像が動いた事になります。

つまり、「画像が動く」とは「画像を表示する座標が動く」事なのです。

試しに、image()命令に与える座標を(0,0)から(100,100)に変更してみましょう。

Sample03-4

<出力結果>

imageSample3(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

ロケットの表示位置が変わりました。
imageSample4
image( rocket, 0, 0 ); と書かれていた命令を image( rocket, 100, 100 ); にする事で、画面左上隅から幅100ピクセル、高さ100ピクセルの位置にロケットが表示されました。

でも残念ながら、これでは「動いた」という感じはしませんよね。なぜならロケットが、最初から(100, 100)の位置に表示されてしまうからです。

「動いた」というからには(0,0)から(100,100)に向かって徐々に移動して欲しい所です。

これを実現するために必要となる知識が3つほどあります。

  • 画像の描画原点(アンカーポイント)について
  • 描画領域サイズと画像サイズについて
  • 描画処理関数の動作について

1つ1つ見ていきましょう。
.

画像の描画原点(アンカーポイント)について

描画原点の事をアンカーポイントとも呼びます。「どこを基準に画像を配置するのか」という「点」の事です。

言葉だけではわかりにくいですよね。以下の図を見てください。

アンカーポイント1(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 hector gomez)

これが描画原点です。PROCESSINGにおける描画原点は、初期状態では画像の左上隅となっています。

image()命令では、この描画原点を、指定された画面座標に重なるように配置していたのです。

アンカーポイント2(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

描画原点は、必ず画像の左上隅であるとは限りません。PROCESSINGでは、描画原点の位置を変更する事が可能だからです。

ただし何も指定しなければ、左上隅になります。左上隅が初期値なのです。

また描画原点が画像のどこになるのかは、ゲームを作るフレームワークによって異なります。例えば Cocos2d-x では画像の中央が描画原点の初期値です。

余談ですが・・・コンピュータ業界では、「初期値や規定値」の事を「デフォルト(default)」値と呼びます。

コンピュータ業界ではこの表現を良く使いますので、IT技術者と話す機会がある人は覚えておくと良いかもしれません(笑)。
.

描画領域サイズと画像サイズについて

実行結果ウィンドウの描画領域サイズは size()命令で指定します。

このときsize()命令で指定した値(画面の幅と高さ)は、PROCESSINGが用意しているグローバル変数に自動的に記憶される仕組みとなっています。

プログラマー(私達)が、「えーと、画面サイズをいくつに設定したっけ?」と覚えておかなくても(笑)、プログラムの中でいつでも知ることができるのです。

具体的には width という名前の変数に描画領域の幅が、height という名前の変数に描画領域の高さが記憶されます。widthとheightはPROCESSINGが自動的に用意してくれる変数ですので、私達がプログラムする必要はありません。

例えば size( 300, 400 );  と指定すると、 width に 300 が、height に 400 が自動的に記憶されます。

この width と height はプログラム中のどこからでも参照できますので、いつでも描画領域の大きさを知ることができます。便利ですね。

screenSizeまたPImage型の変数に読み込んだ画像についても、その幅と高さが自動的に記憶される仕組みとなっています。

読み込んだ画像の幅と高さは、それぞれ PImage型変数名.width と PImage型変数名.height に記憶されます。
imageSize
ロケット画像を読み込んだ rocket 変数なら、rocket.width に画像の幅が、rocket.height に画像の高さが記憶されるのです。

これも私達が意識する必要はありません。PImage型の変数に loadImage() 命令で画像データを読み込むと、PROCESSINGにより自動的に処理されます。

画像を動かす際には、画面サイズや画像サイズを意識する事が良くありますので、この2つはぜひ覚えておいてください。
.

描画処理関数の動作について

描画処理関数(draw)は1回目の処理が終わると1/60秒後に2回目が、2回目の処理が終わると1/60秒後に3回目が・・・というように、1/60秒間隔で何度も繰り返し呼びだされます。

図にすると以下の様な感じです。

frame説明
最初に描画処理関数が呼び出されてから、次に呼び出されるまでの間が1/60秒になります。

上図の太い青矢印部分は「空き時間」です。何も処理が行われない時間調整部分になります。こうする事で、なるべく正確に(1/60秒毎に) draw() 関数が呼び出されるように工夫されているのです。

よく勘違いするのですが、下図のようではありません。

frame説明2
この呼び出し間隔(1/60秒)の事を「フレーム」と呼びます。

このフレームごと(draw関数が呼び出されるごと)に、ロケットの位置を動かせば、ロケットが徐々に移動するように見える筈です。

frame移動(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

次のサンプルを入力して動かしてみてください。

Sample03-5

<出力結果>

sample03-5(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

ロケットが画面左下から斜め上に向かって移動した筈です。

下記はサンプルプログラムをWeb画面用に書き直したものです。わかり易いようにマウスクリックで動くようにしてあります。マウスをクリックするとロケットが飛んで行きます。

スマートフォンで御覧頂いている方は、画面にタッチしていただければ動作します。

pointY = height – rocket.height;
という箇所で、画面の高さ(height)からロケット画像の高さ(rocket.height)を引いた値を、ロケットのY座標(pointY)に指定しています。

sample03-5説明(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

これは図にすると上図のようになります。だから最初にロケットが画面の左下隅に描かれていたのです。

draw()関数の最後に
pointX = pointX + 5;
pointY = pointY – 5;
として、ロケットの描画位置を変更している処理がある事に注目してください。

この処理で、draw()関数が実行されるごとに、ロケットの座標を変更しています。draw()関数が呼び出される毎にロケットの座標が変化するので、ロケットが右斜め上に移動するわけです。

sample03-5説明2(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

ところで、もしも draw()関数の処理が 1/60秒以内に終わらなかった場合は、どうなるでしょうか?・・・。答えは下図のようになります。

frame説明3
draw() 関数の処理が1回飛ばされてしまうわけですね。このような現象を「処理落ち」とか「コマ落ち」と呼びます。

砲弾をたくさんばらまく「弾幕系」のシューティングゲームなどで、弾や敵がたくさん出現すると処理落ちが発生して、敵や弾の動きがカクカクする事があります。

本来期待したタイミングで画面に弾を描くことができないために、動きがぎこちなくなるわけです。

PROCESSINGにおいても draw() 関数内で時間がかかる処理を行うと処理落ちが発生するので、タイミングがシビアなアプリケーションを作る場合は注意が必要です。

 

07:ロケットの移動範囲を制御する

ロケットが画面の外まで飛んで行くのもよいのですが、ロケットが画面の外に飛び出さないようにするには、どうすれば良いでしょうか?。

ロケットが画面の外に飛び出さないようにするためには、ロケットの描画位置が画面の端に達したか判定すれば良い事になります。

画面の4隅毎に考えていきましょう。

.
左上隅の場合
まずロケットが左上にある場合です。この場合、ロケットが下図の青矢印のように画面の上や左に出てしまわないように制御する事になります。

rocket_lt(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

画像を表示するX座標(=pointX:横位置)が0以下にならないようにすれば、ロケットは画面の左端より左にはいかない筈です。

また、画像を表示するY座標(=pointY:縦位置)が0以下にならないようにすれば、ロケットは画面の上端より上にはいかない筈です。

.
左下隅の場合
次にロケットが左下にある場合を考えます。この場合は、ロケットが下図の青矢印のように画面の下や左に出てしまわないように制御する事になります。

rocket_lb(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

画像を表示するX座標(=pointX:横位置)が0以下にならないようにすれば、ロケットは画面の左端より左にはいかない筈です。

また画像を表示するY座標(=pointY:縦位置)が画面の高さ(height)からロケットの高さ(rocket.height)を引いた値以上にならないようにすれば、ロケットは画面の下端より下にはいかない筈です。

.
右上隅の場合
右上の場合はどうでしょうか?。

rocket_rt(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

画像を表示するX座標(=pointX:横位置)が画面の幅(width)からロケットの幅(rocket.width)を引いた値以上にならないようにすれば、ロケットは画面の右端より右にはいかない筈です。

また、画像を表示するY座標(=pointY:縦位置)が0以下にならないようにすれば、ロケットは画面の上端より上にはいかない筈です。

.
右下隅の場合
最後は右下です。

rocket_rb(画像URL:GAHAG | 著作権フリー写真・イラスト素材集 様 LoganArt 、hector gomez)

画像を表示するX座標(=pointX:横位置)が画面の幅(width)からロケットの幅(rocket.width)を引いた値以上にならないようにすれば、ロケットは画面の右端より右にはいかない筈です。

また画像を表示するY座標(=pointY:縦位置)が画面の高さ(height)からロケットの高さ(rocket.height)を引いた値以上にならないようにすれば、ロケットは画面の下端より下にはいかない筈です。

整理すると以下のようになります。

  • pointXは0以下にならない事
  • pointYは0以下にならない事
  • pointXはwidth – rocket.width 以上にならない事
  • pointYはheight – rocket.height 以上にならない事

4隅の座標
これらの条件を組み合わせて、ロケットが画面の4隅から出て行かないようにします。

Sample03-6

これでロケットが画面の外に出ていくことは無くなります。

このように、ゲームでは画像サイズと画面サイズを使って、キャラクターの移動範囲を制御する事がよくあります。

Forest今日の言葉

・描画原点

アンカーポイントとも言います。どこを基準にして画像を描画するのかという点です。PROCESSINGでは左上隅が初期値となります。

・デフォルト値

初期値や規定値の事です。省略して「デフォ」なんていう人もいますね。法令用語のデフォルト(債務不履行)とは全く関係がありません(笑)。

・フレーム

一般的には画面を更新する間隔の事を指します。デフォルト値は1/60秒です。PROCESSINGでは、描画処理関数が呼び出されるタイミングの事を指します。

・処理落ち

描画処理関数の処理が重たくて1フレーム内に収まらず、期待した間隔で呼び出されなくなる現象です。アクションゲームやシューティングゲームを作る場合は、処理落ち対策と呼ばれる特殊処理が組み込まれるのが通例となっているようです。

 

scroll今日の文法

・なし

 

 今日のまとめ

  • 画像を動かすにはフレームごとに表示座標を変更する
  • 画面の幅と高さは width と height に記憶される
  • 画像の幅と高さは PImage型変数.width と PImage型変数.height に記憶される

28:マウスの入力を処理する へ進む
26:画像を表示する へ戻る
目次へ戻る


本ページで利用している特に参照元記載のないアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。