Learning Pixi ============= A step-by-step introduction to making games and interactive media with the [Pixi rendering engine](https://github.com/pixijs/pixi.js). **[Updated for Pixi v4.0.0](https://github.com/pixijs/pixi.js/releases/tag/v4.0.0)**. If you like this tutorial, [you'll love the book, which contains 80% more content!](http://www.springer.com/us/book/9781484210956) ### Table of contents: 1. [Introduction](#introduction) 2. [Setting up](#settingup) 1. [Installing Pixi the simple way](#installingpixithesimpleway) 2. [Installing Pixi with Git](#installingpixiwithgit) 3. [Installing Pixi with Node and Gulp](#installingpixiwithnodeandgulp) 3. [Creating the stage and renderer](#renderer) 4. [Pixi sprites](#sprites) 5. [Loading images into the texture cache](#loading) 6. [Displaying sprites](#displaying) 1. [Using Aliases](#usingaliases) 2. [A little more about loading things](#alittlemoreaboutloadingthings) 1. [Make a sprite from an ordinary JavaScript Image object or Canvas](#makeaspritefromanordinaryjavascriptimageobject) 2. [Assigning a name to a loaded file](#assigninganametoaloadingfile) 3. [Monitoring load progress](#monitoringloadprogress) 4. [More about Pixi's loader](#moreaboutpixisloader) 7. [Positioning sprites](#positioning) 8. [Size and scale](#sizenscale) 9. [Rotation](#rotation) 10. [Make a sprite from a tileset sub-image](#tileset) 11. [Using a texture atlas](#textureatlas) 12. [Loading the texture atlas](#loadingatlas) 13. [Creating sprites from a loaded texture atlas](#creating-sprites-from-a-loaded-texture-atlas) 14. [Moving Sprites](#movingsprites) 15. [Using velocity properties](#velocity) 16. [Game states](#gamestates) 17. [Keyboard Movement](#keyboard) 18. [Grouping Sprites](#grouping) 1. [Local and global positions](#localnglobal) 2. [Using a ParticleContainer to group sprites](#spritebatch) 19. [Pixi's Graphic Primitives](#graphic) 1. [Rectangle](#rectangles) 2. [Circles](#circles) 3. [Ellipses](#ellipses) 4. [Rounded rectangles](#rounded-rectangles) 5. [Lines](#lines) 6. [Polygons](#polygons) 20. [Displaying text](#text) 21. [Collision detection](#collision) 1. [The hitTestRectangle function](#the-hittestrectangle-function) 22. [Case study: Treasure Hunter](#casestudy) 1. [Initialize the game in the setup function](#initialize) 1. [Creating the game scenes](#gamescene) 2. [Making the dungeon, door, explorer and treasure](#makingdungon) 3. [Making the blob monsters](#makingblob) 4. [Making health bar](#healthbar) 5. [Making message text](#message) 2. [Playing the game](#playing) 3. [Moving the explorer](#movingexplorer) 1. [Containing movement](#containingmovement) 4. [Moving the monsters](#movingmonsters) 5. [Checking for collisions](#checkingcollisions) 6. [Reaching the exit door and ending game](#reachingexit) 23. [More about sprites](#spriteproperties) 24. [Taking it further](#takingitfurther)
i.[Hexi](#hexi)
25. [Supporting this project](#supportingthisproject) Introduction ------------ Pixi’s is an extremely fast 2D sprite rendering engine. What does that mean? It means that it helps you to display, animate and manage interactive graphics so that it's easy for you to make games and applications using JavaScript and other HTML5 technologies. It has a sensible, uncluttered API and includes many useful features, like supporting texture atlases and providing a streamlined system for animating sprites (interactive images). It also gives you a complete scene graph so that you can create hierarchies of nested sprites (sprites inside sprites), as well as letting you attach mouse and touch events directly to sprites. And, most importantly, Pixi gets out of your way so that you can use as much or as little of it as you want to, adapt it to your personal coding style, and integrate it seamlessly with other useful frameworks. Pixi’s API is actually a refinement of a well-worn and battle-tested API pioneered by Macromedia/Adobe Flash. Old-skool Flash developers will feel right at home. Other current sprite rendering frameworks use a similar API: CreateJS, Starling, Sparrow and Apple’s SpriteKit. The strength of Pixi’s API is that it’s general-purpose: it’s not a game engine. That’s good because it gives you total expressive freedom to make anything you like, and wrap your own custom game engine around it. In this tutorial you’re going to find out how to combine Pixi’s powerful image rendering features and scene graph to start making games. You’re also going to learn how to prepare your game graphics with a texture atlas, how to make particle effects using the Proton particle engine, and how to integrate Pixi into your own custom game engine. But Pixi isn't just for games - you can use these same techniques to create any interactive media applications. That means apps for phones! What do you need to know before you get started with this tutorial? You should have a reasonable understanding of HTML and JavaScript. You don't have to be an expert, just an ambitious beginner with an eagerness to learn. If you don't know HTML and JavaScript, the best place to start learning it is this book: [Foundation Game Design with HTML5 and JavaScript](http://www.apress.com/9781430247166) I know for a fact that it's the best book, because I wrote it! There are also some good internet resources to help get you started: [Khan Academy: Computer Programming](http://www.khanacademy.org/computing/cs) [Code Academy: JavaScript](http://www.codecademy.com/tracks/javascript) Choose whichever best suits your learning style. Ok, got it? Do you know what JavaScript variables, functions, arrays and objects are and how to use them? Do you know what [JSON data files](http://www.copterlabs.com/blog/json-what-it-is-how-it-works-how-to-use-it/) are? Have you used the [Canvas Drawing API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_graphics_with_canvas)? To use Pixi, you'll also need to run a webserver in your root project directory. Do you know what a webserver is and how to launch one in your project folder? The best way is to use [node.js](http://nodejs.org) and then to install the extremely easy to use [http-server](https://github.com/nodeapps/http-server). However, you need to be comfortable working with the Unix command line if you want to do that. You can learn how to use Unix [in this video](https://www.youtube.com/watch?feature=player_embedded&v=cX9ASUE3YAQ) and, when you're finished, follow it with [this video](https://www.youtube.com/watch?v=INk0ATBbclc). You should learn how to use Unix - it only takes a couple of hours to learn and is a really fun and easy way to interact with your computer. But if you don't want to mess around with the command line just yet, try the Mongoose webserver: [Mongoose](http://cesanta.com/mongoose.shtml) Or, just write your all your code using the excellent [Brackets text editor](http://brackets.io). Brackets automatically launches a webserver and browser for you when you click the lightening bolt button in its main workspace. Now if you think you're ready, read on! (Request to readers: this is a *living document*. If you have any questions about specific details or need any of the content clarified, please create an **issue** in this GitHub repository and I'll update the text with more information.) Setting up ---------- Before you start writing any code, create a folder for your project, and launch a webserver in the project's root directory. If you aren't running a webserver, Pixi won't work. Next, you need to install Pixi. There are two ways to do it: the **simple** way, with **Git** or with **Gulp and Node**. ### Installing Pixi the simple way The version used for this introduction is **v4.0.0** and you can find `pixi.min.js` file on [Pixi's release page for v4.0.0](https://github.com/pixijs/pixi.js/releases/tag/v4.0.0). Or you can get the latest version from [Pixi's main release page](https://github.com/pixijs/pixi.js/releases). This one file is all you need to use Pixi. You can ignore all the other files in the repository: **you don't need them.** Next, create a basic HTML page, and use a ` ``` Here's a basic HTML page that you could use to link Pixi and test that it's working: ```html Hello World ``` If Pixi is linking correctly, something like this will be displayed in your web browser's JavaScript console by default: ``` Pixi.js 4.0.0 - ✰ WebGL ✰ http://www.pixijs.com/ ♥♥♥ ``` Now you can start working with Pixi! ### Installing Pixi with Git You can also use Git to install use Pixi. (What is **git**? If you don't know [you can find out here](https://github.com/kittykatattack/learningGit).) This has some advantages: you can just run `git pull origin master` from the command line to update Pixi to the latest version. And, if you think you've found a bug in Pixi, you can fix it and submit a pull request to have the bug fix added to the main repository. To clone the Pixi repository with Git, `cd` into your root project directory and type: ``` git clone git@github.com:pixijs/pixi.js.git ``` This automatically creates a folder called `pixi.js` and loads the **latest version** of Pixi into it. Keep in mind that this manual is tailored around *version 4.0.0*. To get this version simply checkout cloned `pixi.js` repository using tag like this: ``` git checkout tags/v4.0.0 ``` After Pixi is installed, create a basic HTML document, and use a ` ``` (If you prefer, you could link to the `pixi.min.js` file instead as I suggested in the previous section. The minified file might actually run slightly faster, and it will certainly load faster. The advantage to using the un-minified plain JS file is that if the compiler thinks there's a bug in Pixi's source code, it will give you an error message that displays the questionable code in a readable format. This is useful while you're working on a project, because even if the bug isn't in Pixi, the error might give you a hint as to what's wrong with your own code.) In this **Learning Pixi** repository (what you're reading now!) you'll find a folder called `examples`. Open it and you'll find a file called `helloWorld.html`. Assuming that the webserver is running in this repository's root directory, this is how the `helloWorld.html` file correctly links to Pixi and checks that it's working: ```html Hello World ``` If Pixi is linking correctly, something like this will be displayed in your web browser's JavaScript console by default: ``` Pixi.js 4.0.0 - ✰ WebGL ✰ http://www.pixijs.com/ ♥♥♥ ``` ### Installing Pixi with Node and Gulp You can also install Pixi using [Node](https://nodejs.org) and [Gulp](http://gulpjs.com). If you need to do a custom build of Pixi to include or exclude certain features, this is the route you should take. [See Pixi's GitHub repository for details on how](https://github.com/GoodBoyDigital/pixi.js). But, in general there's no need to do this. Creating the renderer and stage ------------------------------- Now you can start using Pixi! But how? The first step is to create a rectangular display area that you can start displaying images on. Pixi has a `renderer` object that creates this for you. It automatically generates an HTML `` element and figures out how to display your images on the canvas. You then need to create a special Pixi `Container` object called the `stage`. As you'll see ahead, this stage object is going to be used as the root container that holds all the things you want Pixi to display. Here’s the code you need to write to create a `renderer` and `stage`. Add this code to your HTML document between the ` ``` You can see in the code above that all the blobs are created using a `for` loop. Each `blob` is spaced evenly along the `x` axis like this: ```js var x = spacing * i + xOffset; blob.x = x; ``` `spacing` has a value 48, and `xOffset` has a value of 150. What this means is the first `blob` will have an `x` position of 150. This offsets it from the left side of the stage by 150 pixel. Each subsequent `blob` will have an `x` value that's 48 pixels greater than the `blob` created in the previous iteration of the loop. This creates an evenly spaced line of blob monsters, from left to right, along the dungeon floor. Each `blob` is also given a random `y` position. Here's the code that does this: ```js var y = randomInt(0, stage.height - blob.height); blob.y = y; ``` The `blob`'s `y` position could be assigned any random number between 0 and 512, which is the value of `stage.height`. This works with the help of a custom function called `randomInt`. `randomInt` returns a random number that's within a range between any two numbers you supply. ```js randomInt(lowestNumber, highestNumber) ``` That means if you want a random number between 1 and 10, you can get one like this: ```js var randomNumber = randomInt(1, 10); ``` Here's the `randomInt` function definition that does all this work: ```js function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } ``` `randomInt` is a great little function to keep in your back pocket for making games - I use it all the time. Moving Sprites -------------- You now know how to display sprites, but how do you make them move? That's easy: create a looping function using `requestAnimationFrame`. This is called a **game loop**. Any code you put inside the game loop will update 60 times per second. Here's some code you could write to make the `cat` sprite move at a rate of 1 pixel per frame. ```js function gameLoop() { //Loop this function at 60 frames per second requestAnimationFrame(gameLoop); //Move the cat 1 pixel to the right each frame cat.x += 1; //Render the stage to see the animation renderer.render(stage); } //Start the game loop gameLoop(); ``` If you run this bit of code, you'll see the sprite gradually move to the right side of the stage. ![Moving sprites](/examples/images/screenshots/15.png) And that's really all there is to it! Just change any sprite property by small increments inside the loop, and they'll animate over time. If you want the sprite to animate in the opposite direction (to the left), just give it a negative value, like `-1`. You'll find this code in the `movingSprites.html` file - here's the complete code: ```js //Aliases var Container = PIXI.Container, autoDetectRenderer = PIXI.autoDetectRenderer, loader = PIXI.loader, resources = PIXI.loader.resources, Sprite = PIXI.Sprite; //Create a Pixi stage and renderer var stage = new Container(), renderer = autoDetectRenderer(256, 256); document.body.appendChild(renderer.view); //Load an image and the run the `setup` function loader .add("images/cat.png") .load(setup); //Define any variables that are used in more than one function var cat; function setup() { //Create the `cat` sprite cat = new Sprite(resources["images/cat.png"].texture); cat.y = 96; stage.addChild(cat); //Start the game loop gameLoop(); } function gameLoop(){ //Loop this function 60 times per second requestAnimationFrame(gameLoop); //Move the cat 1 pixel per frame cat.x += 1; //Render the stage renderer.render(stage); } ``` (Notice that the `cat` variable needs to be defined outside the `setup` and `gameLoop` functions so that you can access it inside both of them.) You can animate a sprite's scale, rotation, or size - whatever! You'll see many more examples of how to animate sprites ahead. Using velocity properties ------------------------- To give you more flexibility, its a good idea to control a sprite's movement speed using two **velocity properties**: `vx` and `vy`. `vx` is used to set the sprite's speed and direction on the x axis (horizontally). `vy` is used to set the sprite's speed and direction on the y axis (vertically). Instead of changing a sprite's `x` and `y` values directly, first update the velocity variables, and then assign those velocity values to the sprite. This is an extra bit of modularity that you'll need for interactive game animation. The first step is to create `vx` and `vy` properties on your sprite, and give them an initial value. ```js cat.vx = 0; cat.vy = 0; ``` Setting `vx` and `vy` to 0 means that the sprite isn't moving. Next, in the game loop, update `vx` and `vy` with the velocity that you want the sprite to move at. Then assign those values to the sprite's `x` and `y` properties. Here's how you could use this technique to make the cat sprite move down and to right at one pixel each frame: ```js function setup() { //Create the `cat` sprite cat = new Sprite(resources["images/cat.png"].texture); stage.addChild(cat); //Initialize the cat's velocity variables cat.vx = 0; cat.vy = 0; //Start the game loop gameLoop(); } function gameLoop(){ //Loop this function 60 times per second requestAnimationFrame(gameLoop); //Update the cat's velocity cat.vx = 1; cat.vy = 1; //Apply the velocity values to the cat's //position to make it move cat.x += cat.vx; cat.y += cat.vy; //Render the stage renderer.render(stage); } ``` When you run this code, the cat will move down and to the right at one pixel per frame: ![Moving sprites](/examples/images/screenshots/16.png) What if you want to make the cat move in a different direction? To make the cat move to the left, give it a `vx` value of `-1`. To make it move up, give the cat a `vy` value of `-1`. To make the cat move faster, give it larger `vx` and `vy` values, like `3`, `5`, `-2`, or `-4`. You'll see ahead how modularizing a sprite's velocity with `vx` and `vy` velocity properties helps with keyboard and mouse pointer control systems for games, as well as making it easier to implement physics. Game states ----------- As a matter of style, and to help modularize your code, I recommend structuring your game loop like this: ```js //Set the game's current state to `play`: var state = play; function gameLoop() { //Loop this function at 60 frames per second requestAnimationFrame(gameLoop); //Update the current game state: state(); //Render the stage to see the animation renderer.render(stage); } function play() { //Move the cat 1 pixel to the right each frame cat.x += 1; } ``` You can see that the `gameLoop` is calling a function called `state` 60 times per second. What is the `state` function? It's been assigned to `play`. That means all the code in the `play` function will also run at 60 times per second. Here's how the code from the previous example can be re-factored to this new model: ```js //Define any variables that are used in more than one function var cat, state; function setup() { //Create the `cat` sprite cat = new Sprite(resources["images/cat.png"].texture); cat.y = 96; cat.vx = 0; cat.vy = 0; stage.addChild(cat); //Set the game state state = play; //Start the game loop gameLoop(); } function gameLoop(){ //Loop this function 60 times per second requestAnimationFrame(gameLoop); //Update the current game state: state(); //Render the stage renderer.render(stage); } function play() { //Move the cat 1 pixel to the right each frame cat.vx = 1 cat.x += cat.vx; } ``` Yes, I know, this is a bit of [head-swirler](http://www.amazon.com/Electric-Psychedelic-Sitar-Headswirlers-1-5/dp/B004HZ14VS)! But, don't let it scare you and spend a minute or two walking through in your mind how those functions are connected. As you'll see ahead, structuring your game loop like this will make it much, much easier to do things like switching game scenes and levels. Keyboard Movement ----------------- With just a little more work you can build a simple system to control a sprite using the keyboard. To simplify your code, I suggest you use this custom function called `keyboard` that listens for and captures keyboard events. ```js function keyboard(keyCode) { var key = {}; key.code = keyCode; key.isDown = false; key.isUp = true; key.press = undefined; key.release = undefined; //The `downHandler` key.downHandler = function(event) { if (event.keyCode === key.code) { if (key.isUp && key.press) key.press(); key.isDown = true; key.isUp = false; } event.preventDefault(); }; //The `upHandler` key.upHandler = function(event) { if (event.keyCode === key.code) { if (key.isDown && key.release) key.release(); key.isDown = false; key.isUp = true; } event.preventDefault(); }; //Attach event listeners window.addEventListener( "keydown", key.downHandler.bind(key), false ); window.addEventListener( "keyup", key.upHandler.bind(key), false ); return key; } ``` The `keyboard` function is easy to use. Create a new keyboard object like this: ```js var keyObject = keyboard(asciiKeyCodeNumber); ``` It's one argument is the ASCII key code number of the keyboad key that you want to listen for. [Here's a list of ASCII keyboard code numbers](http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00000520.html). Then assign `press` and `release` methods to the keyboard object like this: ```js keyObject.press = function() { //key object pressed }; keyObject.release = function() { //key object released }; ``` Keyboard objects also have `isDown` and `isUp` Boolean properties that you can use to check the state of each key. Take a look at the `keyboardMovement.html` file in the `examples` folder to see how you can use this `keyboard` function to control a sprite using your keyboard's arrow keys. Run it and use the left, up, down, and right arrow keys to move the cat around the stage. ![Keyboard movement](/examples/images/screenshots/17.png) Here's the code that does all this: ```js function setup() { //Create the `cat` sprite cat = new Sprite("images/cat.png"); cat.y = 96; cat.vx = 0; cat.vy = 0; stage.addChild(cat); //Capture the keyboard arrow keys var left = keyboard(37), up = keyboard(38), right = keyboard(39), down = keyboard(40); //Left arrow key `press` method left.press = function() { //Change the cat's velocity when the key is pressed cat.vx = -5; cat.vy = 0; }; //Left arrow key `release` method left.release = function() { //If the left arrow has been released, and the right arrow isn't down, //and the cat isn't moving vertically: //Stop the cat if (!right.isDown && cat.vy === 0) { cat.vx = 0; } }; //Up up.press = function() { cat.vy = -5; cat.vx = 0; }; up.release = function() { if (!down.isDown && cat.vx === 0) { cat.vy = 0; } }; //Right right.press = function() { cat.vx = 5; cat.vy = 0; }; right.release = function() { if (!left.isDown && cat.vy === 0) { cat.vx = 0; } }; //Down down.press = function() { cat.vy = 5; cat.vx = 0; }; down.release = function() { if (!up.isDown && cat.vx === 0) { cat.vy = 0; } }; //Set the game state state = play; //Start the game loop gameLoop(); } function gameLoop() { requestAnimationFrame(gameLoop); state(); renderer.render(stage); } function play() { //Use the cat's velocity to make it move cat.x += cat.vx; cat.y += cat.vy } ``` Grouping Sprites ---------------- Groups let you create game scenes, and manage similar sprites together as single units. Pixi has an object called a `Container` that lets you do this. Let's find out how it works. Imagine that you want to display three sprites: a cat, hedgehog and tiger. Create them, and set their positions - *but don't add them to the stage*. ```js //The cat var cat = new Sprite(id["cat.png"]); cat.position.set(16, 16); //The hedgehog var hedgehog = new Sprite(id["hedgehog.png"]); hedgehog.position.set(32, 32); //The tiger var tiger = new Sprite(id["tiger.png"]); tiger.position.set(64, 64); ``` Next, create an `animals` container to group them all together like this: ```js var animals = new Container(); ``` Then use `addChild` to *add the sprites to the group*. ```js animals.addChild(cat); animals.addChild(hedgehog); animals.addChild(tiger); ``` Finally add the group to the stage. ```js stage.addChild(animals); renderer.render(stage); ``` (As you know, the `stage` object is also a `Container`. It’s the root container for all Pixi sprites.) Here's what this code produces: ![Grouping sprites](/examples/images/screenshots/18.png) What you can't see in that image is the invisible `animals` group that's containing the sprites. ![Grouping sprites](/examples/images/screenshots/19.png) You can now treat the `animals` group as a single unit. You can think of a `Container` as a special kind of sprite that doesn’t have a texture. If you need a list of all the child sprites that `animals` contains, use its `children` array to find out. ``` console.log(animals.children) //Displays: Array [Object, Object, Object] ``` This tells you that `animals` has three sprites as children. Because the `animals` group is just like any other sprite, you can change its `x` and `y` values, `alpha`, `scale` and all the other sprite properties. Any property value you change on the parent container will affect the child sprites in a relative way. So if you set the group's `x` and `y` position, all the child sprites will be repositioned relative to the group's top left corner. What would happen if you set the `animals`'s `x` and `y` position to 64? ``` animals.position.set(64, 64); ``` The whole group of sprites will move 64 pixels right and 64 pixels to the down. ![Grouping sprites](/examples/images/screenshots/20.png) The `animals` group also has its own dimensions, which is based on the area occupied by the containing sprites. You can find its `width` and `height` values like this: ```js console.log(animals.width); //Displays: 112 console.log(animals.height); //Displays: 112 ``` ![Group width and height](/examples/images/screenshots/21.png) What happens if you change a group's width or height? ```js animals.width = 200; animals.height = 200; ``` All the child sprites will scale to match that change. ![Group width and height](/examples/images/screenshots/22.png) You can nest as many `Container`s inside other `Container`s as you like, to create deep hierarchies if you need to. However, a `DisplayObject` (like a `Sprite` or another `Container`) can only belong to one parent at a time. If you use `addChild` to make a sprite the child of another object, Pixi will automatically remove it from its current parent. That’s a useful bit of management that you don’t have to worry about. ### Local and global positions When you add a sprite to a `Container`, its `x` and `y` position is *relative to the group’s top left corner*. That's the sprite's **local position** For example, what do you think the cat's position is in this image? ![Grouping sprites](/examples/images/screenshots/20.png) Let's find out: ``` console.log(cat.x); //Displays: 16 ``` 16? Yes! That's because the cat is offset by only 16 pixel's from the group's top left corner. 16 is the cat's local position. Sprites also have a **global position**. The global position is the distance from the top left corner of the stage, to the sprite's anchor point (usually the sprite's top left corner.) You can find a sprite's global position with the help of the `toGlobal` method. Here's how: ``` parentSprite.toGlobal(childSprite.position) ``` That means you can find the cat's global position inside the `animals` group like this: ``` console.log(animals.toGlobal(cat.position)); //Displays: Object {x: 80, y: 80...}; ``` That gives you an `x` and `y` position of 80. That's exactly the cat's global position relative to the top left corner of the stage. What if you want to find the global position of a sprite, but don't know what the sprite's parent container is? Every sprite has a property called `parent` that will tell you what the sprite's parent is. If you add a sprite directly to the `stage`, then `stage` will be the sprite's parent. In the example above, the `cat`'s parent is `animals`. That means you can alternatively get the cat's global position by writing code like this: ``` cat.parent.toGlobal(cat.position); ``` And it will work even if you don't know what the cat's parent container currently is. There's one more way to calculate the global position! And, it's actually the best way, so heads up! If you want to know the distance from the top left corner of the canvas to the sprite, and don't know or care what the sprite's parent containers are, use the `getGlobalPosition` method. Here's how to use it to find the tiger's global position: ```js tiger.getGlobalPosition().x tiger.getGlobalPosition().y ``` This will give you `x` and `y` values of 128 in the example that we've been using. The special thing about `getGlobalPosition` is that it's highly precise: it will give you the sprite's accurate global position as soon as its local position changes. I asked the Pixi development team to add this feature specifically for accurate collision detection for games. What if you want to convert a global position to a local position? you can use the `toLocal` method. It works in a similar way, but uses this general format: ```js sprite.toLocal(sprite.position, anyOtherSprite) ``` Use `toLocal` to find the distance between a sprite and any other sprite. Here's how you could find out the tiger's local position, relative to the hedgehog. ```js tiger.toLocal(tiger.position, hedgehog).x tiger.toLocal(tiger.position, hedgehog).y ``` This gives you an `x` value of 32 and a `y` value of 32. You can see in the example images that the tiger's top left corner is 32 pixels down and to the left of the hedgehog's top left corner. ### Using a ParticleContainer to group sprites Pixi has an alternative, high-performance way to group sprites called a `ParticleContainer` (`PIXI.ParticleContainer`). Any sprites inside a `ParticleContainer` will render 2 to 5 times faster than they would if they were in a regular `Container`. It’s a great performance boost for games. Create a `ParticleContainer` like this: ```js var superFastSprites = new ParticleContainer(); ``` Then use `addChild` to add sprites to it, just like you would with any ordinary `Container`. You have to make some compromises if you decide to use a `ParticleContainer`. Sprites inside a `ParticleContainer` only have a few basic properties: `x`, `y`, `width`, `height`, `scale`, `pivot`, `alpha`, `visible` – and that’s about it. Also, the sprites that it contains can’t have nested children of their own. A `ParticleContainer` also can’t use Pixi’s advanced visual effects like filters and blend modes. Each `ParticleContainer` can use only one texture (so you'll have to use a spritesheet if you want Sprites with different appearances). But for the huge performance boost that you get, those compromises are usually worth it. And you can use `Container`s and `ParticleContainer`s simultaneously in the same project, so you can fine-tune your optimization. Why are sprites in a `Particle Container` so fast? Because the positions of the sprites are being calculated directly on the GPU. The Pixi development team is working to offload as much sprite processing as possible on the GPU, so it’s likely that the latest version of Pixi that you’re using will have much more feature-rich `ParticleContainer` than what I've described here. Check the current [`ParticleContainer` documentation](http://pixijs.download/release/docs/PIXI.particles.ParticleContainer.html) for details. Where you create a `ParticleContainer`, there are two optional arguments you can provide: the maximum number of sprites the container can hold, and an options object. ```js var superFastSprites = new ParticleContainer(size, options); ``` The default value for size is 15,000. So, if you need to contain more sprites, set it to a higher number. The options argument is an object with 5 Boolean values you can set: `scale`, `position`, `rotation`, `uvs` and `alpha`. The default value of `position` is `true`, but all the others are set to `false`. That means that if you want change the `rotation`, `scale`, `alpha`, or `uvs` of sprite in the `ParticleContainer`, you have to set those properties to `true`, like this: ```js var superFastSprites = new ParticleContainer( size, { rotation: true, alpha: true, scale: true, uvs: true } ); ``` But, if you don't think you'll need to use these properties, keep them set to `false` to squeeze out the maximum amount of performance. What's the `uvs` option? Only set it to `true` if you have particles which change their textures while they're being animated. (All the sprite's textures will also need to be on the same tileset image for this to work.) (Note: **UV mapping** is a 3D graphics display term that refers to the `x` and `y` coordinates of the texture (the image) that is being mapped onto a 3D surface. `U` is the `x` axis and `V` is the `y` axis. WebGL already uses `x`, `y` and `z` for 3D spatial positioning, so `U` and `V` were chosen to represent `x` and `y` for 2D image textures.) Pixi's Graphic Primitives ------------------------- Using image textures is one of the most useful ways of making sprites, but Pixi also has its own low-level drawing tools. You can use them to make rectangles, shapes, lines, complex polygons and text. And, fortunately, it uses almost the same API as the [Canvas Drawing API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_graphics_with_canvas) so, if you're already familiar with canvas, here’s nothing really new to learn. But the big advantage is that, unlike the Canvas Drawing API, the shapes you draw with Pixi are rendered by WebGL on the GPU. Pixi lets you access all that untapped performance power. Let’s take a quick tour of how to make some basic shapes. Here are all the shapes we'll make in the code ahead. ![Graphic primitives](/examples/images/screenshots/23.png) ### Rectangles All shapes are made by first creating a new instance of Pixi's `Graphics` class (`PIXI.Graphics`). ```js var rectangle = new Graphics(); ``` Use `beginFill` with a hexadecimal color code value to set the rectangle’ s fill color. Here’ how to set to it to light blue. ```js rectangle.beginFill(0x66CCFF); ``` If you want to give the shape an outline, use the `lineStyle` method. Here's how to give the rectangle a 4 pixel wide red outline, with an `alpha` value of 1. ```js rectangle.lineStyle(4, 0xFF3300, 1); ``` Use the `drawRect` method to draw the rectangle. Its four arguments are `x`, `y`, `width` and `height`. ```js rectangle.drawRect(x, y, width, height); ``` Use `endFill` when you’re done. ```js rectangle.endFill(); ``` It’s just like the Canvas Drawing API! Here’s all the code you need to draw a rectangle, change its position, and add it to the stage. ```js var rectangle = new Graphics(); rectangle.lineStyle(4, 0xFF3300, 1); rectangle.beginFill(0x66CCFF); rectangle.drawRect(0, 0, 64, 64); rectangle.endFill(); rectangle.x = 170; rectangle.y = 170; stage.addChild(rectangle); ``` This code makes a 64 by 64 blue rectangle with a red border at an x and y position of 170. ### Circles Make a circle with the `drawCircle` method. Its three arguments are `x`, `y` and `radius` ```js drawCircle(x, y, radius) ``` Unlike rectangles and sprites, a circle’s x and y position is also its center point. Here’s how to make a violet colored circle with a radius of 32 pixels. ```js var circle = new Graphics(); circle.beginFill(0x9966FF); circle.drawCircle(0, 0, 32); circle.endFill(); circle.x = 64; circle.y = 130; stage.addChild(circle); ``` ### Ellipses As a one-up on the Canvas Drawing API, Pixi lets you draw an ellipse with the `drawEllipse` method. ```js drawEllipse(x, y, width, height); ``` The x/y position defines the ellipse’s top left corner (imagine that the ellipse is surrounded by an invisible rectangular bounding box - the top left corner of that box will represent the ellipse's x/y anchor position). Here’s a yellow ellipse that’s 50 pixels wide and 20 pixels high. ```js var ellipse = new Graphics(); ellipse.beginFill(0xFFFF00); ellipse.drawEllipse(0, 0, 50, 20); ellipse.endFill(); ellipse.x = 180; ellipse.y = 130; stage.addChild(ellipse); ``` ### Rounded rectangles Pixi also lets you make rounded rectangles with the `drawRoundedRect` method. The last argument, `cornerRadius` is a number in pixels that determines by how much the corners should be rounded. ```js drawRoundedRect(x, y, width, height, cornerRadius) ``` Here's how to make a rounded rectangle with a corner radius of 10 pixels. ```js var roundBox = new Graphics(); roundBox.lineStyle(4, 0x99CCFF, 1); roundBox.beginFill(0xFF9933); roundBox.drawRoundedRect(0, 0, 84, 36, 10) roundBox.endFill(); roundBox.x = 48; roundBox.y = 190; stage.addChild(roundBox); ``` ### Lines You've seen in the examples above that the `lineStyle` method lets you define a line. You can use the `moveTo` and `lineTo` methods to draw the start and end points of the line, in just the same way you can with the Canvas Drawing API. Here’s how to draw a 4 pixel wide, white diagonal line. ```js var line = new Graphics(); line.lineStyle(4, 0xFFFFFF, 1); line.moveTo(0, 0); line.lineTo(80, 50); line.x = 32; line.y = 32; stage.addChild(line); ``` `PIXI.Graphics` objects, like lines, have `x` and `y` values, just like sprites, so you can position them anywhere on the stage after you've drawn them. ### Polygons You can join lines together and fill them with colors to make complex shapes using the `drawPolygon` method. `drawPolygon`'s argument is an path array of x/y points that define the positions of each point on the shape. ```js var path = [ point1X, point1Y, point2X, point2Y, point3X, point3Y ]; graphicsObject.drawPolygon(path); ``` `drawPolygon` will join those three points together to make the shape. Here’s how to use `drawPolygon` to connect three lines together to make a red triangle with a blue border. The triangle is drawn at position 0,0 and then moved to its position on the stage using its `x` and `y` properties. ```js var triangle = new Graphics(); triangle.beginFill(0x66FF33); //Use `drawPolygon` to define the triangle as //a path array of x/y positions triangle.drawPolygon([ -32, 64, //First point 32, 64, //Second point 0, 0 //Third point ]); //Fill shape's color triangle.endFill(); //Position the triangle after you've drawn it. //The triangle's x/y position is anchored to its first point in the path triangle.x = 180; triangle.y = 22; stage.addChild(triangle); ``` Displaying text --------------- Use a `Text` object (`PIXI.Text`) to display text on the stage. The constructor takes two arguments: the text you want to display and a style object that defines the font’s properties. Here's how to display the words "Hello Pixi", in white, 32 pixel high Arial font. ```js var message = new Text( "Hello Pixi!", {fontFamily: "Arial", fontSize: 32, fill: "white"} ); message.position.set(54, 96); stage.addChild(message); ``` ![Displaying text](/examples/images/screenshots/24.png) Pixi’s Text objects inherit from the `Sprite` class, so they contain all the same properties like `x`, `y`, `width`, `height`, `alpha`, and `rotation`. Position and resize text on the stage just like you would any other sprite. If you want to change the content of a text object after you've created it, use the `text` property. ```js message.text = "Text changed!"; ``` Use the `style` property if you want to redefine the font properties. ```js message.style = {fill: "black", font: "16px PetMe64"}; ``` Pixi makes text objects by using the Canvas Drawing API to render the text to an invisible and temporary canvas element. It then turns the canvas into a WebGL texture so that it can be mapped onto a sprite. That’s why the text’s color needs to be wrapped in a string: it’s a Canvas Drawing API color value. As with any canvas color values, you can use words for common colors like “red” or “green”, or use rgba, hsla or hex values. Other style properties that you can include are `stroke` for the font outline color and `strokeThickness` for the outline thickness. Set the text's `dropShadow` property to `true` to make the text display a shadow. Use `dropShadowColor` to set the shadow's hexadecimal color value, use `dropShadowAngle` to set the shadow's angle in radians, and use `dropShadowDistance` to set the pixel height of a shadow. And there's more: [check out Pixi's Text documentation for the full list](http://pixijs.download/release/docs/PIXI.Text.html). Pixi can also wrap long lines of text. Set the text’s `wordWrap` style property to `true`, and then set `wordWrapWidth` to the maximum length in pixels, that the line of text should be. Use the `align` property to set the alignment for multi-line text. ```js message.style = {wordWrap: true, wordWrapWidth: 100, align: center}; ``` (Note: `align` doesn't affect single line text.) If you want to use a custom font file, use the CSS `@font-face` rule to link the font file to the HTML page where your Pixi application is running. ```js @font-face { font-family: "fontFamilyName"; src: url("fonts/fontFile.ttf"); } ``` Add this `@font-face` rule to your HTML page's CSS style sheet. [Pixi also has support for bitmap fonts](http://pixijs.download/release/docs/PIXI.extras.BitmapText.html). You can use Pixi's loader to load Bitmap font XML files, the same way you load JSON or image files. Collision detection -------------------------- You now know how to make a huge variety of graphics objects, but what can you do with them? A fun thing to do is to build a simple **collision detection** system. You can use a custom function called `hitTestRectangle` that checks whether any two rectangular Pixi sprites are touching. ```js hitTestRectangle(spriteOne, spriteTwo) ``` if they overlap, `hitTestRectangle` will return `true`. You can use `hitTestRectangle` with an `if` statement to check for a collision between two sprites like this: ```js if (hitTestRectangle(cat, box)) { //There's a collision } else { //There's no collision } ``` As you'll see, `hitTestRectangle` is the front door into the vast universe of game design. Run the `collisionDetection.html` file in the `examples` folder for a working example of how to use `hitTestRectangle`. Use the arrow keys to move the cat. If the cat hits the box, the box becomes red and "Hit!" is displayed by the text object. ![Displaying text](/examples/images/screenshots/25.png) You've already seen all the code that creates all these elements, as well as the keyboard control system that makes the cat move. The only new thing is the way `hitTestRectangle` is used inside the `play` function to check for a collision. ```js function play() { //use the cat's velocity to make it move cat.x += cat.vx; cat.y += cat.vy; //check for a collision between the cat and the box if (hitTestRectangle(cat, box)) { //if there's a collision, change the message text //and tint the box red message.text = "hit!"; box.tint = 0xff3300; } else { //if there's no collision, reset the message //text and the box's color message.text = "No collision..."; box.tint = 0xccff99; } } ``` Because the `play` function is being called by the game loop 60 times per second, this `if` statement is constantly checking for a collision between the cat and the box. If `hitTestRectangle` is `true`, the text `message` object uses `setText` to display "Hit": ```js message.text = "hit!"; ``` The color of the box is then changed from green to red by setting the box's `tint` property to the hexadecimal red value. ```js box.tint = 0xff3300; ``` If there's no collision, the message and box are maintained in their original states: ```js message.text = "no collision..."; box.tint = 0xccff99; ``` This code is pretty simple, but suddenly you've created an interactive world that seems to be completely alive. It's almost like magic! And, perhaps surprisingly, you now have all the skills you need to start making games with Pixi! ### The hitTestRectangle function But what about the `hitTestRectangle` function? What does it do, and how does it work? The details of how collision detection algorithms like this work is a little bit outside the scope of this tutorial. The most important thing is that you know how to use it. But, just for your reference, and in case you're curious, here's the complete `hitTestRectangle` function definition. Can you figure out from the comments what it's doing? ```js function hitTestRectangle(r1, r2) { //Define the variables we'll need to calculate var hit, combinedHalfWidths, combinedHalfHeights, vx, vy; //hit will determine whether there's a collision hit = false; //Find the center points of each sprite r1.centerX = r1.x + r1.width / 2; r1.centerY = r1.y + r1.height / 2; r2.centerX = r2.x + r2.width / 2; r2.centerY = r2.y + r2.height / 2; //Find the half-widths and half-heights of each sprite r1.halfWidth = r1.width / 2; r1.halfHeight = r1.height / 2; r2.halfWidth = r2.width / 2; r2.halfHeight = r2.height / 2; //Calculate the distance vector between the sprites vx = r1.centerX - r2.centerX; vy = r1.centerY - r2.centerY; //Figure out the combined half-widths and half-heights combinedHalfWidths = r1.halfWidth + r2.halfWidth; combinedHalfHeights = r1.halfHeight + r2.halfHeight; //Check for a collision on the x axis if (Math.abs(vx) < combinedHalfWidths) { //A collision might be occuring. Check for a collision on the y axis if (Math.abs(vy) < combinedHalfHeights) { //There's definitely a collision happening hit = true; } else { //There's no collision on the y axis hit = false; } } else { //There's no collision on the x axis hit = false; } //`hit` will be either `true` or `false` return hit; }; ``` Case study: Treasure Hunter --------------- So I told you that you now have all the skills you need to start making games. What? You don't believe me? Let me prove it to you! Let’s take a close at how to make a simple object collection and enemy avoidance game called **Treasure Hunter**. (You'll find it the `examples` folder.) ![Treasure Hunter](/examples/images/screenshots/26.png) Treasure Hunter is a good example of one of simplest complete games you can make using the tools you've learnt so far. Use the keyboard arrow keys to help the explorer find the treasure and carry it to the exit. Six blob monsters move up and down between the dungeon walls, and if they hit the explorer he becomes semi-transparent and the health meter at the top right corner shrinks. If all the health is used up, “You Lost!” is displayed on the stage; if the explorer reaches the exit with the treasure, “You Won!” is displayed. Although it’s a basic prototype, Treasure Hunter contains most of the elements you’ll find in much bigger games: texture atlas graphics, interactivity, collision, and multiple game scenes. Let’s go on a tour of how the game was put together so that you can use it as a starting point for one of your own games. ### The code structure Open the `treasureHunter.html` file and you'll see that all the game code is in one big file. Here's a birds-eye view of how all the code is organized. ```js //Setup Pixi and load the texture atlas files - call the `setup` //function when they've loaded function setup() { //Initialize the game sprites, set the game `state` to `play` //and start the 'gameLoop' } function gameLoop() { //Runs the current game `state` in a loop and renders the sprites } function play() { //All the game logic goes here } function end() { //All the code that should run at the end of the game } //The game's helper functions: //`keyboard`, `hitTestRectangle`, `contain` and `randomInt` ``` Use this as your world map to the game as we look at how each section works. ### Initialize the game in the setup function As soon as the texture atlas images have loaded, the `setup` function runs. It only runs once, and lets you perform one-time setup tasks for your game. It's a great place to create and initialize objects, sprites, game scenes, populate data arrays or parse loaded JSON game data. Here's an abridged view of the `setup` function in Treasure Hunter, and the tasks that it performs. ```js function setup() { //Create the `gameScene` group //Create the `door` sprite //Create the `player` sprite //Create the `treasure` sprite //Make the enemies //Create the health bar //Add some text for the game over message //Create a `gameOverScene` group //Assign the player's keyboard controllers //set the game state to `play` state = play; //Start the game loop gameLoop(); } ``` The last two lines of code, `state = play;` and `gameLoop()` are perhaps the most important. Running `gameLoop` switches on the game's engine, and causes the `play` function to be called in a continuous loop. But before we look at how that works, let's see what the specific code inside the `setup` function does. #### Creating the game scenes The `setup` function creates two `Container` groups called `gameScene` and `gameOverScene`. Each of these are added to the stage. ```js gameScene = new Container(); stage.addChild(gameScene); gameOverScene = new Container(); stage.addChild(gameOverScene); ``` All of the sprites that are part of the main game are added to the `gameScene` group. The game over text that should be displayed at the end of the game is added to the `gameOverScene` group. ![Displaying text](/examples/images/screenshots/27.png) Although it's created in the `setup` function, the `gameOverScene` shouldn't be visible when the game first starts, so its `visible` property is initialized to `false`. ```js gameOverScene.visible = false; ``` You'll see ahead that, when the game ends, the `gameOverScene`'s `visible` property will be set to `true` to display the text that appears at the end of the game. #### Making the dungeon, door, explorer and treasure The player, exit door, treasure chest and the dungeon background image are all sprites made from texture atlas frames. Very importantly, they're all added as children of the `gameScene`. ```js //Create an alias for the texture atlas frame ids id = resources["images/treasureHunter.json"].textures; //Dungeon dungeon = new Sprite(id["dungeon.png"]); gameScene.addChild(dungeon); //Door door = new Sprite(id["door.png"]); door.position.set(32, 0); gameScene.addChild(door); //Explorer explorer = new Sprite(id["explorer.png"]); explorer.x = 68; explorer.y = gameScene.height / 2 - explorer.height / 2; explorer.vx = 0; explorer.vy = 0; gameScene.addChild(explorer); //Treasure treasure = new Sprite(id["treasure.png"]); treasure.x = gameScene.width - treasure.width - 48; treasure.y = gameScene.height / 2 - treasure.height / 2; gameScene.addChild(treasure); ``` Keeping them together in the `gameScene` group will make it easy for us to hide the `gameScene` and display the `gameOverScene` when the game is finished. #### Making the blob monsters The six blob monsters are created in a loop. Each blob is given a random initial position and velocity. The vertical velocity is alternately multiplied by `1` or `-1` for each blob, and that’s what causes each blob to move in the opposite direction to the one next to it. Each blob monster that's created is pushed into an array called `blobs`. ```js var numberOfBlobs = 6, spacing = 48, xOffset = 150, speed = 2, direction = 1; //An array to store all the blob monsters blobs = []; //Make as many blobs as there are `numberOfBlobs` for (var i = 0; i < numberOfBlobs; i++) { //Make a blob var blob = new Sprite(id["blob.png"]); //Space each blob horizontally according to the `spacing` value. //`xOffset` determines the point from the left of the screen //at which the first blob should be added var x = spacing * i + xOffset; //Give the blob a random `y` position var y = randomInt(0, stage.height - blob.height); //Set the blob's position blob.x = x; blob.y = y; //Set the blob's vertical velocity. `direction` will be either `1` or //`-1`. `1` means the enemy will move down and `-1` means the blob will //move up. Multiplying `direction` by `speed` determines the blob's //vertical direction blob.vy = speed * direction; //Reverse the direction for the next blob direction *= -1; //Push the blob into the `blobs` array blobs.push(blob); //Add the blob to the `gameScene` gameScene.addChild(blob); } ``` #### Making the health bar When you play Treasure Hunter you'll notice that when the explorer touches one of the enemies, the width of the health bar at the top right corner of the screen decreases. How was this health bar made? It's just two overlapping rectangles at exactly the same position: a black rectangle behind, and a red rectangle in front. They're grouped into a single `healthBar` group. The `healthBar` is then added to the `gameScene` and positioned on the stage. ```js //Create the health bar healthBar = new PIXI.DisplayObjectContainer(); healthBar.position.set(stage.width - 170, 6) gameScene.addChild(healthBar); //Create the black background rectangle var innerBar = new PIXI.Graphics(); innerBar.beginFill(0x000000); innerBar.drawRect(0, 0, 128, 8); innerBar.endFill(); healthBar.addChild(innerBar); //Create the front red rectangle var outerBar = new PIXI.Graphics(); outerBar.beginFill(0xFF3300); outerBar.drawRect(0, 0, 128, 8); outerBar.endFill(); healthBar.addChild(outerBar); healthBar.outer = outerBar; ``` You can see that a property called `outer` has been added to the `healthBar`. It just references the `outerBar` (the red rectangle) so that it will be convenient to access later. ```js healthBar.outer = outerBar; ``` You don't have to do this; but, hey why not! It means that if you want to control the width of the red `outerBar`, you can write some smooth code that looks like this: ```js healthBar.outer.width = 30; ``` That's pretty neat and readable, so we'll keep it! #### Making the message text When the game is finished, some text displays “You won!” or “You lost!”, depending on the outcome of the game. This is made using a text sprite and adding it to the `gameOverScene`. Because the `gameOverScene`‘s `visible` property is set to `false` when the game starts, you can’t see this text. Here’s the code from the `setup` function that creates the message text and adds it to the `gameOverScene`. ```js message = new Text( "The End!", {font: "64px Futura", fill: "white"} ); message.x = 120; message.y = stage.height / 2 - 32; gameOverScene.addChild(message); ``` ### Playing the game All the game logic and the code that makes the sprites move happens inside the `play` function, which runs in a continuous loop. Here's an overview of what the `play` function does ```js function play() { //Move the explorer and contain it inside the dungeon //Move the blob monsters //Check for a collision between the blobs and the explorer //Check for a collision between the explorer and the treasure //Check for a collision between the treasure and the door //Decide whether the game has been won or lost //Change the game `state` to `end` when the game is finsihed } ``` Let's find out how all these features work. ### Moving the explorer The explorer is controlled using the keyboard, and the code that does that is very similar to the keyboard control code you learnt earlier. The `keyboard` objects modify the explorer’s velocity, and that velocity is added to the explorer’s position inside the `play` function. ```js explorer.x += explorer.vx; explorer.y += explorer.vy; ``` #### Containing movement But what's new is that the explorer's movement is contained inside the walls of the dungeon. The green outline shows the limits of the explorer's movement. ![Displaying text](/examples/images/screenshots/28.png) That's done with the help of a custom function called `contain`. ```js contain(explorer, {x: 28, y: 10, width: 488, height: 480}); ``` `contain` takes two arguments. The first is the sprite you want to keep contained. The second is any object with `x`, `y`, `width` and `height` properties that define a rectangular area. In this example, the containing object defines an area that's just slightly offset from, and smaller than, the stage. It matches dimensions of the dungeon walls. Here's the `contain` function that does all this work. The function checks to see if the sprite has crossed the boundaries of the containing object. If it has, the code moves the sprite back into that boundary. The `contain` function also returns a `collision` variable with the value "top", "right", "bottom" or "left", depending on which side of the boundary the sprite hit. (`collision` will be `undefined` if the sprite didn't hit any of the boundaries.) ```js function contain(sprite, container) { var collision = undefined; //Left if (sprite.x < container.x) { sprite.x = container.x; collision = "left"; } //Top if (sprite.y < container.y) { sprite.y = container.y; collision = "top"; } //Right if (sprite.x + sprite.width > container.width) { sprite.x = container.width - sprite.width; collision = "right"; } //Bottom if (sprite.y + sprite.height > container.height) { sprite.y = container.height - sprite.height; collision = "bottom"; } //Return the `collision` value return collision; } ``` You'll see how the `collision` return value will be use in the code ahead to make the blob monsters bounce back and forth between the top and bottom dungeon walls. ### Moving the monsters The `play` function also moves the blob monsters, keeps them contained inside the dungeon walls, and checks each one for a collision with the player. If a blob bumps into the dungeon’s top or bottom walls, its direction is reversed. All this is done with the help of a `forEach` loop which iterates through each of `blob` sprites in the `blobs` array on every frame. ```js blobs.forEach(function(blob) { //Move the blob blob.y += blob.vy; //Check the blob's screen boundaries var blobHitsWall = contain(blob, {x: 28, y: 10, width: 488, height: 480}); //If the blob hits the top or bottom of the stage, reverse //its direction if (blobHitsWall === "top" || blobHitsWall === "bottom") { blob.vy *= -1; } //Test for a collision. If any of the enemies are touching //the explorer, set `explorerHit` to `true` if(hitTestRectangle(explorer, blob)) { explorerHit = true; } }); ``` You can see in this code above how the return value of the `contain` function is used to make the blobs bounce off the walls. A variable called `blobHitsWall` is used to capture the return value: ```js var blobHitsWall = contain(blob, {x: 28, y: 10, width: 488, height: 480}); ``` `blobHitsWall` will usually be `undefined`. But if the blob hits the top wall, `blobHitsWall` will have the value "top". If the blob hits the bottom wall, `blobHitsWall` will have the value "bottom". If either of these cases are `true`, you can reverse the blob's direction by reversing its velocity. Here's the code that does this: ```js if (blobHitsWall === "top" || blobHitsWall === "bottom") { blob.vy *= -1; } ``` Multiplying the blob's `vy` (vertical velocity) value by `-1` will flip the direction of its movement. ### Checking for collisions The code in the loop above uses `hitTestRectangle` to figure out if any of the enemies have touched the explorer. ```js if(hitTestRectangle(explorer, blob)) { explorerHit = true; } ``` If `hitTestRectangle` returns `true`, it means there’s been a collision and a variable called `explorerHit` is set to `true`. If `explorerHit` is `true`, the `play` function makes the explorer semi-transparent and reduces the width of the `health` bar by 1 pixel. ```js if(explorerHit) { //Make the explorer semi-transparent explorer.alpha = 0.5; //Reduce the width of the health bar's inner rectangle by 1 pixel healthBar.outer.width -= 1; } else { //Make the explorer fully opaque (non-transparent) if it hasn't been hit explorer.alpha = 1; } ``` If `explorerHit` is `false`, the explorer's `alpha` property is maintained at 1, which makes it fully opaque. The `play` function also checks for a collision between the treasure chest and the explorer. If there’s a hit, the `treasure` is set to the explorer’s position, with a slight offset. This makes it look like the explorer is carrying the treasure. ![Displaying text](/examples/images/screenshots/29.png) Here's the code that does this: ```js if (hitTestRectangle(explorer, treasure)) { treasure.x = explorer.x + 8; treasure.y = explorer.y + 8; } ``` ### Reaching the exit door and ending the game There are two ways the game can end: You can win if you carry the treasure to the exit, or you can lose if you run out of health. To win the game, the treasure chest just needs to touch the exit door. If that happens, the game `state` is set to `end`, and the `message` text displays "You won". ```js if (hitTestRectangle(treasure, door)) { state = end; message.text = "You won!"; } ``` If you run out of health, you lose the game. The game `state` is also set to `end` and the `message` text displays "You Lost!" ```js if (healthBar.outer.width < 0) { state = end; message.text = "You lost!"; } ``` But what does this mean? ```js state = end; ``` You'll remember from earlier examples that the `gameLoop` is constantly updating a function called `state` at 60 times per second. Here's the `gameLoop`that does this: ```js function gameLoop(){ //Loop this function 60 times per second requestAnimationFrame(gameLoop); //Update the current game state state(); //Render the stage renderer.render(stage); } ``` You'll also remember that we initially set the value of `state` to `play`, which is why the `play` function runs in a loop. By setting `state` to `end` we're telling the code that we want another function, called `end` to run in a loop. In a bigger game you could have a `tileScene` state, and states for each game level, like `leveOne`, `levelTwo` and `levelThree`. So what is that `end` function? Here it is! ```js function end() { gameScene.visible = false; gameOverScene.visible = true; } ``` It just flips the visibility of the game scenes. This is what hides the `gameScene` and displays the `gameOverScene` when the game ends. This is a really simple example of how to switch a game's state, but you can have as many game states as you like in your games, and fill them with as much code as you need. Just change the value of `state` to whatever function you want to run in a loop. And that’s really all there is to Treasure Hunter! With a little more work you could turn this simple prototype into a full game – try it! More about sprites ----------------------------- You've learnt how to use quite a few useful sprite properties so far, like `x`, `y`, `visible`, and `rotation` that give you a lot of control over a sprite's position and appearance. But Pixi Sprites also have many more useful properties that are fun to play with. [Here's the full list.](http://pixijs.download/release/docs/PIXI.Sprite.html) How does Pixi’s class inheritance system work? ([What is a **class** and what is **inheritence**? Click this link to find out.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript)) Pixi’s sprites are built on an inheritance model that follows this chain: ``` DisplayObject > Container > Sprite ``` Inheritance just means that the classes later in the chain use properties and methods from classes earlier in the chain. The most basic class is `DisplayObject`. Anything that’s a `DisplayObject` can be rendered on the stage. `Container` is the next class in the inheritance chain. It allows `DisplayObject`s to act as containers for other `DisplayObject`s. Third up the chain is the `Sprite` class. That’s the class you’ll be using to make most of your game objects. However, later you’ll learn how to use `Container`s to group sprites together. Taking it further ----------------- Pixi can do a lot, but it can't do everything! If you want to start making games or complex interactive applications with Pixi, you'll need to use some helper libraries: - [Bump](https://github.com/kittykatattack/bump): A complete suite of 2D collision functions for games. - [Tink](https://github.com/kittykatattack/tink): Drag-and-drop, buttons, a universal pointer and other helpful interactivity tools. - [Charm](https://github.com/kittykatattack/charm): Easy-to-use tweening animation effects for Pixi sprites. - [Dust](https://github.com/kittykatattack/dust): Particle effects for creating things like explosions, fire and magic. - [Sprite Utilities](https://github.com/kittykatattack/spriteUtilities): Easier and more intuitive ways to create and use Pixi sprites, as well adding a state machine and animation player. Makes working with Pixi a lot more fun. - [Sound.js](https://github.com/kittykatattack/sound.js): A micro-library for loading, controlling and generating sound and music effects. Everything you need to add sound to games. - [Smoothie](https://github.com/kittykatattack/smoothie): Ultra-smooth sprite animation using true delta-time interpolation. It also lets you specify the fps (frames-per-second) at which your game or application runs, and completely separates your sprite rendering loop from your application logic loop. You can find out how to use all these libraries with Pixi in the book [Learn PixiJS](http://www.springer.com/us/book/9781484210956). ### Hexi Do you want to use all the functionality of those libraries, but don't want the hassle of integrating them yourself? Use **Hexi**: a complete development environment for building games and interactive applications: https://github.com/kittykatattack/hexi It bundles the best version of Pixi (the latest stable one) with all these libraries (and more!) for a simple and fun way to make games. Hexi also lets you access the global `PIXI` object directly, so you can write low-level Pixi code directly in a Hexi application, and optionally choose to use as many or as few of Hexi's extra conveniences as you need. Please help to support this project! ------------------- Buy the book! Incredibly, someone actually paid me to finish writing this tutorial and turn it into a book! [Learn PixiJS](http://www.springer.com/us/book/9781484210956) (And it's not just some junky "e-book", but a real, heavy, paper book, published by Springer, the world's largest publisher! That means you can invite your friends over, set it on fire, and roast marshmallows!!) There's 80% more content than what's in this tutorial, and it's packed full of all the essential techniques you need to know to use Pixi to make all kinds of interactive applications and games. Find out how to: - Make animated game characters. - Create a full-featured animation state player. - Dynamically animate lines and shapes. - Use tiling sprites for infinite parallax scrolling. - Use blend modes, filters, tinting, masks, video, and render textures. - Produce content for multiple resolutions. - Create interactive buttons. - Create a flexible drag and drop interface for Pixi. - Create particle effects. - Build a stable software architectural model that will scale to any size. - Make complete games. And, as a bonus, all the code is written entirely in the latest version of JavaScript: ES6/2015. If you want to support this project, please buy a copy of this book, and buy another copy for your mom!