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:
- Get the AR Minecraft guy working ✅
- Use a hand drawn custom marker ❌
- Fire an AR ballistic missile from Korea ✅❌
But AR is hard.
AR is hard pic.twitter.com/SDwTMPjxJw
— Swizec Teller (@Swizec) September 3, 2017
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.
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.
AR is hard pic.twitter.com/SDwTMPjxJw
— Swizec Teller (@Swizec) September 3, 2017
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.
🚀
Continue reading about Livecoding Recap #45: Almost building a ballistic missile with React and AR.js
Semantically similar articles hand-picked by GPT-4
- Livecoding Recap #44: Dipping my toes in AR.js
- Livecoding Recap #46: 3D is hard, WebAR defeats me
- Livecoding recap #41: Towards declarative 3D scenes with React and Three.js
- Livecoding #16: canvas.drawImage performance is weird but magical
- Livecoding Recap: A new more versatile React pattern
Learned something new?
Read more Software Engineering Lessons from Production
I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.
Subscribe below 👇
Software Engineering Lessons from Production
Join Swizec's Newsletter and get insightful emails 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.
"Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"
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 ❤️