<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: 5px;
  top: 5px;
}
/* global Phaser */

var config = {
  width: 800,
  height: 600,
  backgroundColor: 0x404040,
  loader: {
    baseURL: 'https://labs.phaser.io',
    crossOrigin: 'anonymous'
  },
  scene: {
    preload: preload,
    create: create,
    update: update
  },
  physics: {
    default: 'arcade',
    arcade: {
      debug: true,
      debugShowVelocity: false
    }
  }
};

var group;

var caption;

var captionStyle = {
  fill: '#7fdbff',
  fontFamily: 'monospace',
  lineSpacing: 4
};

var captionTextFormat = (
  'Total:    %1\n' +
  'Max:      %2\n' +
  'Active:   %3\n' +
  'Inactive: %4\n' +
  'Used:     %5\n' +
  'Free:     %6\n' +
  'Full:     %7\n'
);

new Phaser.Game(config);

function preload () {
  this.load.image('space', 'assets/skies/space2.png');
  this.load.spritesheet('alien', 'assets/tests/invaders/invader1.png', { frameWidth: 32, frameHeight: 32 });
}

function create () {
  // You can zoom out to see the changes at boundaries:
  this.cameras.main.setZoom(0.8);
  
  this.anims.create({
    key: 'creep',
    frames: this.anims.generateFrameNumbers('alien', { start: 0, end: 1 }),
    frameRate: 2,
    repeat: -1
  });

  this.add.image(400, 300, 'space');

  group = this.physics.add.group({
    defaultKey: 'alien',
    defaultFrame: 0,
    maxSize: 100,
    // Physics body default.
    // We're creating inactive sprites so they should be physics-disabled until activation.
    // The body will be enabled in activeAlien()
    enable: false
  });

  group.createMultiple({
    key: group.defaultKey,
    frame: group.defaultFrame,
    // We'll seed the group with 50 inactive sprites.
    // The group will eventually grow to 100 (maxSize).
    frameQuantity: 0.5 * group.maxSize,
    active: false,
    visible: false
  });

  caption = this.add.text(16, 16, '', captionStyle);

  this.time.addEvent({
    delay: 100,
    loop: true,
    callback: addAlien
  });
}

function update () {
  group.children.iterate(function (alien) {
    if (alien.y > 600) {
      deactivateAlien(alien);
    }
  });

  updateCaption();
}

function activateAlien (alien, x, y) {
  alien.enableBody( // Enable physics body
    true, // Reset body and game object, at (x, y)
    x,
    y,
    true, // Activate sprite
    true  // Show sprite
  );
  
  console.assert(alien.body.enable, 'body.enable is true');
  console.assert(alien.active, 'active is true');
  console.assert(alien.visible, 'visible is true');
  // Reset also stops the body:
  console.assert(alien.body.speed === 0, 'speed is 0');
  
  alien.setVelocityY(60);
  alien.setTint(Phaser.Display.Color.RandomRGB().color);
  alien.play('creep');
}

function deactivateAlien(alien) {
  alien.disableBody( // Stop and disable physics body
    true, // Deactivate sprite (active=false)
    true  // Hide sprite (visible=false)
  );
  
  console.assert(!alien.body.enable, 'body.enable is false');
  console.assert(!alien.active, 'active is false');
  console.assert(!alien.visible, 'visible is false');
  console.assert(alien.body.speed === 0, 'speed is 0');
}

function addAlien () {
  // Get first inactive sprite or create a new sprite (unless group is full).
  var alien = group.get();

  if (!alien) return; // None free.

  activateAlien(alien, Phaser.Math.Between(250, 800), Phaser.Math.Between(-64, 0));
}

function updateCaption () {
  caption.setText(Phaser.Utils.String.Format(captionTextFormat, [
    group.getLength(),
    group.maxSize,
    group.countActive(true),
    group.countActive(false),
    group.getTotalUsed(),
    group.getTotalFree(),
    group.isFull()
  ]));
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/npm/phaser@3.19.0/dist/phaser.js
  2. https://cdn.jsdelivr.net/npm/colors.css@3.0.0/js/colors.js