pui4
pui4

Reputation: 11

Loading code between 2 Javascript files doesn't carry variable

I have a game engine type thing and I'm tring to make it so the users can have multiple 'poopengine-window' elements and specify a Javascript file for that specific game window to use. However I cannot figure out how to comunicate between these two files properly without using window variable. Here is the older version on github. Currently I get this error:

Uncaught (in promise) ReferenceError: poopengine is not defined
    <anonymous> http://127.0.0.1:5500/poopengine.mjs line 408 > eval:1
    attributeChangedCallback http://127.0.0.1:5500/poopengine.mjs:408
    promise callback*attributeChangedCallback/< http://127.0.0.1:5500/poopengine.mjs:403
    promise callback*attributeChangedCallback http://127.0.0.1:5500/poopengine.mjs:403
    <anonymous> http://127.0.0.1:5500/poopengine.mjs:413

Here is the code:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../poopengine.mjs" type="module"></script>
    <style>
        body {
            padding: 0;
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <poopengine-window script="./main.js"></poopengine-window>
</body>
</html>

main.js

const originalStart = poopengine.start;
poopengine.start = function () {
  originalStart.apply(this);
  const box = poopengine.object({
    width: 50,
    height: 50,
    x: 0,
    y: 0,
    colour: 'blue'
  });
}

const originalUpdate = poopengine.update;
poopengine.update = function () {
  originalUpdate.apply(this);
  // Code goes here:
}

poopengine.resize = function () {
  // Code goes here:
}

poopengine.start();

poopengine.mjs

class poopengine_class {
  constructor(canvas) {
    this.canvas = canvas;
    this.display = { width: 300, height: 200 };
    this.context = null;
    this.objects = [];
    this.input = new class input {
      constructor() {
        this.keydown = null;
        this.keypressed = null;
        this.keyup = null;
        this.click = null;
        this.mousedown = null;
        this.mouseup = null;
        this.mouse_pos = { x: null, y: null };
        this.hovering = function (object) {
          if (this.mouse_pos.x != null) {
            const index = poopengine.objects.indexOf(object);
            const array = poopengine.objects.slice(index + 1);

            for (let i = 0; i < array.length; i++) {
              if (
                this.mouse_pos.x >= array[i].x &&
                this.mouse_pos.x <= array[i].x + array[i].width &&
                this.mouse_pos.y >= array[i].y &&
                this.mouse_pos.y <= array[i].y + array[i].height
              ) {
                return false;
              }
            }

            if (
              this.mouse_pos.x >= object.x &&
              this.mouse_pos.x <= object.x + object.width &&
              this.mouse_pos.y >= object.y &&
              this.mouse_pos.y <= object.y + object.height
            ) {
              return true;
            } else {
              return false;
            }
          }
        }
      }
    }
    this.fps = 0;
    this.deltaTime = 1;
    this.times = [];

    this.start = function () {
      if (!this.canvas) {
        console.error("Canvas is not initialized yet.");
        return;
      }

      this.canvas.click();
      this.canvas.width = this.display.width;
      this.canvas.height = this.display.height;

      this.context = this.canvas.getContext("2d");

      // Event listeners
      window.onresize = () => {
        this.resize();
      };

      // Mouse input
      window.addEventListener("mousedown", () => {
        this.input.mousedown = true;
      });
      window.addEventListener("click", () => {
        if (this.input.click == null) {
          this.input.click = true;

          setTimeout(() => {
            this.input.click = null;
          }, 10);
        }
      });
      window.addEventListener("mouseup", () => {
        this.input.mouseup = true;
        this.input.mousedown = null;

        setTimeout(() => {
          this.input.mouseup = null;
        }, 10);
      });

      window.addEventListener("mousemove", (event) => {
        this.input.mouse_pos = { x: event.clientX, y: event.clientY };
      });

      // Input
      window.addEventListener("keydown", (event) => {
        this.input.keydown = event.key;
      });
      window.addEventListener("keypress", (event) => {
        if (this.input.keypressed == null) {
          this.input.keypressed = event.key;

          setTimeout(() => {
            this.input.keypressed = null;
          }, 10);
        }
      });
      window.addEventListener("keyup", (event) => {
        this.input.keyup = event.key;
        this.input.keydown = null;

        setTimeout(() => {
          this.input.keyup = null;
        }, 10);
      });

      window.requestAnimationFrame(() => this.update());
    };

    this.update = function () {
      this.context.reset()
      window.requestAnimationFrame(() => this.update());
      this.context.clearRect(0, 0, this.display.width, this.display.height);

      // Fps and delta calculation
      const now = performance.now();

      if (this.times.length > 0) {
        this.deltaTime = now - this.times[this.times.length - 1];
      }

      while (this.times.length > 0 && this.times[0] <= now - 1000) {
        this.times.shift();
      }
      this.times.push(now);
      this.fps = this.times.length;

      // Update object drawing
      for (let i = 0; i < this.objects.length; i++) {
        this.objects[i].update();
      }
    };

    // Utitlty functions
    this.object = function ({
      width,
      height,
      x,
      y,
      colour,
      image,
      tile,
      image_alpha,
      text,
      text_size,
      font,
      word_wrap,
      line_height,
      text_align,
    }) {
      this.width = width;
      this.height = height;
      this.x = x;
      this.y = y;
      this.colour = colour;

      let ctx = poopengine.context;

      // Image logic
      if (image != undefined) {
        this.image = new Image();
        this.image.src = image;
        this.image_alpha = image_alpha;
        if (this.image_alpha != undefined) {
          ctx.globalAlpha = this.image_alpha;
        }
        if (tile == undefined || tile == false) {
          ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
        } else {
          this.pattern = ctx.createPattern(this.image, "repeat");
          ctx.fillStyle = this.pattern;
          ctx.fillRect(this.x, this.y, this.width, this.height);
        }
      }
      // Text logic
      else if (text != undefined) {
        ctx.fillStyle = this.colour;
        this.text = text;
        this.text_size = text_size;
        if (font != undefined) {
          this.font = font;
          ctx.font = String(text_size + "px " + font);
        } else {
          ctx.font = String(text_size + "px Arial");
        }

        if (text_align != undefined) {
          ctx.textAlign = text_align;
        }

        ctx.fillText(this.text, this.x, this.text_size + this.y, this.width);
      }
      // Box logic
      else {
        ctx.fillStyle = this.colour;
        ctx.fillRect(this.x, this.y, this.width, this.height);
      }

      this.update = function () {
        // Image logic
        if (image != undefined) {
          if (this.image_alpha != undefined) {
            ctx.globalAlpha = this.image_alpha;
          }
          if (tile == undefined || tile == false) {
            ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
          } else {
            this.pattern = ctx.createPattern(this.image, "repeat");
            ctx.fillStyle = this.pattern;
            ctx.fillRect(this.x, this.y, this.width, this.height);
          }
        }
        // Text logic
        else if (text != undefined) {
          ctx.fillStyle = this.colour;
          if (this.font != undefined) {
            ctx.font = String(this.text_size + "px " + this.font);
          } else {
            ctx.font = String(this.text_size + "px Arial");
          }

          if (word_wrap == true) {
            this.line_height = line_height;

            const words = this.text.split(" ");
            let currentLine = words[0];
            let lineCount = 0;

            for (let i = 1; i < words.length; i++) {
              const word = words[i];
              const width = ctx.measureText(currentLine + " " + word).width;

              if (width < this.width) {
                currentLine += " " + word;
              } else {
                ctx.fillText(
                  currentLine,
                  this.x,
                  this.y + this.line_height * lineCount++ + this.text_size
                );
                currentLine = word;
              }
            }

            ctx.fillText(
              currentLine,
              this.x,
              this.y + this.line_height * lineCount + this.text_size
            );
          } else {
            ctx.fillText(this.text, this.x, this.y + this.text_size, this.width);
          }
        }
        // Box logic
        else {
          ctx.fillStyle = this.colour;
          ctx.fillRect(this.x, this.y, this.width, this.height);
        }
      };

      this.destroy = function () {
        const index = poopengine.objects.indexOf(this);

        poopengine.objects.splice(index, 1);
      };

      poopengine.objects.push(this);
      this.index = poopengine.objects.indexOf(this);
      ctx.reset();
    };

    // TODO: fix audio
    /* this.audio = function (src) {
      this.sound = document.createElement("audio");
      this.sound.src = src;
      this.sound.setAttribute("preload", "auto");
      this.sound.setAttribute("controls", "none");
      this.sound.style.display = "none";
      document.body.appendChild(this.sound);
      this.play = function () {
        this.sound.play();
      };
      this.stop = function () {
        this.sound.pause();
        this.sound.currentTime = 0;
      };
      this.pause = function () {
        this.sound.pause();
      };
      this.loop = function (option) {
        this.sound.loop = option;
      };
    }, */

    // Z index logic
    this.move_to_top = function (object) {
      const index = this.objects.indexOf(object);
      if (index !== -1) {
        this.objects.splice(index, 1);
      }

      this.objects.push(object);
    };

    this.send_to_back_bg = function (object) {
      const index = this.objects.indexOf(object);

      if (index !== -1) {
        this.objects.splice(index, 1);
        this.objects.splice(1, 0, object);
      }
    };

    this.send_to_back = function (object) {
      const index = this.objects.indexOf(object);
      if (index !== -1) {
        this.objects.splice(index, 1);
      }

      this.objects.unshift(object);
    };

    this.change_index = function (object, change) {
      const index = this.objects.indexOf(object);
      const new_index = index + change;

      if (index !== -1) {
        this.objects.splice(index, 1);
        this.objects.splice(new_index, 0, object);
      }
    };

    this.set_index = function (object, new_index) {
      const index = this.objects.indexOf(object);

      if (index !== -1) {
        this.objects.splice(index, new_index);
        this.objects.splice(new_index, 0, object);
      }
    };

    this.revert_index = function (object) {
      const currentIndex = this.objects.indexOf(object);

      if (currentIndex !== -1 && typeof object.index === "number") {
        this.objects.splice(currentIndex, 1);

        this.objects.splice(object.index, 0, object);
      }
    };

    this.resize= function () {};

    // Animation
    this.animating_vals = []
    this.animate_value = function (id, val, increase, speed, until, callback, finish) {
      let isBigger;
      if (val < until) {
        isBigger = true;
      } else {
        isBigger = false;
      }
      if (!this.animating_vals.includes(id)) {
        this.animating_vals.push(id);
        const inter = setInterval(() => {
          val += increase;
          if (val >= until && isBigger) {
            if (finish != undefined) {
              finsish()
            }
            clearInterval(inter);
            val = until;
          } else if (val <= until && !isBigger) {
            if (finish != undefined) {
              finsish()
            }
            clearInterval(inter);
            val = until;
          }
          callback(val);
        }, speed);
      }
    }
  }
}

class poopengine_component extends HTMLElement {
  static observedAttributes = ['script'];

  constructor() {
    super();
  }

  attributeChangedCallback(name, _oldValue, newValue) {
    fetch(newValue).then(res => res.text().then(scr => {
      const shadowRoot = this.attachShadow({ mode: 'open' });
      const canvas = document.createElement('canvas');
      shadowRoot.append(canvas);
      const localPoopengine = new poopengine_class(canvas);
      eval(scr);
    }));
  }
}

customElements.define("poopengine-window", poopengine_component);

I've searched everywhere and can't find a solution. Any help would be greatly appreciated. 😀

Edit: Just though I would go into a bit more detail. It runs the functions properly in main.js. However the poopengine.object doesn't run and is the part where poopengine is not defined.

Upvotes: 1

Views: 45

Answers (2)

pui4
pui4

Reputation: 11

FIXED!!!

The issue was that in my poopengine class I was calling poopengine itself instead of this. There was nothing wrong with my eval however I am now using Function instead. Thanks for the help. 😁

Upvotes: 0

Eghizio
Eghizio

Reputation: 36

Somehow my answer got turned into a comment...

I believe this relates to your problem: Access imported functions from evaled code

Here is the annotation to the eval() function from the MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#script

"It will be parsed as a script, so import declarations (which can only exist in modules) are not allowed."

Upvotes: 0

Related Questions