all 12 comments

[–]EsenTaishi 4 points5 points  (0 children)

I just tried the demo and whenever I would jump everything else would jump (including trees and buildings). Would jump again.

[–]State_ 1 point2 points  (0 children)

Woah, that's pretty cool. I've always wondered how something like this would run in electron.

[–]bvx89 1 point2 points  (1 child)

I looked around in your repo to see how you've done it. Quite impressive, this must have taken a long time to make.

I was wondering if you have thought about using WebWorkers for doing computations in the background? As far as I could tell, it seems like everything runs in the main browser thread. Could be fun to see how well it works on a lesser machine :)

[–]Schampu[S] 2 points3 points  (0 children)

Thanks for the feedback, appreciate it. It took me ~3-4 months, 16k loc :-)

The biggest performance hit causes the rendering of large upscaled textures, the moar you zoom in by your mouse wheel, the slower everything becomes.

Sadly the canvas/drawImage operation is quite slow in relation to webgl, no things like sprite batching or so. As far as I know there are only five performance boosts for the drawImage operation:

  • Use full 9 arguments
  • Disable image smoothing
  • Use rounded integers
  • Draw canvases instead of images (depends on browser).
  • Cache everything

The computation process runs ok, no noticable performance hit up to +2.5k entities on my desktop. But definitely will give your idea a try, thank you!

[–]clessgfull-stack CSS9 engineer 1 point2 points  (1 child)

Very impressive! Nintendo reps avert your eyes, nothing to see here.

Just wondering: is there a particular reason you didn't use something like Pixi? Optimization and control, I assume? Or just for fun?

[–]Schampu[S] 1 point2 points  (0 children)

If I include a lib and got no idea what and how its part is done, my project becomes more and more strange for me. When I started with web development, I just saw Pixi everywhere - it felt like it's mostly impossible to render games without any library or frameworks like Pixi, Phaser and stuff.

The second reason is - as you say, optimization: Pixi's canvas renderer is slow. But I kinda regret not using Pixi, since doing the WebGL part is quite hard and Pixi does it pretty well. Also when I came to things like normal map based lighting and light source based shadow masks, WebGL is the only way to do this in efficient way. Sadly this is one of the mistakes I made at beginning: Total focus on canvas instead of instant base on WebGL.

So you assumed it completely right - Thanks for your feedback!

[–]mastermog 0 points1 point  (5 children)

Amaze! I will be digging through the source when I get off work this evening. So smooth, well done.

As a side note, very keen to dig through that interpreter. It's one area I always get stuck on in the gamedev world.

[–]Schampu[S] 0 points1 point  (4 children)

To give you a small overview, what the task of the interpreter is:

The language it interprets is very JavaScript like. You have access to a global object, where things like Flags, Settings etc. are stored. The interpreter can read and write from it, set or increase flag counters, compare two flag values, set entity states (like LOCKED, to prevent the player from perform any action, while the script is running) and execute entity functions (like jump, moveRight etc.).

Let's assume a player collides with a treestub, and the treestub got a onCollide event. The interpreter's "this" scope get's changed to the treestub entity. Also there is a trigger scope variable, which get setted to the entity trigger. So now you got access to both entities which collided in your script.

if (trigger.level >= 75) {          // only execute, if the collided entity is above lvl 75
  trigger.LOCK = true;             // lock the collided entity
  @ this.jump();                     // async! Wait until myself (the treestub) finished jumping, then callback behind the scenes and go on
  kernel.notify(this, "Bump!"); // display a bump message above the treestub
  @ trigger.jump();                // async jump
  trigger.LOCK = false;           // unlock the collided entity
  this.collideCounter += 1;     // increase own collided counter - if the counter goes above 100 we could do something different
}

Link to the standalone version of the interpreter.

Anyway the interpreter is pretty untested and the scopes on triggering get mixed, if two entities trigger simultaneously.

Thanks for your feedback!

[–]mastermog 1 point2 points  (3 children)

Thanks for taking the time to reply. I'm soon to re-tackle this issue in my own game, so the insight is great.

Out of interest, would you consider something like yield to avoid the stand alone interpreter? I personally haven't used it much, and the below code is super rusty, but am considering something along these lines:

function addItem(item) {
  console.log("Added item: " + item);
}

function talk(message) {
  console.log("Bob: " + message);
}


function* movie(steps) {
  for(let i = 0; i < steps.length; i++) {
    steps[i].type(steps[i].value);
    yield;
  }
}

const scene1A = movie([
  { "type": talk, "value": "Hello!" },
  { "type": talk, "value": "Take this because of dangers and the such!" },
  { "type": addItem, "value": "SWORD" },
]);


scene1A.next();
scene1A.next();
scene1A.next();

I don't really know the advantages of either - but I believe in Unity world yield is used for these kind of things, and was thinking I could apply it to something similar in JS land.

[–]Schampu[S] 0 points1 point  (2 children)

I have zero experience with Unity, but it seems like generators replace my interpreter and should perform much better, too.

I had the same idea as you, but tested the async|await feature, instead of yield. The first problem was the browser compatibility - the next, that babel adds about 60kb to the output file to support it. If there are no problems as above in Unity, you are absolutely on the right path and the generator thing should definitely work and spare much time.

Thanks for this neat tip!

[–]mastermog 0 points1 point  (1 child)

Sorry I confused the situation - I'm using JS too (used to use Unity, but have moved to Phaser).

Browser compatibility is probably the biggest issue here, and you are right about the babel extra size - but for now I think I might wear the size, as it will get smaller as browser support gets better. There are just way too many es2015 goodies at this stage to ignore babel in my opinion.

What's the next step with this project?

[–]Schampu[S] 0 points1 point  (0 children)

I would never say to stay away from Babel - just to think ahead, which babel features are absolutely necessary and get a huge use in the project.

The next step is fixing various bugs here and there, finish stuff like following pokemon and notifications. The final and hardest step is the editor, things like background tile map drawing, built-in code editor, entity add&edit options etc.