Skip to content

Getting Started

Andrew McPherson edited this page Mar 26, 2016 · 12 revisions

Getting Started

Just getting started? Let's do this!!

STEP 1: GET READY

NodeJS

Node is a native javascript interpreter, so you write javascript for not only the browser but also the terminal. You can install it from the binaries provided at www.nodejs.org. You can also install it via homebrew or aptitude, but be warned this will install it as nodejs and not node.

To test that you've installed everything correctly, try to access it from the terminal.

$ node --version
5.x.x
$ npm --version
3.x.x

If they print out their versions, then you've successfully installed them. But if you get any errors, your node or npm might not be in your PATH.

Git

Git is a version control system. We use it to handle saving and sharing your code. It integrates with Github, so you can push you code here. You can install it via www.git-scm.com.

To test that you've installed everything correctly, try to access it from the terminal.

$ git --version
2.x.x

Chrome

Chrome is an awesome browser. And although our game will be playable on all the browsers, we particularly support Chrome, since we're bundling the game with the chromium executable. You can install it via www.google.com/chrome.

Atom?

Atom is an awesome editor. If you don't have a plain-text editor yet, now is a good time to install one! You can use whatever you want, but I recommend Atom. But again, you can use whatever you're most comfortable with.

STEP 2: GET CODE

Get on Github

You need an account. You can signup for free. If you're a student, you can get upgrade to an academic account through the Student Developer Package.

Fork it

After forking this repository, you'll have your own copy of the code at https://github.com/yourusername/enchiridion.

How to fork a repository

Clone it

On your computer, clone your repository.

$ git clone https://github.com/yourusername/enchiridion

Remember to clone from your fork, using your username!

Branch it

$ get checkout getting-started

This is a branch prepared for this guide.

STEP 3: BUILD CODE

Get node

https://nodejs.org

Run build script

npm install
node build server

Troubleshoot

Got some errors? It happens.

Check that you're not using an outdated version of Node. Some package managers are distributing outdated versions of Node. You can see what version of node you have by running node --version. We support anything greater than or equal to v0.12. If you are running an outdated version, you can install nvm to handle version-switching.

Delete your node_modules directory, and run npm install again.

If none of that helps, leave a comment on this gist and I'll fix it as fast as I can.

STEP 4: WRITE CODE

Hello World!

Open up index.js. Type:

console.log("Hello World")

HOLY SHIT it logged it!

Note that, while the build server is running, any changes you make to any of the files are reflected immediately on the page via reloading the page. So it's nice to do a half-and-half environment flow, so you can see both the page and your editor.

DOM for DOMmies (ha ha)

Okay, let's get some React up in here! But first we need to install it.

$ npm install --save-dev react
$ npm install --save-dev react-dom

And then import it into our script.

var React = require("react")
var ReactDOM = require("react-dom")

And now we can use it!

var mount = (
    <div>
        <h1>Hello World!</h1>
        <h3>How are you today?</h3>
    </div>
)

ReactDOM.render(mount, document.getElementById("mount"))

What?! HTML in Javascript??

That's right! With a bit of React, we can programmatically generate HTML! It should something like this:

Using Components

The coolest part of React, though, is that we can define our own custom components, which are composed of other bits of HTML, CSS and JS, which we can reuse throughout our code. Included in the getting-started branch is some code like that, which lives in the ./scripts/render directory.

var AspectRatioFrame = require("./scripts/render/AspectRatioFrame.js")

var mount = (
    <AspectRatioFrame width={320} height={180}>
        <h1>Hello World!</h1>
        <h3>How are you today?</h3>
    </AspectRatioFrame>
)

Which should look something like this:

The AspectRatioFrame component, when given a width and height, will resize itself (and all of the content inside of it) to fit the screen, while maintaining those dimensions. Handy, huh?

Entity

Also provided is the Entity component, which we'll be using for just about everything.

var Entity = require("./scripts/render/Entity.js")
var AspectRatioFrame = require("./scripts/render/AspectRatioFrame.js")

var mount = (
    <AspectRatioFrame width={320} height={180}>
        <Entity/>
    </AspectRatioFrame>
)

Do you see a little untextured square in the corner? That's an entity! And just like the AspectRatioFrame, we can pass parameters to Entity.

var adventurer = {
    position: {x: 2, y: 2},
    color: "#C00",
}

var mount = (
    <AspectRatioFrame width={320} height={180}>
        <Entity data={adventurer}/>
    </AspectRatioFrame>
)

We can position the entity along our grid using the position attribute, and color the entity using the color attribute. A full list of all the attributes that the Entity component supports are detailed in the documentation.

Class

So my favorite part of React is how we can declaratively establish how we want to render the game using our HTML. And from then on, we can just manipulate the state of the game using JS, and it'll automatically update the HTML!

Buuuuut as it stands right now, the HTML we're generating is static; we can't update it. We need to convert HTML to a dynamic component, like this:

var Mount = React.createClass({
    render: function() {
        return (
            <AspectRatioFrame width={320} height={180}>
                <Entity data={adventurer}/>
            </AspectRatioFrame>
        )
    }
})

var render = ReactDOM.render(<Mount/>, document.getElementById("mount"))

And now that it's dynamic react, and not static react, we can use it as a component, like we did with AspectRatioFrame and Entity. Which is important for our game loop.

Game Loop

So up at the top of the page, import Afloop.

var Afloop = require("afloop")

And then make a loop.

var loop = new Afloop(function() {
    adventurer.position.x += 0.1
    render.forceUpdate()
})

Input

Up at the top, import Keyb.

var Keyb = require("keyb")

And use it in the loop.

var loop = Afloop(function() {
    if(Keyb.isJustDown("<up>")) {
        adventurer.position.y -= 1
    } if(Keyb.isJustDown("<down>")) {
        adventurer.position.y += 1
    } if(Keyb.isJustDown("<left>")) {
        adventurer.position.x -= 1
    } if(Keyb.isJustDown("<right>")) {
        adventurer.position.x += 1
    }
    render.forceUpdate()
})

A better adventurer

We don't want a boring square forever. Let's import an image!

var image = require("./images/shapes/entities/0.png")
console.log(image)

Which will log something like this:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYAQMAAADJbu9PAAAABlBMV…DUP2Eo/sNQfI+h+AlIpPwDg9oHBp4fDDw1DBwKDBIGyDoBN70Pb2m0Lv0AAAAASUVORK5CYII=

This is a Data URI, a base-64 encoded version of our image! We can pass this to entity to give it an image.

var adventurer = {
    position: {x: 2, y: 2},
    shape: require("./images/shapes/entities/0.png"),
    color: "#C00",
}

The graphics were put together at the Oryx Design Lab, and we use them as a mask.

But we're not done yet, we can also add a transition property

var adventurer = {
    position: {x: 2, y: 2},
    shape: require("./images/shapes/entities/0.png"),
    transition: {duration: 0.2},
    color: "#C00",
}

And now, whenever the entity is moved, they take 0.2 seconds to transition between their positions.

Object Oriented

So, like, ECMA6 came out, and I was all like:

Because now we can use proper javascript classes. Like so:

class Adventurer {
    constructor() {
        this.position = {x: 2, y: 2}
        this.shape = require("./images/shapes/entities/0.png")
        this.transition = {duration: 0.2}
        this.color = "#C00"
    }
}

var adventurer = new Adventurer()

This refactor affords us a lot, because now we can instantiate multiple instances of a given class (like monsters), employ encapsulation to handle data, and extend classes via inheritance.

We can also refactor our updating function as a method of our class.

class Adventurer {
    constructor() {
        this.position = {x: 2, y: 2}
        this.shape = require("./images/shapes/entities/0.png")
        this.transition = {duration: 0.2}
        this.color = "#C00"
    }
    update() {
        if(Keyb.isJustDown("<up>")) {
            this.position.y -= 1
        } if(Keyb.isJustDown("<down>")) {
            this.position.y += 1
        } if(Keyb.isJustDown("<left>")) {
            this.position.x -= 1
        } if(Keyb.isJustDown("<right>")) {
            this.position.x += 1
        }
    }
}
var loop = Afloop(function() {
    adventurer.update()
    render.forceUpdate()
})

STEP ?: CELEBRATE

Aaaaand you're done! Good work!

What's next? Time to start Contributing!