Osmose

I make websites and chiptunes!

  • he/him

AKAs:
Lapsed Neurotypical
JavaScript's Strongest Warrior
Fake Podcast Host
Thotleader
Vertically Integrated Boyfriend
Your Fave's Mutual
Certified 5x Severals by the RIAA
Inconsistently Medicated
The Source That Views Back
Carnally Known
The Alternative


Homepage
osmose.ceo/

posts from @Osmose tagged #typescript

also:

function html(strings: TemplateStringsArray, ...values: unknown[]): Node[] {
  let finalString = '';
  for (let k = 0; k < strings.length; k++) {
    finalString += strings[k];

    const value = values[k];
    if (Array.isArray(value)) {
      finalString += value.join('');
    } else if (value instanceof Element) {
      finalString += value.outerHTML;
    } else if (value === undefined || value === null) {
      // Do not add anything
    } else {
      finalString += value.toString();
    }
  }

  return Array.from(new DOMParser().parseFromString(finalString, 'text/html').body.childNodes);
}

// How to use
const title = 'Title';
const paragraphs = ['Paragraph 1', 'Paragraph 2'];
const button = document.createElement('button');
button.innerText = 'Button';
button.type = 'submit';

document.body.append(
  ...html`
    <h1>${title}</h1>
    <p>content</p>
    ${paragraphs.map((text) => `<p>${text}</p>`)} 
    ${button}
  `
);

You can fiddle with it to make it return a DocumentFragment or skip text nodes depending on your needs.



Like, first off, you're not really expected to keep up with every new development—I still haven't built a site with GraphQL even though it's past it's peak ubiquity. You just need to not be afraid of the possibility of learning something new.

I looked up GraphQL once for like 5 mins years ago to see what shape of thing it was, and now if I'm ever on a project that seems like it might benefit from it, that's when I do a 30 min look into what it actually provides and why I might use it, and most of that is just searching for those questions and seeing what other people have said.

Having said that, I've also still "kept up" with the latest in JS through two pretty low-effort habits:

  • I read the headlines and articles on Hacker News while using uBlock Origin and the filter news.ycombinator.com##.subtext to hide the comments and upvotes. This is hardly comprehensive and doesn't constitute a "healthy" view of silicon valley tech but it's broader than just JS and at least lets me know the major things tech bros care about. Again, I cannot emphasize how important it is to ignore the comments as they provide much more harm than benefit, and the site is considered a cesspool for a reason.
  • Newsletters! The real trick is that people are already doing the work for you and summarize stuff. My current newsletter for JS is https://bytes.dev/ which can be a bit blindly positive about new tech but hasn't, like, pivoted to being about LLMs or any shit like that.


Osmose
@Osmose

Pretty much all my gamedev is hobby Phaser stuff and I've settled on a very specific state-machine-based pattern for logic in my games. I'm curious what game logic (e.g. player movement) looks like in other systems, esp since I've never had more than a cursory look at engines like Godot where behavior is more split between raw code and systems configured from the UI. Phaser is entirely a code library so I'm used to not having a GUI of any kind.

Anyone wanna share their own or know of any open source games that I could look at?


Osmose
@Osmose
class CrouchingState extends PlayerState {
  handleEntered() {
    this.player.stop().setFrame(5).setVelocityX(0).setCollisionBox(CROUCHING_COLLISION_BOX);
  }

  update() {
    const { scene, player } = this;

    // Falling
    if (!player.body.blocked.down) {
      return this.transition('falling');
    }

    // Stay crouched as long as down is held
    if (!scene.controller.down.isDown) {
      return this.transition('idle');
    }

    // Dropping from a droppable floor
    if (scene.controller.button1.isDown && player.canDrop) {
      player.body.y += DROP_GAP;
      return this.transition('falling');
    }
  }

  handleExited() {
    this.player.setCollisionBox(STANDING_COLLISION_BOX);
  }
}

Fairly straightforward, has handlers to run when you enter and exit a state, update that runs once a frame, etc. Once nice thing is that this.transition can accept arguments that get passed to handleEntered for parameterized states:

class WalkingState extends PlayerState {
  facing!: Facing;

  handleEntered(facing: Facing) {
    this.facing = facing;
    this.player
      .setFacing(facing)
      .setVelocityX(directionalVelocity(WALKING_VELOCITY_X, facing))
      .play(this.player.holdingObject ? 'playerHoldingWalking' : 'playerWalking');
  }

This also shows why I favor classes for each state; they often end up needing to store some data that otherwise isn't useful to the rest of the player class. Inheritance and customizing a state via constructor arguments have also proven useful; most of my states of the player midair inherit from a base MidairState.

My favorite bit, though, is that the enter/exit handlers can be async:

class HurtState extends PlayerState {
  async handleEntered() {
    const { player, scene } = this;

    // Invulnerability duration
    scene.time.delayedCall(1000, () => {
      player.invulnerable = false;
    });

    // Shove player back
    player
      .stop()
      .setFrame(14)
      .setInvulnerable(true)
      .setVelocity(directionalVelocity(HURT_VELOCITY_X, oppositeDirection(player.facing)), HURT_VELOCITY_Y);
    scene.soundHurt.play();
    await wait(scene, 300);

    return this.transition('idle');
  }
}

Crucially, the state machine will not run the update handler until the promise returned by handleEntered resolves, which ends up being really handy for states that involve running a bunch of animations / sounds / delays / etc. in sequence before transitioning. In this case we chuck the player backwards, wait 300ms for them to start falling, and then handle control back to the player.

At this point mostly I just wanna tighten up the syntax a bit and add useful typing to it so you can do things like type check state parameters.