<footer><div id=version></div></footer>
html,
body {
  height: 100%;
}

body {
  margin: 0;
  padding: 0;
  background: #111;
  color: #eee;
  font: caption;
}

#version {
  position: absolute;
  left: 0;
  top: 0;
  padding: 1px 2px;
  background: rgba(0, 0, 0, 0.5);
  color: white;
}
/* global Phaser */

var layoutSize = { width: 1024, height: 768 };
var X_POSITION;
var Y_POSITION;

var player_control;
var player2_control;

var player;
var player2;

const moveVelocity = 500;
const jumpVelocity = 1050;
const gravityY = 3000;

var platforms_normal;

var debug1;
var debug2;

function init() {
  this.input.keyboard.on("keydown", ({ key, type }) => {
    console.info(type, key);
  });
  this.input.keyboard.on("keyup", ({ key, type }) => {
    console.info(type, key);
  });
}

function preload() {
  this.load.image("charaBase_jump", "assets/sprites/bikkuriman.png");
  this.load.image("nPlatform_1x4", "assets/sprites/crate.png");
}

function create() {
  const { width, height } = this.game.scale;

  X_POSITION = {
    LEFT: 0,
    CENTER: width / 2,
    RIGHT: width
  };

  Y_POSITION = {
    TOP: 0,
    CENTER: height / 2,
    BOTTOM: height
  };

  this.physics.world.setBounds(
    X_POSITION.CENTER - 1024 / 2,
    Y_POSITION.TOP,
    1024,
    768,
    true,
    true,
    true,
    false
  );

  /* Player */
  player = this.physics.add.sprite(
    X_POSITION.CENTER - 24,
    Y_POSITION.CENTER,
    "charaBase_jump"
  );
  player.setTint(0x42f5e9);
  player.setDepth(5);
  player.setData("color", 0x42f5e9);
  player.setData("isJumping", false);

  player.body.setCollideWorldBounds(true);
  player.setSize(28, 40);
  player.setOffset(10, 8);
  player.body.gravity.y = gravityY;

  player2 = this.physics.add.sprite(
    X_POSITION.CENTER + 24,
    Y_POSITION.CENTER,
    "charaBase_jump"
  );
  player2.setTint(0xf54242);
  player2.setDepth(5);
  player2.setData("color", 0xf54242);
  player2.setData("isJumping", false);

  player2.body.setCollideWorldBounds(true);
  player2.setSize(28, 40);
  player2.setOffset(10, 8);
  player2.body.gravity.y = gravityY;

  /* Input */
  if (player_control) {
    for (const key of Object.values(player_control)) {
      this.input.keyboard.addKey(key);
    }
  } else {
    player_control = this.input.keyboard.addKeys({
      up: "up",
      down: "down",
      left: "left",
      right: "right"
    });
  }
  
  if (player2_control) {
    for (const key of Object.values(player2_control)) {
      this.input.keyboard.addKey(key);
    }
  } else {
    player2_control = this.input.keyboard.addKeys({
    up: Phaser.Input.Keyboard.KeyCodes.W,
    down: Phaser.Input.Keyboard.KeyCodes.S,
    left: Phaser.Input.Keyboard.KeyCodes.A,
    right: Phaser.Input.Keyboard.KeyCodes.D
  });
  }

  

  /* Platform */
  platforms_normal = this.physics.add.staticGroup();

  platforms_normal
    .create(X_POSITION.CENTER - 192, 650, "nPlatform_1x4")
    .setData("horizontal", true);
  platforms_normal.create(X_POSITION.CENTER, 650, "nPlatform_1x4");
  platforms_normal
    .create(X_POSITION.CENTER + 192, 650, "nPlatform_1x4")
    .setData("horizontal", true);

  this.physics.add.collider(player, platforms_normal);
  this.physics.add.collider(player2, platforms_normal);

  debug1 = this.add.text(0, 0, "", { fill: "#42f5e9" });
  debug2 = this.add.text(384, 0, "", { fill: "#f54242" });
}

function update() {
  const keys = [
    "up",
    "down",
    "left",
    "right",
    "isDown",
    "timeDown",
    "isUp",
    "timeUp",
    "originalEvent",
    "type"
  ];

  debug1.setText(JSON.stringify(player_control, keys, 2));
  debug2.setText(JSON.stringify(player2_control, keys, 2));

  if (
    player.y > layoutSize.height + 192 ||
    player2.y > layoutSize.height + 192
  ) {
    this.scene.restart();
    
    // Remove the keys so they don't get destroyed,
    // but save state for next cycle

    for (const key of [
      ...Object.values(player_control),
      ...Object.values(player2_control)
    ]) {
      this.input.keyboard.removeKey(key, false);
    }

    return;
  }

  /* Player 1 Control */
  if (player_control.left.isDown) {
    player.body.velocity.x = -moveVelocity;
    player.flipX = true;
  } else if (player_control.right.isDown) {
    player.body.velocity.x = moveVelocity;
    player.flipX = false;
  } else {
    player.body.velocity.x = 0;
  }

  if (
    player_control.up.isDown &&
    player.body.touching.down &&
    !player.getData("isJumping")
  ) {
    player.setData("isJumping", true);
    player.body.velocity.y = -jumpVelocity;
  } else if (player_control.up.isUp && player.getData("isJumping")) {
    player.setData("isJumping", false);
  }

  /* Player 2 Control */
  if (player2_control.left.isDown) {
    player2.body.velocity.x = -moveVelocity;
    player2.flipX = true;
  } else if (player2_control.right.isDown) {
    player2.body.velocity.x = moveVelocity;
    player2.flipX = false;
  } else {
    player2.body.velocity.x = 0;
  }

  if (
    player2_control.up.isDown &&
    player2.body.touching.down &&
    !player2.getData("isJumping")
  ) {
    player2.setData("isJumping", true);
    player2.body.velocity.y = -jumpVelocity;
  } else if (player2_control.up.isUp && player2.getData("isJumping")) {
    player2.setData("isJumping", false);
  }
}

function stringifyReplace(k, v) {
  if (!k) return v;

  if (v && typeof v === "object") return String(v);

  return v;
}

document.getElementById("version").textContent = "Phaser v" + Phaser.VERSION;

var config = {
  width: 1024,
  height: 768,
  physics: {
    default: "arcade",
    arcade: {
      gravity: { y: 300 },
      debug: true
    }
  },
  scene: { init, preload, create, update },
  loader: {
    baseURL: "https://labs.phaser.io",
    crossOrigin: "anonymous"
  }
};

new Phaser.Game(config);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser-arcade-physics.js