Create a Space Shooter With PlayCanvas: Part 1

PlayCanvas makes it really easy to build WebGL-powered, 3D interactive content for the web. It’s all JavaScript, so it runs natively in the browser without any plugins. It’s a pretty young engine that’s only been around since 2014, but it’s been quickly gaining traction with names like Disney, King and Miniclip using it to develop games.

Create a Space Shooter With PlayCanvas: Part 1

It’s a great tool for two main reasons: First, it’s a fully featured game engine, so it handles everything from graphics and collision to audio and even integration with gamepads/VR. (So you won’t have to hunt down external libraries or worry about browser compatibility issues for most things.) The second, and what I think makes it really stand out, is their browser-based editor.

Create a Space Shooter With PlayCanvas: Part 1

If you’re used to working with the Unity engine, PlayCanvas’s editor should look familiar (it even uses a similar component-based system for stringing together functionality). Unlike Unity, PlayCanvas is not cross-platform and can only publish for the web. However, if the web is all you care about, then this ends up being a big plus, since the engine’s focus on the web makes it really fast and lightweight compared to the competition.

One final note: While the engine itself is free and open source, the online editor and tools are only free for public projects. It’s certainly worth paying for if you’re developing commercial work with it, but you can always just use it as purely a code framework as well for free.

The Final Result

This is what we’ll be creating:

Create a Space Shooter With PlayCanvas: Part 1

You can try out a live demo.

The project itself is public, so you can poke around and/or fork it on its project page.

You don’t need to have any experience with 3D games to follow along, but I will assume some basic familiarity with JavaScript.

Creating Our Own Project From Scratch

The final result is a relatively simple demo where you just fly around and push asteroids, but it covers enough basic functionality that will be useful in making any kind of 3D game. Part 1 will cover the basic setup, working with models, the physics system, and camera controls. Part 2 will cover the bullet system, spawning asteroids, and working with text.

1. Project Setup

Head over to playcanvas.com and create an account.

Once you’re logged in, click on the projects tab in the dashboard and press the big orange new button to create a new project. This should bring up the “new project” box. Select “blank project” and give it a name:

Create a Space Shooter With PlayCanvas: Part 1

Once you’re done, hit the create button at the bottom right. This will send you to the project overview page. Here, you can access your settings and add collaborators. For now we’ll just dive into the project, so click on the big orange editor button.

When you enter your first project, PlayCanvas will show you a lot of hints about its editor. You can dismiss those for now. The main things to note are:

  • The left (hierarchy) panel is a list of all your world objects. This is also where you can add, duplicate, or delete entities from your scene.
  • The right (inspector) panel is where you edit the properties of the selected object. After selecting an object (by clicking on it), you’ll be able to set its position and orientation or attach scripts and components.
  • The bottom (assets) panel contains all your assets. This is where you can upload textures or 3D models as well as create scripts.
  • The center scene is where you can edit and build your game world.

2. Creating an Object

To create a new object in your scene, click on the little plus button at the top of the hierarchy panel:

Create a Space Shooter With PlayCanvas: Part 1

Note: You might accidentally create a new object inside an already existing one. This is useful for building objects composed of multiple parts or that are tied together in some way. You can move objects around the hierarchy panel to control nesting. Drag it onto the Root object to place it back at the top of the hierarchy. 

As an example, I’m going to create a new box and color it red. To give it a custom color, we have to create a new material. You can do this in the assets panel, either by right clicking anywhere inside the panel, or clicking on the little plus icon:

Create a Space Shooter With PlayCanvas: Part 1

Once created, select your material and give it a descriptive name such as “RedMaterial” (you can see the name field in the inspector panel).

Now scroll down the diffuse section and change the color:

Create a Space Shooter With PlayCanvas: Part 1

Once that’s done, go back and select the new box you created (either by clicking on it in the scene or in the hierarchy panel). Then set its material to the custom material we just created:

Create a Space Shooter With PlayCanvas: Part 1

And the box should now be red! Note that the material you created can be attached to as many objects as you like.

3. Adding Physics

To enable physics on an object, we have to add two components: Rigid Body and Collision.

Add the Rigid Body by clicking on “Add Component” in the inspector panel on your object:

Create a Space Shooter With PlayCanvas: Part 1

Make sure its type is set to dynamic:

Create a Space Shooter With PlayCanvas: Part 1

And then add a collision component in the same way.

Now launch your game by clicking on the little play button at the top right of your scene. You should see your box falling down through the floor! To fix that, you’ll have to add a rigid body and collision to the plane as well, making sure that its rigid body type is static (so that it doesn’t fall as well).

Challenge: Just for fun, try adding a sphere and tilt the plane slightly (either on the X or Z axis) to watch it roll off.

A Note on the Component System

It’s worth talking briefly about the component system since it’s a fundamental part of the architecture of PlayCanvas. Conceptually, the idea is to separate functionality from objects. The biggest advantage of that is the ability to compose complex behavior out of smaller modular components.

For example, if you look at the camera in your scene, you’ll notice it’s not a special object. It’s just a generic entity with a camera component attached. You could attach a camera component to anything to turn it into a camera, or attach a rigid body and collision to the camera to turn it into a solid object (try it!).

If you’re curious, you can read more about the advantages and drawbacks of component systems on the Wikipedia page.

4. Adding a Model

Now that you’re comfortable with the basics, we can start putting together our space game. We need at least a ship and an asteroid to work with. There are two ways to add models:

Grab a Model From PlayCanvas’s Library

PlayCanvas has a store (similar to the Unity Asset Store in some ways) where you can find and download assets directly into your project. To access it, just click on library in the assets panel.

The store is very new, so it’s rather sparse, but it’s a good place to find placeholders or assets to experiment with.

I used the hovership asset from the store as my player ship.

Upload Your Own Model

PlayCanvas does support uploading FBX, OBJ, 3DS and COLLADA (DAE) files, but it prefers FBX. You can easily convert any 3D model into FBX by opening it with Blender and exporting it in your desired format.

You can find the asteroid model I used on Blendswap.com. Note that you might want to optimize your 3D models before using them in-game. For example, that asteroid model contains over 200,000 triangles! That might be fine for a special object in the game, but once I added over a hundred asteroids in the scene, things really slowed to a crawl. Blender’s Decimate modifier is an easy way to optimize your models. I used that to cut down the asteroid model to around 7,000 triangles without losing too much detail.

Once the models are in your project (you might need to refresh if you don’t see it right away in your assets panel), you can add them to your scene. The easiest way to do that is to just drag the model into the scene:

Create a Space Shooter With PlayCanvas: Part 1

Just like before, add a rigid body and a collision component to the ship. One trick you could do with collision is to add the actual object’s mesh as its own collision shape. This would result in a pixel-perfect collision mesh, but wouldn’t be very efficient. For this demo, I opted for a simple box as my collision shape (and a sphere for the asteroids) and edited the half-extents to roughly match the shape of the model.

How to Offset the Collision Shape

One problem you might run into when adjusting collision shapes is the inability to offset it from the center. An easy way to get around this (aside from having to offset the model itself in something like Blender before exporting it) is to create a parent object that has the collision and rigid body, and a child object that has the model itself. So you could offset the model as a child relative to the parent that contains the collision.

This is how I have it set up for the demo project, so you can take a look at that to see how that’s done.

5. Changing Gravity & Scene Settings

Since our game is set in space, we need to override the default gravity. You can do this in the scene settings. At the very bottom left of your screen, click on the gear icon. This will open up the settings in the inspector panel. Find the physics section and change the gravity value:

Create a Space Shooter With PlayCanvas: Part 1

To make sure it worked, try launching again and see if the ship is just floating in space.

It’s not quite space without a starry background, so while we’re in the scene settings, let’s add a skybox. You can grab one from the store or just find one online you like. Once you’ve got it, add it in the rendering section:

Create a Space Shooter With PlayCanvas: Part 1

That should give the game a more nebulous feel. This would also be a good time to clean up your scene and delete any test objects we created before.

6. Scripting the Ship

This is where we finally get to write some code. PlayCanvas’s script system is another thing that should be familiar if you’ve used Unity. You create scripts that can be attached to any object, and these scripts can have attributes that are configured on a per-object basis. Script attributes are very useful and accomplish two main things:

  1. Modularity. You can create a script that defines how an enemy moves with a speed attribute, and reuse that for different kinds of enemies with different speeds.
  2. Collaboration. Script attributes can be tweaked directly in the editor without having to touch any code. This allows designers to go in and tweak values themselves without having to bother the programmer or dig through code.

Create a Script

Go to the assets tab and create a new asset of type Script. This will be the code for the ship’s behavior, so name it something like “Fly”. Double click on it to open up the script editor.

The PlayCanvas user manual is a very useful reference when writing scripts, as is the API reference. The auto-completion also makes it really easy to figure out what methods are available. We’ll start by just making our ship rotate. Type this out in the update function:

this.entity.rigidbody.applyTorque(0,1,0);

Inside of any script, this refers to the script component itself, whereas this.entity refers to the object the script is attached to. You can access any of the components attached to the entity this way. Here we’re accessing the rigid body and applying an angular force to it.

Make sure you save your script now.

Attach a Script

Before our script gets too involved, let’s attach it to our ship to see if it works. To do that, just add a script component to your ship, and then add your “fly” script to that. Note that you can only add one script component per object, but you can add multiple scripts inside that component.

Once you launch, you should see your ship rotating!

Add an Attribute

As discussed above, script attributes make our code much more flexible. You can add one by typing this out at the top of your code, right after the first line where the script is created:

Fly.attributes.add('speed', { type: 'number', default:10, title:'Ship Speed' });

In this case, the name of my script is Fly. The only required option is type.

To see the attribute in the editor, go back to your script component, and click on the icon with two arrows on the fly script. This is the parse button which will look for any attributes and update the editor. Your component should now look like this:

Create a Space Shooter With PlayCanvas: Part 1

Finally, to use the value of the attribute in your script, just do this.[attribute_name]. So if we wanted this to be the speed of rotation, we could change our line of code to:

this.entity.rigidbody.applyTorque(0,this.speed,0);

Note: Since there’s no angular damping, the ship will keep rotating faster the longer the force is applied. If you remove the force, it will retain its inertia and keep rotating at the same speed. To change this, set the angular damping in the rigid body component to something above zero. 

Movement With Arrow Keys

Now we want to script it so that we can orient the ship with the arrow keys. A naive approach might look like this:

Fly.prototype.update = function(dt) {
    if(this.app.keyboard.isPressed(pc.KEY_RIGHT)){
        this.entity.rigidbody.applyTorque(0,this.speed,0);
    }
    if(this.app.keyboard.isPressed(pc.KEY_LEFT)){
        this.entity.rigidbody.applyTorque(0,this.speed*-1,0);
    }
    if(this.app.keyboard.isPressed(pc.KEY_UP)){
        this.entity.rigidbody.applyTorque(this.speed*-1,0,0);
    }
    if(this.app.keyboard.isPressed(pc.KEY_DOWN)){
        this.entity.rigidbody.applyTorque(this.speed,0,0);
    }
    
};

Can you tell what the problem is with this script? Try it out. Can you easily point the ship where you want?

Give it some thought before you read on. How would you fix this?

The problem is that we’re applying a force in global coordinates without taking into account where the ship is facing. If the ship is horizontal relative to the camera, and we rotate it on the y axis when we press left/right, then it rotates correctly. But if the ship is vertical, a rotation on the y axis is now a barrel roll.

The same problem would happen if we were trying to move the ship forward too. The direction that is “forward” depends on where the ship is facing and cannot be absolute.

Now conveniently, each entity has three direction vectors we can use: up, right, and forward. To turn left/right, we rotate along the up axis, and up and down we rotate along the right axis. These are up and right relative to the entity. A fixed version would look like this:

Fly.prototype.update = function(dt) {
    var horizontalForce = this.entity.up.clone();
    var verticalForce = this.entity.right.clone();
    
    if(this.app.keyboard.isPressed(pc.KEY_RIGHT)){
        this.entity.rigidbody.applyTorque(horizontalForce.scale(this.speed  * -1));
    }
    if(this.app.keyboard.isPressed(pc.KEY_LEFT)){
        this.entity.rigidbody.applyTorque(horizontalForce.scale(this.speed));
    }
    if(this.app.keyboard.isPressed(pc.KEY_UP)){
        this.entity.rigidbody.applyTorque(verticalForce.scale(this.speed * -1));
    }
    if(this.app.keyboard.isPressed(pc.KEY_DOWN)){
        this.entity.rigidbody.applyTorque(verticalForce.scale(this.speed));
    }
    
};

Adding forward movement is the same idea:

if(this.app.keyboard.isPressed(pc.KEY_Z)){
    this.entity.rigidbody.applyForce(this.entity.forward.clone().scale(-1));
}

If the movement feels off or too slippery, spend some time tweaking the speeds and damping factors to get it to where it feels right.

7. Camera Controls

It’s hard to keep track of a moving ship with a static camera. The easiest way to make the camera follow an object is just by putting the camera as a child of that object.

Try dragging the camera in the hierarchy panel onto your ship. A convenient way to adjust the camera’s view is by switching to that camera’s view in scene. Click on the button at the top of the screen where it says Perspective. This will give you a drop-down with all the different scene views you can select. Select Camera, which should be the furthest one down. This is a special view because whatever you see in the editor is what the camera will see in the game.

Once you’ve adjusted the camera’s view, make sure to switch back to perspective or any other view to avoid accidentally messing up the camera angles.

Tip: If you have an object selected in the hierarchy, but you can’t find it in your scene, press F. This will focus the view on that object and zoom in on it. You can see more keyboard shortcuts by clicking on the keyboard button on the far left of your screen.

At this point, you should have a camera following your ship (as rigid as it may be). (You won’t be able to tell if you’re moving if the camera is moving along and there are no other objects in the world, so try adding some.)

Camera Scripts

A camera that’s just stuck on the player isn’t very interesting. This post on the PlayCanvas blog explores various different types of camera motion. The simplest one we can implement is the look at camera.

To do that, first move the camera back onto the root object.

Next, create a new script called lookAt.

The update function of that script should look like:

LookAt.prototype.update = function(dt) {
    this.entity.lookAt(this.target.getPosition());
};

And it should have an attribute:

LookAt.attributes.add('target', {type: 'entity'});

Now attach that script to the camera object. Press the parse button and set the target to be the ship entity.

Try launching! If all went well, your camera will be staying in place but will just orient itself towards the ship.

You can implement the other types of cameras in the same way. The trailing follow camera mentioned in the blog post ideally looks the nicest, but I’ve found it to be too jittery when the framerate dips a little, so for the final demo, I ended up going with a camera that was attached as a child to the ship but scripted to move and rotate as the ship did.

Conclusion

Don’t worry if some of this feels a little overwhelming. PlayCanvas is a complex engine with a lot of bells and whistles. There’s a lot to explore, and keeping the manual close by is a good way to find your bearings. Another good way to learn is just by finding public projects and looking at how things are made.

Part 2 will start with creating a bullet system, and then adding some asteroids to shoot at, and we’ll top it off by adding an FPS counter and in-game text. If you have any requests or suggestions, or if anything is unclear, please let me know in the comments!