PlayCanvas is a popular 3D WebGL game engine originally created by Will Eastcott and Dave Evans. It is open sourced on GitHub, with an editor available online and good documentation. It is free for public projects and two team members, but there are also paid plans if you'd like to run a commercial, private project with more developers.
Games and demos
PlayCanvas is known for a few games showcasing its possibilities. Tanx is a multiplayer tank game where you can fight and shoot directly at other players, and Swooop let's you fly your plane around the magical island. It's not only for games - you can find demos and visualizations like the Star Lord or BMW i8 that take advantage of the engine and showcase what's possible. You can check the list of featured demos for inspiration.
Engine vs editor
The engine itself can be used as a standard library by including its file containing the source code in a project, so you can start coding right away, but PlayCanvas toolset also comes with an online editor which you can use to drag and drop components onto the scene - a great way to create games and applications if you're more of a designer than a coder. Those approaches are offering different experiences, but work equally well - after all the more tools to choose from, the better.
PlayCanvas engine
Built for modern browsers, PlayCanvas is a fully featured 3D game engine with resource loading, entity and component system, advanced graphic manipulation, collision and physics (built with ammo.js), audio and input from various devices (including gamepads). That's quite an impressive list of features - let's see some in action.
The engine demo
We can try a simple demo first - cube rendered on the screen. If you had the time to check the Building up a basic demo with Three.js article you've probably noticed that PlayCanvas have similar concepts: camera, light and objects. To play with all that though we have to organize our development environment first.
Environment setup
To start developing with PlayCanvas, you don't need much. You should:
- Make sure you are using a modern browser with good WebGL support, such as the latest Firefox or Chrome.
- Create a directory to store your experiments in.
- Save a copy of the latest PlayCanvas engine inside your directory.
- Open the PlayCanvas documentation in a separate tab — it is useful to refer to.
HTML structure
Here's the HTML structure we will use.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>MDN Games: PlayCanvas demo</title> <style> body { margin: 0; padding: 0; } canvas { width: 100%; height: 100%; } </style> </head> <body> <script src="js/playcanvas-latest.js"></script> <canvas id="application-canvas"></canvas> <script> var canvas = document.getElementById("application-canvas"); /* all our JavaScript code goes here */ </script> </body> </html>
It contains some basic information like the document <title>, and some CSS to set the width and height of the <canvas> element that PlayCanvas will use to 100% so that it will fill the entire available viewport space. The first <script> element includes the PlayCanvas library in the page, and we will write our example code into the second one. There is one helper variable already included, which will store the reference to the <canvas> element.
Before reading on, copy this code to a new text file, and save it in your working directory as index.html
.
PlayCanvas application
To begin developing our game we have to create the PlayCanvas application first (using the given <canvas> element), and then start the update loop.
var app = new pc.Application(canvas); app.start();
The pc
object is a global handle that contains all the PlayCanvas functions available in the engine.
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW); app.setCanvasResolution(pc.RESOLUTION_AUTO);
Next, we'll set the Canvas to fill the window, and then automatically change its resolution to be the same as the Canvas size.
Camera
Now when the setup code is in place we need to think about the usual components of the scene: camera, lights and objects. Let's start with the camera - add these lines to your code, below the prevous ones.
var camera = new pc.Entity(); camera.addComponent("camera", { clearColor: new pc.Color(0.8, 0.8, 0.8) }); app.root.addChild(camera); camera.setPosition(0, 0, 7);
The code above will create a new Entity
and add a camera
component to it with the light gray clearColor
which will be visible as the background. Then the camera
object is added to the root of our application and positioned to be 7 units away from the center of the scene on the z
axis, so we can make some place for the objects that we will create later on.
Note: The distance values (e.g. for the camera z position) are unitless, and can basically be anything you deem suitable for your scene — milimeters, meters, feet, or miles — it's up to you.
Try saving the file and loading it in your browser. You should now see a gray window. Congratulations!
Geometry
Now when the scene is properly rendering we can start adding 3D shapes to it. To speed up development PlayCanvas provides a bunch of predefined primitives that you can use to create shapes instantly in a single line of code. There are cubes, spheres, cylinders and more complicated shapes available. Drawing everything for given shape is taken care of by the engine, so we can focus on the high level coding. Let's start by defining the geometry for a cube shape — add the new code below your previous additions:
var box = new pc.Entity(); box.addComponent("model", { type: "box" }); app.root.addChild(box); box.rotate(10, 15, 0);
It will create and Entity
with the box
model component and add it to the root of the application, our scene. We can rotate the box a bit to see it's actually a 3D cube and not a square.
As you can already see the cube is visible, but it looks black. To make it look better we have to shine some light onto it.
Lights
The basic light types in PlayCanvas are directional and ambient. The first type is a directional light placed somewhere on the scene while the scond one reflects the light from the first type, so it looks more natural, and can be set globally. Again, add the new code below your previous additions.
var light = new pc.Entity(); light.addComponent('light'); app.root.addChild(light); light.rotate(45, 0, 0);
It will create a light Entity
component and add it to the scene. We can rotate the light on the x
axis to make it shine on more than one side of the cube. It's time to add the ambient light:
app.scene.ambientLight = new pc.Color(0.2, 0.2, 0.2);
The code above assign a dark grey ambient light for the whole scene. The box look better now, but it could get some colors to look even better - for that we need to create material for it.
Material
The basic PlayCanvas material is called PhongMaterial - add those lines below the previous code.
var boxMaterial = new pc.PhongMaterial(); boxMaterial.diffuse.set(0, 0.58, 0.86); boxMaterial.update(); box.model.model.meshInstances[0].material = boxMaterial;
By diffusing the light on the object we can set it's own color - in this case the familiar blue one.
Note: The values are given in the 0-1
float
range instead of 0-255
int
range for the RGB scale.
After the material is created and color is set it have to be updated so our changes are going to be applied. Then all we need is to set the box
's material to the newly created boxMaterial
.
Congratulations, you've created your first object in a 3D environment using PlayCanvas! It was easier than you thought, right? Here's how it should look:
And here's the code we have created so far:
{{JSFiddleEmbed("https://jsfiddle.net/end3r/cqs6pg3x/","","350")}}
You can also check it out on GitHub.
More shapes
Now we will add more shapes to the scene. Let's move the cube 2 units to the left to make space for some friends — add the following line just below the previous one:
box.translate(-2, 0, 0);
Now let's add a new shape - how about a cylinder?
Cylinder
Add the following lines to your JavaScript code:
var cylinder = new pc.Entity(); cylinder.addComponent("model", { type: "cylinder" }); app.root.addChild(cylinder); cylinder.rotate(15, 0, 0);
It looks very similar to creating a cube, but instead of the box
component we are adding a cylinder
. It is also rotated on the x
axis to show it's actually a 3D shape. To make the cylinder have a color, let's say yellow, we need to create the material for it.
var cylinderMaterial = new pc.PhongMaterial(); cylinderMaterial.diffuse.set(1, 0.58, 0); cylinderMaterial.update(); cylinder.model.model.meshInstances[0].material = cylinderMaterial;
It looks and works the same as the one we have created for the cube, but with a different color.
Cone
Creating a cone and its material looks almost exacly the same as the cylinder.
var cone = new pc.Entity(); cone.addComponent("model", { type: "cone" }); app.root.addChild(cone); cone.translate(2, 0, 0); var coneMaterial = new pc.PhongMaterial(); coneMaterial.diffuse.set(0.9, 0.9, 0.9); coneMaterial.update(); cone.model.model.meshInstances[0].material = coneMaterial;
The code above will create a new cone
, add it to the app
and move it by 2 units to the right so it's not interfering with the cylinder. Then the material is created, set with the gray color which is then applied, and it is assigned to the cone Entity
.
Here's how it should look right now:
This looks a little bit boring though. In a game something is usually happening — we can see animations and such — so let's try to breathe a little life into those shapes by animating them.
Animation
We already used translate
or rotate
to adjust the position of the shapes; we could also change thier positions directly with setPosition
, or scale them. To show actual animation, we need to make changes to these values inside the render loop, so they are updated on every frame. There's a special update
event that we can use for that - add the following code just below the previous one:
var timer = 0; app.on("update", function (deltaTime) { timer += deltaTime; // code executed on every frame });
It takes the deltaTime
as the parameter, so we have the relative time that has passed since the previous invocation of this update. For time based animations we'll use a timer
variable that will store the time that has passed since the start of the app by adding the deltaTime
to it on every update.
Rotation
Rotating is quite easy — all you need to do is to add a defined value to the given direction of the rotation on each frame. Add this line of code inside the app.on("update")
function right after the addition of the deltaTime
to the timer
variable:
box.rotate(deltaTime*10, deltaTime*20, deltaTime*3);
It will rotate the box
by deltaTime*10
on the x
axis, deltaTime*20
on the y
axis and deltaTime*30
on the z
axis respectively - by doing so it will look like a smooth animation.
Scaling
We can also scale a given object - there's a function for that called setLocalScale
.
cylinder.setLocalScale(1, Math.abs(Math.sin(timer)), 1);
This way we'll be able to use Math.sin
and end up with quite an interesting result: this will scale the cylinder and repeat the whole process, as sin
is a periodic function. We're wrapping the y
scale value in Math.abs
to pass the absolute values (greater or equal to 0), because sin
is between -1 and 0, and for negative values the cylinder might render unexpectedly (in this case it looks black half the time).
Now onto the movement part.
Moving
Beside rotation and scaling we can also move objects around the scene. Add the following code to achieve that.
cone.setPosition(2, Math.sin(timer*2), 0);
This will move the cone
up and down by applying the sin
value to the y
axis on each frame, with a little bit of adjustment to make it look cooler. Try changing the value to see how it affects the animations.
Conclusion
Here's the final piece of the code:
{{JSFiddleEmbed("https://jsfiddle.net/end3r/auvcLoc4/","","350")}}
You can also see it on GitHub and fork the repository if you want to play with it yourself locally. Now you know the basics of PlayCanvas engine, congatulations!
PlayCanvas Editor
Instead of coding everything from scratch you can also use the online editor. It might be a lot easier for you if you don't like to code. The PlayCanvas Editor is free - all you have to do is register your account and login.
TODO: EDITOR INSTRUCTIONS, SUMMARY