マリオみたいなゲームを作ろうその5(p5.js)

p5.js

今回は画面を自機を中心にカメラをスクロールさせます。
割と簡単に実装できますが、今回説明するものが多いです。
今回のやったものはおそらくUnityで再現しても使う機会はなさそうですが、覚えておいて損はないと思います。

※自機が横からブロックにぶつかったら停止するように更新しました。
自機にvx、futurex座標を追加しました。

sprite.jsはこちら。

class Mine {
  constructor(x, y, vy, vx, gridSize) {
    this.x = x;
    this.y = y;
    this.w = gridSize;
    this.h = gridSize;
    this.vy = vy;
    this.vx = vx;
    this.isJumping = false;
    this.isAlive = true;
    this.speed = 2;
    this.direction = 1;
  }

  show() {
    fill(255, 0, 0);
    rect(this.x, this.y, this.w, this.h);
  }

  move() {
    if (keyIsDown(LEFT_ARROW)) {
      this.x -= 5;
    }

    // 右の矢印キーが押されている場合
    if (keyIsDown(RIGHT_ARROW)) {
      this.x += 5;
    }
  }
}

// 敵クラス
class Enemy {
  constructor(x, y, vy, gridSize) {
    this.x = x;
    this.y = y;
    this.w = gridSize;
    this.h = gridSize;
    this.vy = vy;
    this.isAlive = true;
    this.speed = 2;
    this.direction = 1;
  }

  // 敵を描画
  show() {
    fill(0, 0, 255);
    rect(this.x, this.y, this.w, this.h);
  }
  
  move() {
    this.x += this.speed * this.direction;
    
    if (this.x <= 0 || this.x + this.w >= width) {
      this.direction *= -1;
    }
  }
}

class Block {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }
  
  show() {
    fill(139, 69, 19);
    rect(this.x, this.y, this.w, this.h);
  }
}

sketch.jsはこちら。

let player;
let blocks = [];
let enemies = [];
let cameraX = 0; // カメラのX座標

let gridSize = 40;

function setup() {
  createCanvas(800, 400);

  // プレイヤー初期設定
  player = new Mine(50, 300, 0, 0, gridSize);

  // ブロック(地面を含む)を配置
  blocks = [
    new Block(200, 250, 100, 20),// 通常ブロック
    new Block(300, 200, 100, 20),// 通常ブロック
    new Block(600, 300, 20, 100),// 通常ブロック
    new Block(500, 200, 100, 20),// 通常ブロック
    new Block(0, height - 50, width, 50),// 地面
  ];
  
  // 敵を追加
  enemies = [
    new Enemy(300, 210, 0, gridSize),
    new Enemy(500, height - 90, 0, gridSize),
  ];
}

function draw() {
  background(135, 206, 250);

  // カメラ位置をプレイヤーに合わせて更新
  cameraX = constrain(player.x - width / 2, 0, 2000 - width);
  
  // 描画をカメラ位置に応じてシフト
  push();
  translate(-cameraX, 0);
  
  if (player.isAlive) {
    player.show();
    player.move();
  }
  
  
  player.vy += 1; // 重力
  let futureY = player.y + player.vy; // 次のフレームのY位置
  let futureX = player.x + player.vx;
  
  // 衝突判定
  for (let block of blocks) {
    if (
      player.x < block.x + block.w && // 水平方向の重なり
      player.x + player.w > block.x && // 水平方向の重なり
      player.y + player.h <= block.y && // プレイヤーがブロックの上面にいる
      futureY + player.h >= block.y // 次のフレームでブロックを通過しない
    ) {
      futureY = block.y - player.h; // プレイヤーをブロックの上に移動
      player.vy = 0; // 縦方向の速度をリセット
      player.isJumping = false; // ジャンプ状態を解除
    }
    
    if (
      futureX + player.w > block.x &&
      futureX < block.x + block.w &&
      player.y + player.h > block.y &&
      player.y < block.y + block.h
    ) {
      // プレイヤーが右から衝突
      if (player.x < block.x) {
        futureX = block.x - player.w;
        player.vx = 0;
      }
      // プレイヤーが左から衝突
      else if (player.x > block.x) {
        futureX = block.x + block.w;
        player.vx = 0;
      }
    }
  }
  
  // プレイヤー位置を更新
  player.y = futureY;
  player.x = futureX;
  
  // 敵との衝突判定
  for (let enemy of enemies) {
    if (
      enemy.isAlive && // 敵が生存中
      player.x < enemy.x + enemy.w && // 水平方向の重なり
      player.x + player.w > enemy.x && // 水平方向の重なり
      player.y + player.h <= enemy.y && // プレイヤーが敵の上面にいる
      player.y + player.h + player.vy >= enemy.y // 次のフレームで敵を踏む
    ) {
      // 敵を倒す
      enemy.isAlive = false;
      player.vy = -10; // プレイヤーを跳ね返す
    } else if (
      enemy.isAlive && // 敵が生存中
      player.x < enemy.x + enemy.w && // 水平方向の重なり
      player.x + player.w > enemy.x && // 水平方向の重なり
      player.y + player.h > enemy.y // プレイヤーが敵の上面以外に触れる
    ) {
      player.isAlive = false;
    }
  }
  
  
  
  // ブロックの描画
  for (let block of blocks) {
    block.show();
  }
  
  // 敵の描画
  for (let enemy of enemies) {
    if (enemy.isAlive) {
      
      enemy.vy += 1; // 重力
      let enemyFutureY = enemy.y + enemy.vy;
      
      for (let block of blocks){
        
        if (
          enemy.x < block.x + block.w && // 水平方向の重なり
          enemy.x + enemy.w > block.x && // 水平方向の重なり
          enemy.y + enemy.h <= block.y && // 敵がブロックの上面にいる
          enemyFutureY + enemy.h >= block.y // 次のフレームでブロックを通過しない
        ) {
          enemyFutureY = block.y - enemy.h; // 敵をブロックの上に移動
          enemy.vy = 0; // 縦方向の速度をリセット
        }
        
        
        
        if (
          enemy.x < block.x + block.w && // 水平方向の重なり
          enemy.x + enemy.w > block.x && // 水平方向の重なり
          enemy.y + enemy.h > block.y && // 敵がブロックに接触
          enemy.y < block.y + block.h // 敵がブロック内にいる
        ) {
          enemy.direction *= -1; // 方向を反転
          enemy.x += enemy.speed * enemy.direction; // 衝突を避ける
        }
      }
      
      enemy.y = enemyFutureY;
      
      enemy.show();
      enemy.move();
    }
  }
  
  pop();
  
}

function keyPressed() {
   if (keyCode === 32 && !player.isJumping) { // スペースキーでジャンプ
    player.vy = -15;
    player.isJumping = true;
  }
}

では今回書き加えたところをそれぞれ開設します。
それぞれどこの行に書いてあるかは、単語でctrl + Fで検索したら見つかりやすいです。

まず、cameraXはカメラのX座標です。

cameraX = constrain(player.x – width / 2, 0, 2000 – width);
これは、cameraX が 0 以下や仮想ステージの幅(例: 2000ピクセル)を越えないように constrain() を使用。
constrainは英語で制限するという意味で、これで動きを制限しているという意味です。

push();
これは現在の描画状態を保存します。

translate(-cameraX, 0);
これはカメラの座標分だけ全体を左に移動させます。

pop();
これは保存された描画状態を復元します。

コメント


タイトルとURLをコピーしました