横スクロールゲームを作る-3
前回は画像ファイルの読み込みと,走るアニメーションの実装を行いました
processing-p5.hateblo.jp
今回は障害物が右から左へと流れるようにし,当たり判定も実装しましょう
以下が今回作るアプリです
trexがサボテンに衝突するとその時点でゲームオーバーとなります
今回のコードは以下となります
PImage rexJmpImg, rexRunImg1, rexRunImg2, rexHitImg, cactusImg; float gravity = 1; // 下向きの重力加速度 Player rex; Hurdle cactus; Observer observer; boolean hit; void setup(){ size(600,200); background(255); frameRate(60); imageMode(CENTER); rectMode(CENTER); rexJmpImg = loadImage("trex.png"); rexRunImg1 = loadImage("trex_run1.png"); rexRunImg2 = loadImage("trex_run2.png"); rexHitImg = loadImage("trex_hit.png"); cactusImg = loadImage("cactus.png"); rex = new Player(); cactus = new Hurdle(); observer = new Observer(rex, cactus); } void draw(){ background(255); line(0, 0.8*height, width, 0.8*height); cactus.update(); rex.update(); observer.update(); } void keyPressed(){ if(key==' '){rex.jump();} } class Player{ PImage rexImg; float px,py; float vy; float stdHeight = 0.7*height; boolean isGrounded; Player(){ px = 50; py = stdHeight; vy = 0; isGrounded = true; } void update(){ if(hit){image(rexHitImg, px, py, 55, 55);} else{ vy += gravity; py += vy; if(py>=stdHeight){ isGrounded = true; py = stdHeight; } if(isGrounded){ if(frameCount%10>0 && frameCount%10<6) rexImg = rexRunImg1; else rexImg = rexRunImg2; } else rexImg = rexJmpImg; image(rexImg, px, py, 60, 60); } } void jump(){ if(isGrounded){ isGrounded = false; vy = -14; } } } class Hurdle{ float px, py; float vx; Hurdle(){ px = width; py = 0.7*height; vx = -10; } void update(){ if(!hit){ px+= vx; if(px<0) px = width; } image(cactusImg, px, py, 40, 70); } } class Observer{ Player rex; Hurdle cactus; Observer(Player _rex, Hurdle _cactus){ rex = _rex; cactus = _cactus; } void update(){ if(dist(rex.px, rex.py, cactus.px, cactus.py)<40) hit = true; else hit = false; } }
サボテンのpngファイルと,ゲームオーバー時のtrexの画像は以下のものを使用しています
これらのファイルについても,dataフォルダ下に保存します
画像ファイルの取り扱いについては,前回の記事を参考にしてください
上のコードを詳しくみていきます
障害物はHurdleクラスとして定義しています
Hurdleクラスには,位置を指定する変数pxと, 速度を指定する変数vxがあり,pxの初期値はキャンバスの幅widthとなっています
PImage rexJmpImg, rexRunImg1, rexRunImg2, rexHitImg, cactusImg; cactusImg = loadImage("cactus.png"); void setup(){ cactusImg = loadImage("cactus.png"); cactus = new Hurdle(); } void draw(){ cactus.update(); } class Hurdle{ float px, py; float vx; Hurdle(){ px = width; py = 0.7*height; vx = -10; } void update(){ if(!hit){ px+= vx; if(px<0) px = width; // サボテンが画像から見切れた場合はpxを初期値にもどす } image(cactusImg, px, py, 40, 70); } }
各フレームごとにpx+=vxを行うので,pxは時間経過とともに10ずつ小さい値をとることになります
そしてimage()関数を用いてサボテンの画像を描画します
pxはだんだん小さな値をとるので,サボテンの画像は右から左へと流れることがわかります
次に,サボテンとtrexが衝突したことを監視するクラスとしてObserverを用意します
boolean hit; class Observer{ Player rex; Hurdle cactus; Observer(Player _rex, Hurdle _cactus){ rex = _rex; cactus = _cactus; } void update(){ if(dist(rex.px, rex.py, cactus.px, cactus.py)<40) hit = true; else hit = false; } }
Observerクラスのコンストラクタ引数にはプレイヤーと障害物のオブジェクトを渡しています
プレイヤーと障害物の中心距離が40pxより近くなったとき,衝突したとみなしグローバル変数hitにtrueを格納します
Playerクラス内では,グローバル変数hitがtrueのときに衝突時の画像が表示されるように設定しています
Player(){ void update(){ if(hit){ image(rexHitImg, px, py, 55, 55); // 衝突時に衝突の画像を表示する } else{ // 衝突するまでは以下の処理を行う vy += gravity; py += vy; if(py>=stdHeight){ isGrounded = true; py = stdHeight; } } }