If your kid love Castlevania and they are interested in computer TL;DR...

Why?

My kids loves Castlevania when he saw me played in Nintendo virtual console. Graphics/sound effect are simple enough for him to digest.

So why not make a typing game and meanwhile play a bit of PIXI.js (haven't done serious project for a bit while 😜)?

Ambush! Webpack Complains

Because gatsby try to render the page and staticize it using server side rendering (please see here). For our app, it's dynamic js so we need to stop it, by wrapping our modules with LazyLoader components as below:

import Loadable from "@loadable/component"
export const SimonSpriteDemo = Loadable(() => import("./castletypia/simon-sprite-demo.js"))

WebGL and Legacy

It turns out My Linux's Firefox doesn't support webgl well... For larger audience with no WebGL but only html5 I turned off it by installing the legacy PIXI and import the right pkgs:

    "pixi.js-legacy": "^5.3.3",
import {
  ...
} from '@inlet/react-pixi/legacy';

...

const stageOptions = {
  forceCanvas: true,
}

React PIXI.js!

Good someone already have done something for react! But they don't support animation timing (different duration for each frame at this point)?

Let's customize our own component!

const MyAnimatedSprite = PixiComponent('MyAnimatedSprite', {
    create: (props)=>{
        const { frameObjects, images, isPlaying = true, initialFrame } = props
        const animatedSprite = new PIXI.AnimatedSprite(frameObjects)
        animatedSprite[isPlaying ? 'gotoAndPlay' : 'gotoAndStop'](initialFrame || 0)
        return animatedSprite
    },
    applyProps:(instance, oldProps, newProps) => {
        const { frameObjects, isPlaying, initialFrame, ...props } = newProps

        applyDefaultProps(instance, oldProps, props)

        if (frameObjects && oldProps['frameObjects'] !== frameObjects) {
            instance.textures = (frameObjects)
            instance.gotoAndPlay(0)
        }

        if (isPlaying !== oldProps.isPlaying || initialFrame !== oldProps.initialFrame) {
            const frame = typeof initialFrame === 'number' ? initialFrame : instance.currentFrame || 0
            instance[isPlaying ? 'gotoAndPlay' : 'gotoAndStop'](frame)
        }
        return instance
    }
})

This MyAnimatedSprite do similar what AnimatedSprite does but accept frameObjects as defined here:

export interface FrameObject {
    texture: Texture;
    time: number;
    }

Sprites!

2D game is all about sprite. We just need animation texture and its meta file (json). For texture, I found Simon sprite here and with some basic painting tool e.g. gimp, I slowly locate their coordinates and sprite size... basically to fill these format:

    "frames": {
        "walk0":
        {
            "frame": {"x":29,"y":22,"w":16,"h": 32},
            "rotated": false,
            "trimmed": true,
            "spriteSourceSize": {"x":0,"y":0,"w":16,"h":32},
            "sourceSize": {"w":16,"h":32}
        },
        ...
    }

For the frameObjects, I created my definition of animations that'll be compatible with AnimatedSprite:

{
    "my_animations": {
        "walk": [
        {
            "texture": "walk0",
            "time": 200
        }
        ...
    }

Okay, now here it is:

...