jorbascrumps / phaser-plugin-water-body

A water body physics simulation plugin for Phaser v3
20 stars 2 forks source link

Does not work with any version of Phaser >= 3.22.0 #13

Open Abspirit opened 1 year ago

Abspirit commented 1 year ago

I get an error when I try the demo version with any version of Phaser >= 3.22.0

phaser.min.js:1 Uncaught TypeError: i.gameObject.emit is not a function at phaser.min.js:1:975347 at Array.map () at Object. (phaser.min.js:1:975280) at n.trigger (phaser.min.js:1:264646) at n.update (phaser.min.js:1:928623) at initialize.update (phaser.min.js:1:938724) at h.emit (phaser.min.js:1:8553) at initialize.step (phaser.min.js:1:160055) at initialize.update (phaser.min.js:1:379349) at initialize.step (phaser.min.js:1:686533)

Abspirit commented 1 year ago

It may help someone : I fixed the emit error with a small change in waterbody class. I made the class extends Phaser.GameObjects.Gameobject

class WaterBody extends Phaser.GameObjects.GameObject {

    constructor (
        context,
        x = 0,
        y = 0,
        w = 100,
        h = 100,
        depth = 150,
        {
            dampening = 0.025,
            renderDepth = 1,
            spread = 0.25,
            tension = 0.025,
            texture,
        } = {}
    ) {

        super(context, x, y);
likeavenus commented 4 months ago

I updated the code and it works on Phaser 3.70

import WaterColumn from "./waterColumn";
import Phaser from "phaser";

export default class WaterBody {
  debug: boolean;
  x: number;
  y: number;
  w: number;
  h: number;
  emitter: Phaser.Events.EventEmitter;
  context: Phaser.Scene;

  constructor(
    context,
    x = 0,
    y = 0,
    w = 100,
    h = 100,
    depth = 150,
    {
      dampening = 0.025,
      renderDepth = 1,
      spread = 0.25,
      tension = 0.025,
      texture,
    } = {}
  ) {
    if (typeof texture === "undefined") {
      throw new Error(
        "This version of WaterBody requires explicitly setting a texture"
      );
    }

    this.debug = false;
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;

    this.tension = tension;
    this.dampening = dampening;
    this.spread = spread;
    this.depth = Math.min(depth, this.h);
    this.texture = texture;

    const coords = [0, this.h - this.depth, this.w, this.h - this.depth];
    const surface = new Phaser.Geom.Line(...coords);
    const points = surface.getPoints(0, 20);

    this.columns = [
      ...points,
      {
        x: this.w,
        y: coords[1],
      },
    ].map(({ x, y }, i) => new WaterColumn(x, y, i));

    const data = this.columns.reduce((cache, { x, y }) => [...cache, x, y], []);
    this.body = context.add
      .polygon(x, y, [coords[0], this.h, ...data, coords[2], this.h])
      .setFillStyle(0x145dd1, 0)
      .setDepth(99)
      .setOrigin(0, 0);

    if (typeof texture === "string") {
      this.background = context.add
        .tileSprite(this.x, this.y, this.w, this.h, this.texture)
        .setAlpha(0.75)
        .setDepth(renderDepth)
        .setOrigin(0, 0);

      this.background.mask = new Phaser.Display.Masks.GeometryMask(
        context,
        this.body
      );

      this.sensor = context.matter.add.rectangle(
        this.x + this.w / 2,
        this.y + this.h - this.depth / 2,
        w,
        this.depth,
        {
          isSensor: false,
          isStatic: true,
          // gameObject: this,
        }
      );
      this.sensor.label = "water";

      this.debugGraphic = context.add.graphics({
        fillStyle: {
          color: 0xffffff,
        },
      });

      const emitterDeathZone = new Phaser.Geom.Polygon(
        Object.values(this.body.geom.points).map(({ x, y }) => [
          this.x + x,
          this.y + y,
        ])
      );

      this.emitter = context.add.particles(0, 0, "droplet", {
        alpha: 1,
        tint: 0x0b5095,
        speed: {
          min: 100,
          max: 500,
        },
        gravityY: 1000,
        lifespan: 4000,
        quantity: 0,
        frequency: 1000,
        angle: {
          min: 240,
          max: 300,
        },
        scale: {
          min: 0.5,
          max: 0.1,
        },
        deathZone: {
          type: "onEnter",
          source: emitterDeathZone,
        },
        deathCallbackScope: this,
        deathCallback: this.onDropletDeath,
      });

      context.sys.events.on("update", this.update, this);
    }
  }

  update() {
    this.columns.forEach((column) =>
      column.update(this.dampening, this.tension)
    );

    const data = this.columns.reduce((cache, { x, y }) => [...cache, x, y], []);
    this.body.geom.setTo([0, this.h, ...data, this.w, this.h]);
    this.body.updateData();

    this.debugGraphic.clear();
    if (this.debug) {
      this.columns.forEach(({ x, y }) =>
        this.debugGraphic.fillRect(this.x + x - 1, this.y + y - 1, 2, 2)
      );
    }

    let lDeltas = Array(this.columns.length).fill(0);
    let rDeltas = Array(this.columns.length).fill(0);

    for (let i = 0; i < 1; i++) {
      for (let j = 0; j < this.columns.length - 1; j++) {
        if (j > 0) {
          const currColumn = this.columns[j];
          const prevColumn = this.columns[j - 1];

          lDeltas[j] = this.spread * (currColumn.y - prevColumn.y);
          prevColumn.speed += lDeltas[j];
        }

        if (j < this.columns.length - 1) {
          const currColumn = this.columns[j];
          const nextColumn = this.columns[j + 1];

          rDeltas[j] = this.spread * (currColumn.y - nextColumn.y);
          nextColumn.speed += rDeltas[j];
        }
      }

      for (let j = 0; j < this.columns.length - 1; j++) {
        if (j > 0) {
          const prevColumn = this.columns[j - 1];
          prevColumn.y += lDeltas[j];
        }

        if (j < this.columns.length - 1) {
          const nextColumn = this.columns[j + 1];
          nextColumn.y += rDeltas[j];
        }
      }
    }
  }

  splash(index, speed = 1, numDroplets = 3) {
    let column = this.columns[index];
    column.speed = speed;

    this.emitter.explode(numDroplets, this.x + column.x, this.y + column.y);

    return this;
  }

  ripple(index, speed) {
    let column = this.columns[index];
    column.speed = speed;

    return this;
  }

  setDebug(bool) {
    this.debug = bool;

    return this;
  }

  onDropletDeath({ x }) {
    const minColumn = 0;
    const maxColumn = this.columns.length - 1;
    const targetColumn = this.columns.findIndex(
      (col, i) => this.x + col.x >= x && i
    );
    const column = Phaser.Math.Clamp(targetColumn, minColumn, maxColumn);

    const speed = 0.5; // TODO: Calculate dynamic speed

    this.ripple(column, speed);
  }
}
jorbascrumps commented 4 months ago

Sorry @Abspirit, I didn't get notified of your issue :( I didn't realize anyone was using this! @likeavenus if you want to submit a PR I'd be happy to release a new version!