Swizec Teller - a geek with a hatswizec.com

Senior Mindset Book

Get promoted, earn a bigger salary, work for top companies

Senior Engineer Mindset cover
Learn more

    Livecoding Recap #45: Almost building a ballistic missile with React and AR.js

    This is a Livecoding Recap – an almost-weekly post about interesting things discovered while livecoding. Usually shorter than 500 words. Often with pictures. Livecoding happens almost every Sunday at 2pm PDT on multiple channels. You should follow My Youtube channel to catch me live.

    This Sunday we had 3 jobs:

    1. Get the AR Minecraft guy working ✅
    2. Use a hand drawn custom marker ❌
    3. Fire an AR ballistic missile from Korea ✅❌

    But AR is hard.

    We got pretty close, just like I suggested in last week's Livecoding Recap the Minecraft guy just needed some textures. After we added those, he became visible and looked great.

    Here he is next to Kiwi.

    To get that working we learned about A-FRAME Schemas. They're a way to describe the properties that an A-FRAME component can accept. A lot like React's propTypes, but as an object.

    The minecraft component comes from A-FRAME-minecraft.js, which we copied off examples in Jerome Etiennes AR.js repo. That looks like this:

    FRAME.registerComponent("minecraft", {
      schema: {
        skinUrl: {
          type: "string",
          default: "",
        },
        wellKnownSkin: {
          type: "string",
          default: "",
        },
        heightMeter: {
          default: 1,
        },
      },
      init: function () {
        var character = new THREEx.MinecraftChar();
        this.character = character;
    
        this.el.object3D.add(character.root);
        // this.el.setObject3D('superRoot', character.root);
      },
      update: function () {
        if (Object.keys(this.data).length === 0) return;
        var character = this.character;
        character.root.scale.set(1, 1, 1).multiplyScalar(this.data.heightMeter);
    
        if (this.data.skinUrl) {
          character.loadSkin(this.data.skinUrl);
        } else if (this.data.wellKnownSkin) {
          character.loadWellKnownSkin(this.data.wellKnownSkin);
        }
      },
    });
    

    I have no idea which component system this is modeled after, but it works. Looks like we can define minecraft guy's height in meters, a URL for the skin texture, and choose one of well known skins. Those won't work in our project because we don't have the files.

    Schema props are passed into A-FRAME components as a string. Like this:

    <entity minecraft="{`heightMeter:" 2;="" skinurl:="" ${skinurl}`}="" minecraft-head-anim="yes" minecraft-body-anim="hiwave">
    </entity>
    

    You can read that as *"Create an A-FRAME Entity composed out of minecraft with heightMeter: 2; skinUrl:, and minecraft-head-anim set to yes and minecraft-body-anim set to hiwave. Both minecraft-head-anim and minecraft-body-anim are A-FRAME components.

    I don't know yet how that composition works, but it looks neat.

    Equipped with a working Minecraft demo dude, we went on to try creating our own marker from scratch. According to the AR.js repo, you are to use this Flash-based marker training implementation from 2009.

    Flash doesn't work well in 2017. Couldn't get it to run at all in Chrome and Firefox wouldn't let us click on "Give camera permissions". Had to hack around that and manually add a rule to the global permissions table.

    We were able to create a marker file. The trick is to have an enclosed area with straight lines. You can put any sort of scribble inside, as long as the outside is straight and enclosed.

    Custom Marker

    Unfortunately we weren't able to convince AR.js to use our marker. Even explicitly setting the marker URL didn't work and it kept using default settings.

    <a-marker type="pattern" url="/markers/marker64.pat"></a-marker>
    

    ¯\_(ツ)_/¯

    Onwards to making a ballistic missile!

    We went the aframe-react way to build our ballistic missile. That way we could postpone understanding true A-FRAME components a little longer.

    class BallisticMissile extends Component {
    
        render() {
            const { pos } = this.props;
    
            return (
                <entity geometry="{{primitive:" 'box',="" width:="" .2,="" height:="" 1,="" depth:="" .2}}="" material="{{roughness:" 0,="" src:="" require('.="" texture.jpg')}}="" scale="{{x:" y:="" z:="" 1}}="" position="{pos}<p">
    
                        />
            )
        }
    }
    </entity>
    

    BallisticMissile is a class component because we wanted to use a tickHandler to animate as aframe-react README suggests.

    For example, don't create requestAnimationFrames to continuously throw React prop updates to A-Frame. Instead, create an A-Frame component with a tick handler such that everything is done in memory with little overhead.

    This did not work. I couldn't figure out how to set a tick handler that actually gets called, so we set up a little game loop in our ``component to make the missile fly.

    class App extends Component {
        state = {
            missile: false,
            launching: false
        }
    
        launch = () => {
            this.setState({
                missile: true,
                launching: true,
                missilePos: { x: 0, y: 0, z: 0 },
                missileA: { ax: 0, ay: .005, az: 0 },
                missileV: { vx: 0, vy: 0, vz: 0 }
            });
            if (!this.launched) {
                window.requestAnimationFrame(() => this.gameLoop());
                this.launched = true;
            }
        }
    
        gameLoop() {
            const { x, y, z } = this.state.missilePos,
                  { vx, vy, vz } = this.state.missileV,
                  { ax, ay, az } = this.state.missileA;
    
            this.setState({
                missilePos: { x: x+vx, y: y+vy, z: z+vz },
                missileV: { vx: vx+ax, vy: vy+ay, vz: vz+az }
            });
            window.requestAnimationFrame(() => this.gameLoop());
        }
    

    On every requestAnimationFrame we update missilePos with velocity on each axis, and missileV with acceleration for each axis. This creates a missile that flies straight up and never stops.

    You can try launching a missile here. You'll need the hiro marker.

    Join me next time as we try to make the missile look better and convince it to fly in a ballistic trajectory.

    🚀

    Published on September 4th, 2017 in arjs, JavaScript, Livecoding, react, reactjs, Technical

    Did you enjoy this article?

    Continue reading about Livecoding Recap #45: Almost building a ballistic missile with React and AR.js

    Semantically similar articles hand-picked by GPT-4

    Senior Mindset Book

    Get promoted, earn a bigger salary, work for top companies

    Learn more

    Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.

    Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.

    Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.

    Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev

    Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization

    Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections

    Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog

    Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️

    Created by Swizec with ❤️