What do you want to learn?
Skip to main content
by Japhia Olson
Start CourseBookmarkAdd to Channel
Table of contents
Introduction and Project Overview
Introduction and Project Overview
(guitar music) Hello, everyone, my name is Japhia Olson. As the founder of Mint Design Company, I'm a full-stack developer and designer, specializing in HTML5 game development, responsive web design, and animation. My latest projects have included comprehensive UX strategy and wireframe development for South Carolina Tourism, a series of dynamic HTML5 physics-based games, Ajax site development for the Sheryl Crow and Chateau St. Jean partnership, and my own company rebrand and site development. Some of the key takeaways from watching this course include learning how to, one, outline and concept a game, two, create and implement sprites into your game play, three, work with a popular Box2D library to come up with creative and engaging physics-based game play, four, come up with ideas and ways to structure more complex game code, and five, implement the Pixi.js renderer and work with GSAP to create smoothly animated graphics and UI elements. By the end of training, you'll have learned how to concept, design, code, and animate an entire desktop HTML5 game from scratch. I'm excited to work with Digital-Tutors and share these techniques with you. So let's get started with the first lesson.
Concepting the Game
Okay, so first off we'll start with the basics of game concepting. And everyone will have their own workflow, wire framing, and creative design processes. So this is just one example of, for me, what works to discover common pitfalls. And things to think about before going into the final design and development processes. And here I'm using Photoshop CC, and the Wacom Cintiq HT24 tablet to just sketch out wire frames, and flow charts, and general concepting notes. So first off I want to start here with a large canvas. This is fine just 1600 by 1600, plenty of room to make illustrations and concept. I want to go ahead and zoom in. Use our navigator to move to the top-left corner. And then we'll want to go ahead and select, first of all, our Vector Tool. And here we're really, we're just thinking of a way that's real quick to sort of show our flow chart, and concept notes, and stuff. And whatever works for you is totally fine. We just want something clear, and that's real fast to build out. And very simple, but just, you know, is very clear and makes sense. So we'll start with our Vector Tool and a light tan, and we'll do just a light shadow. Let's adjust our color a little bit. Okay, and then next we'll want to make a new layer. We'll call this sketch notes. I want to select our brush. I want to change this to black. And here I just have a three pixel charcoal brush, which is totally personal preference. But I like to do it where it just kind of looks hand-drawn, and is a nice and clear black brush. So, and before you start concepting, you should check out other HTML5 games. See what the flow is generally like, what do people expect? And what do you need to make for HTML5 online-specific games? So one thing we know we'll need to create, of course, is a pre-loader. So we'll just make a quick diagram showing that. And then we'll want to go ahead and add labels, so everything's real clear. Here we'll go pre-loader. And we have our Text Tool, we have Times New Roman, 12 pixel font. Just nice and clear and simple, okay. Then we'll want to go ahead and grab our brush back, switch back to sketch notes, and create a flow arrow. And then we'll want to go ahead select our rectangle. Duplicate Layer. And go ahead, hit Enter. And then use our Move Tool. Click, hold Shift, drag, and it keeps it on the one plane. Drop that here. So next after our pre-loader, we're going to likely want a game title screen. And an instructions, help button, and our game play button. We want a title. And again we can click, hold Shift, oops. Click, hold Shift, drag, and it keeps this a perfect straight line. We want to add the next label. So this will be initial game screen and title. Okay, go ahead and position that. So using these same techniques, we would just continue to illustrate out our user flow. And make adjustments, double-check, you know, make sure everything's correct. But we just keep it real simple. Same, you know, one brush, one or two colors, and just a nice clean layout. So we will continue to fill that out, and when we come back, we will have the concept kind of laid out here. And we'll go over the actual functionality. Okay, so we laid out our flow chart, and did a quick illustration. Here's some illustrations to get an idea of how our game will work. So we'll walk through this point-by-point. So starting off, we had our pre-loader. And then we went, after our pre-loader, we went to the initial game screen of the title, help button, and start game. So the next thing we'll likely want to do is a countdown, especially for real action-oriented, fast-paced games. We'll need to have a countdown, so that people don't lose points unexpectedly when the game first starts and they're not prepared. This just makes sure you're ready to go. So that's nice to have for action-type games. After our countdown, we want to start the active game. Now this is, our gameplay is way too complex to have really here, and with games, it's more, it's really good to have just quick illustrations of what the objects look like and how they work. So, this is a pretty simple game. We just have one screen, and we have enough room there to describe everything that's going on. But, you know, for a more advanced game we'd probably want just to dedicate a whole page or series of pages on, with your game illustrations. And here we just have our points clearly laid out over here, documented so that we can reference back. We don't forget what any of this meant, it's all very clearly laid out here. So after our, and we'll go over that in detail. But after our active game, we have our game over. And our game over we'll want to reset and replay, and we might also want a high score button. If we hit the high score, that will take us to enter our username, so we need a text input to enter the name. And we will have our submit name button. If after we submit, then we get to see the top 10 scores. And for, you know, you might want to do top 100, but for this particular game we just stuck to a top 10 because we didn't really want to go. It's kind of out of the scope to go over, you know, how we deal with paging and the different screens and stuff. So we wanted just to stick with the simple top 10 for this game. So, now here we have this note to do the active gameplay. And as you can see, we just did a bunch of quick illustrations to kind of, we came up with how this is going to, sort of, work. So, we'll start with point number one. Fruit falls from top constrained on the y-axis. So this is just a classic, kind of, block falling game, where the blocks fall in at different types. In this particular instance, it's fruit. And it's apples, peaches, oranges, and pineapple bombs. So if we have two or more blocks touching that match, then we can double-tap or double-click, and we will get points. The more we have matching, the more points we get. And then we also made note here, that the blocks are falling from the top. And that we need to stick to this one y-axis. Even though this is based on a dynamic Box2D physics engine, we don't want this flying around on the screen when it's hitting stuff. We just want it to fall directly down. So we need to make note of that kind of functionality, so when we go into development we are prepared, and know what we need to do; what kind of systems we need to implement to make that happen. So the next one is number two; which it would be the pineapple bomb. So pineapple bombs explode, try to remove before explosion happens. If pineapple bomb does explode, then you lose a life. All nearby fruits are destroyed. So this is just, just like this fruit, it falls down on its axis, but, and it has a little sprite animation where the wick, the wick burns down, and the whole thing explodes. Now if you don't double-tap it before it hits, then you lose a life, and all of the surrounding blocks will be removed, destroyed. So it just adds a lot more fun and dynamic gameplay, and a fun little explosion animation. It just makes things a lot more interesting. So next up, we have number three, double-click matching fruit box for high score. More fruit box equals higher score. So that's what we went over, those are just showing that if we have multiple apples there, we double-tap, we get a higher score. So next up is four, fruit rots after a certain time. So here, we just show that to make things a little bit more fun, and the game a little bit faster paced, and add some extra animation. We'll have all the fruits after they sit here, we'll have them deteriorate, sort of rot, over time. And that makes us have to go, well we need to, we need to catch all the apples, say, before one of them rots, and now we just missed out on some extra points. So it just makes things a little more fun. Number five create matching background illustrations. This is just a quick note that we will have to, you know, actually create the illustrative world. Here we have trees, and we have our little ground plane. Simple, but we just want to make note of that. Six, make game frame scale to always show entire game in device screen. So even though this is an HTML5 online game, we now have all the different screen sizes and stuff. We want to make sure that, that it actually scales to each device. And this game, it would be pretty unplayable if it was hanging out of any screen, and you had to scroll over to click the blocks and stuff, you'd lose pretty fast. So we want to just make sure that it fits to any screen size. Number seven, create ground plane for fruits to land on. So this is just a note that we have to make a dynamic physics space ground plane. So that when these fruits fall in they hit it and bounce and stuff. So that, and then number eight always show current score, lives lost, remaining while game is active. So we want to just make some nice, fun, little graphics here, and show how many lives we have lost, and how many we have remaining. And here we want to show our total points. So this is just one way that works pretty well to, sort of, create the flow chart for your game UI, as well as layout your actual game, all the graphics. Now you can quickly look at this, you know all the graphics you need to make. That you need to make an apple, you need to make all your fruit blocks, you need to make your sprite animations. So this real quickly helps to sort of layout how everything works, and especially if you're working with a team, you all can share this and everybody's on the same page. That way you aren't creating the whole game and then, oh you've forgot, you know, the countdown, or the play button or something. And now you have to go back in there to hack your code and get that functionality back in there. It's a lot better to just build everything properly from the get go. And then you know what assets to create. So next up we'll be going over the actual asset creation. Animations and sprites, and how to design for high DPI screens and stuff. So that will be up next.
Illustrating the Game Icons
Okay, so here we're going to go over the creation of our assets. And there's many ways you can make game sprites. If you wanted to make a clean vector-style in Illustrator, or 3D graphics. And here we're just doing, we chose for this particular game to do all hand, sort of a hand-illustrated look. Everything, the animations will be hand-drawn, frame-by-frame, and all the background will be on paper. And everything will just have this really nice, little, organic 2D illustrative look so. And we'll go to the creation of one of the objects. So we're going to start with one of the fruits. And we'll do the orange. And we want to make a habit of always designing everything for Retina screens. So we want to make sure that even though, you know, our final size might be pretty small, we want to go ahead and make this really big. So that we have, you know, if we wanted to change the design, or make a poster, or anything, we always have really nice full-res graphics. So here I'm going to start with just a natural, charcoal-style brush. And, you know, I've downloaded many packs of brushes and stuff. So, and you can find, you know, free packs online, or purchase real high-quality packs. But for this one, I just have a nice natural brush texture. You can see it's a charcoal brush, and I want to make this real small, let's say two pixels. And this will start with the outline. So we want to make sure that this is plenty big. Here. And this needs to be squarer, so it's like a block, since this is sort of a block-matching game. So even though it's a fruit, we went with this design that's totally square. So we want to draw our little stem here. Draw our leaf. Here we want to draw a couple more leaves. And a last one here. Okay, and we'll want to make our bottom here. This is just using the Wacom tablet to sort of start our outlines. We make sure we clean this up a bit. Take this down a little bit. Make our outlines here. And one more here. Okay and we'll just keep fleshing this out, until we have just sort of a basic design that we're pretty happy with. I'm going to go here. And up here I want to have outlines. Okay and then once we're pretty happy with the overall outline, let's clean this up a little bit. And again, we can be pretty loose with this. Because it is supposed to just have a nice, hand-drawn, illustrated look. Nothing needs to be perfect. And then the next thing we want to do is take this brush size down a bit. And we might want to take the opacity down a bit. And then we'll want to go ahead and make a new layer. So this will just be shading. Let's go ahead and put these in a folder called black and white. New group, B and W. Okay drop these in the folder. And get our shading layer. And start on our shading. So we just want to quickly start shading. Just keeping this light and adding dimension to your whole object. Before we go in with the color. So this is just fast and light. And think of, you always think about where the lighting is coming from, the light source is coming from. And you'll want to do all your objects for your whole game that way. So if you're thinking, okay, the sun, you know, the light's, kind of coming from this direction, then we want to make sure that our shadows and stuff basically follow that. I mean, this is such a just kind of a hand-drawn look that we don't need to be too particular about that kind of thing. But it's good to always think about, okay, light's coming from above, then we need to shade a little bit more on the bottom, et cetera. And you'll just go through and make sure that, you know, you give your leaf a little drop shadow. Shade in the middle here. Shade your stem. And we're going to go back in here with color, but first of all, just because this looks nice to have the black and white shading first. And again, you want to keep your strokes nice and neat. Like you don't want to just be randomly going like this. As you can see that's going to look terrible, so we always need to make sure that our strokes are neat, and going in the proper angles. We want to really keep that sort of loopy look from happening. Because that just, it starts to look cheap. Even though this is sort of supposed to be a nice, hand-drawn, sketchy look, we still want to keep things neat and organized with our lines. Then we want to add some dimension to our leaves. So first of all, we shade in here. We'll want to actually shade on these leaf lines. So it looks like they've got a little dimension. I'm going to add a little bit of a shadow from the leaf onto the fruit. And you click R then you can drag. In order to keep your lines nice and neat, you'll need to rotate your canvas now and then. B to quickly select your brush. This just gets a nice fast workflow. And now I can easily draw in this other angle. And keep my, it really helps to keep my lines neater. And R rotate around again. B get our brush back. R. And you can use Shift to get it on exact angles, when you want to get it back just use Shift. B to get your brush back. So we want to keep on doing this until we're happy with kind of the way our illustrations are looking. And then we'll want to go ahead and create a new layer. 'Cause this is an orange we want to make nice little dots, to represent the orange skin. And I like to have this on a separate layer, just in case we wanted to go back in and rework our shading. We don't have to worry about, you know, messing up our little spots, so. We'll go, I'll pause this and I'll go ahead and finish this out. It's just the same technique, it's really our two brushes. The one that's a little bit lighter for our shading, and then our heavier one for the outlines. So we'll go ahead, and just finish that out to look nice. And we'll come back, and start on the color in a second. Okay so now we went ahead and finished this all out, and got a nice black and white outline with shading. So the next thing we'll want to do is go ahead and start filling in the color. So we want to go ahead and we can just use our same natural brush, but we'll want to make it much bigger. We want to keep the opacity kind of light. And we'll just start filling in our orange. And we want to think about color paletting and stuff with our whole gang. For example, this is just a pleasant kind of a natural looking, everything's just kind of pleasant and natural. So, we want to think about our orange. We don't want to make it, like, garishly bright and stuff. So we want to just go ahead and select all, kind of, more natural colors in all of our fruits. So we'll make sure that they just, they nicely match. We design a palette that works really well together. So again we're going to want to sort of shade our orange with our orange colors here. And you can see I'm not really worrying about when I'm drawing on the leaf and stuff. We'll just go in and fix that with our green colors later. This is just to get a kind of a rough outline going in here. Again, we want to make sure that we just shade this. We lightly touch up on everything. And now that's not, and we want to make it a lot, give it a lot more dimension. So we'll go ahead and take this, and we'll make this a little bit more yellow and a lot lighter. And we'll start sort of shading the middle here. Let's make this, the edges a little softer. So that we don't have such hard looking edges. And we just want to make sure this is nice and gradual. And let's take this much lighter. Really add some dimension here. As you can see, we're starting to get a nice, really nicely shaded look. So again, this is just kind of about what feels right. And since it is supposed to be kind of hand-painted looking we're going to, we don't want to get rid of all the imperfections and stuff in the final design. We actually purposely want to keep that look. So, we'll just add a little bit of a darker shade right to the edges, really make this pop. A little bit shading here. And we'll want to go ahead and clean up these edges now. And there we go. And we'll go ahead and start adding our leaf color. So we just want again a nice, a nice color that goes with our orange palette here. Let's start with this as a base color. Wanna take our... there's a nice shortcut for the brush size, are the bracket keys so you can quickly just scale up and scale down, adjust your brush size. Go ahead and fill this in. And all of your leaves just start with this base green. Again as you can see, I'm just kind of shading it for dimension. So where these leaf lines are, that's needs to be a little darker. So it's got a little more dimension. And then kind of fades out to lighter on the leaf tips. Wanna make sure we have gotten rid of our orange here. Add a little. Okay and then we want to go ahead and adjust this a little yellower, a little lighter. Add in some more dimension. Make sure the orange is gone down here. Add a little dimension here. Then we'll go ahead and get a real light. And kind of add a little bit more dimension. A little more shading here on the small leaves. Okay now let's go ahead and give the stem a go. So again we want a nice matching brown. That's just kind of pleasant, okay. And we start filling out the stem. And add a little highlighting. And a little lighter. Really give that final pop. And you'll want to continue working that way. First we'll need to add that little bottom bit. And we'll keep working this way until the entire orange has been filled out. We'll need to clean it up. And we'll come back in just a second with the final design. Alright and here we have the final orange object. So we'll go ahead and new group from Layers. Make sure its back and it's black and white. Folder, okay. Now you can see there's our color object underneath our outline object. So you just want to mess with this in how it looks, right? As you can see this is a very painterly look. It's not hyper-realistic. We didn't want it to be super realistic. We just wanted to have this kind of cool, handcrafted look. So now we want to go ahead and convert this to a Smart Object. Since this will need to be scaled, and used in different sizes, and create Retina graphics and stuff. This way you can scale it, and you can see you don't lose any resolution. So, at this point we are ready to convert to sprites and stuff. So, next up we will discuss creating animation.
So here we're going to discuss how we actually create our little bomb animation we want to do. Because our whole look is hand-drawn illustration we wanted to also do a completely hand-drawn, frame-by-frame, old school, cell-type animation. And we're going to do it in Photoshop so that we can keep all of our brushes, styles, and everything the same as our original design. And you can make sprite animations in any program, for whatever your style is. Since we want to keep this hand-drawn, we're doing it in Photoshop so everything matches. So first thing we want to do is go ahead and get our timeline, our Photoshop timeline, up. Right now we can't actually do anything for timeline, because we need to create it. So once it's created, what you can do, you can zoom in here, and you can see this is each frame. Nothing's happening because we just have one long frame. So what we'll want, we can just have our bomb there for reference. So we want to go ahead, Duplicate Layer, okay. And then we'll just move this. Just go ahead and move this up here. Okay, and then we'll want to go ahead and cut this. Cut, move it down so it's all in the, so you can see right now we've got three layers here. So if we drag this down to that same timeline, now we've got a video group. And this is where each layer represents a frame. So we want to cut this again. Let's just go ahead and cut. Go ahead and delete. Now you want to make sure that your timeline shortcut keys are enabled. You can see here Enable Timeline Shortcut Keys. And once you start doing a lot of frames, that's really, really vital because then you can use your keyboard to just use your backup. Your left and right keyboard keys, to quickly go through your animation. Now, since we have this here, our little spark animation is going to basically go up, and then the whole thing's going to explode. And we're not going to do every frame because that would just take forever to go through. But I'll use the same technique. So we'll start here. We'll get a nice one pixel brush to match that. Start with black, go B. Oh and we have, we need to make sure that this is rasterized, Rasterize Layer. You don't want to have smart objects. You need to be able to edit these, which means you need to make sure that they're full resolution while you're drawing this. Because again, you want to be able to make Retina animations and stuff, so you always want to have large artwork to start from. Okay so now we're going to take this down a bit. We're going to make this nice and dark. So we'll start by going ahead, and just erasing this. Erase up a little bit, okay. And now we can use our keyboard, and we can see okay, now we're up a frame. So I'm going to go ahead and add our little sparks here. Okay. And we'll want to add our color. And since this is just kind of a sparky little thing, we don't really need to keep anything super accurate. We need to understand the basics of motion, and how it works to have everything look right. Okay and then we would go ahead and Duplicate Layer, OK. Go to the next frame. Go ahead and erase everything we just did again. And we're going to take it up a bit. You need to be careful of accidentally erasing your lines and stuff. Because that will then follow through on your entire animation if you have a little accidental erase. And then you have to literally go through every single frame and fill it back in. So be very careful when you're doing this kind of frame-by-frame animation. You don't make that kind of mistake. Again. And you'll want to enable onion skins. Again if you can see this here, oops. Let's stick this to the bottom. Double-click, scale this back up. And Enable Onion Skins. Now you can see, you see your little outline of the previous skin. Which is really helpful when you're doing the animation. You're drawing in the proper position, and everything's laid out properly. And now actually, what I would normally do is do all the black outlines, and then I would go back through with the color. That just, it's actually a lot faster to do it that way. And then if you, you can play through, and if you had any mistakes in your motion, before you start filling in any color, you can fix your mistakes and stuff. But for this one, just to show how it's done, we're doing both color, and the outline at the same time. So that's an example. You would just keep going up and around until it exploded. So now we'll show an example of how we actually accomplish the explosions. So if we Duplicate Layer. Now at this point, let's just go ahead and get rid of our onion frames for now. Because it's distracting when we don't need it. And we'll go ahead and just erase. Since I have a tablet, I just turn my pencil upside down to use the eraser. Otherwise you'd want to select the erase tool. Okay, so let's assume that our wick burnt down. Now once I started on the actual explosion, what I might do is go ahead and just kind of take chunks out of our pineapple, just like that. Get the Move Tool, and drag out a bit. Get our next chunk. Select, Move Tool. Drag out, and get our next chunk. Select, move out. You can do Ctrl-D to quickly deselect. Move it out, Ctrl-D, selection. Move out, Ctrl-D, selector. And our final piece, move out. Let's go ahead and get rid of this for now, Ctrl-D. Okay and then what we might want to do is take our Outline Tool. Make sure that that's one pixel. Then we would have, say our explosion happening, just a nice explosion pattern. And we'd want to go ahead and erase everything from our pineapples inside of the explosion. Do our black outline back here. This, get this. And again, we would just do all of our black outlines to the entire explosion. Make sure our motion looked proper, and then start filling it in. But just to show quickly how this is done, we will start with our color next. So we want, you know, a light yellow. We will go ahead and fill in our color. Actually, the way I'd normally do this, I'll just do this how I'd normally do it, is I would want to keep the color on a separate group. So we go New Layer. We want to go ahead and cut this with our scissors tool. Drag this into this layer. It makes this layer, let's name this color. Name of this group, just name it color. And now we can go here. We want to, we actually want to put our color underneath our outline. And then we'll go ahead and select our color layer. And now we can do this and we don't have to worry about accidentally messing up our outlines and stuff. And then again, we'll want to use darker orange. This is an explosion. And this is going to be real fast, so we don't really need to be too exact with it. But we just want to make sure that the motion and colors are basically right. We should do this lighter. Make it fade. And we'll work that until the final explosion. So I'll go ahead and we'll finish this out. And when we'll come back, you can see what the explosion looks like. Alright so here we are. We just went ahead and laid it out in an example template with our paper texture and a couple other fruits. So we'll see what this really looks like, you know, in the final project. So we hit Spacebar and we play. And then you can see we have a nice little explosion. It really looks like it's exploding, it looks real hand-drawn. It's full resolution so we have our Retina explosion. And we can just see what it looks like. Now if we go through here frame-by-frame, you can see we have our little, our little wick burning up. And once it's burnt up, we're just doing our quick explosion. We have our pieces flying out, we make little trails. And it's all, just has a very hand-drawn look to it. And you just need to make sure you get your motion basically right. But there you have it. Okay so next up we will discuss how we need to actually export these sprites to turn them into Retina graphics. And export the animation so that it's in a sprite that's usable with Pixi.js and our development environment.
Exporting Sprites for Game Development
Okay, so here we're going to discuss how we go about setting up and exporting our sprites. Both our animations and our static graphics. How we want to organize it, how we want to do different file sets for Retina screens versus low-res screens. So here's kind of the final design that we came up with. It's all a very natural, hand-drawn, illustrative look. We've got our fruits. And we've got our other little graphics. So, first thing we'll do is go ahead and grab one of our fruits. If we get our orange. And I'll create a new document, any size is fine. Back over here, click, oops, we have two things selected here. Okay we'll drag them to a new layer, zoom in. And we don't actually want to have the drop shadow for this. Okay, so there it is. That's the size, the final size, that we're going to have in our game, roughly. So let's see if we can use our Ruler Tool. We decided that we're going to do 60 pixels. Okay that was our design. So this is fine for the low-resolution version. But since this is a smart object this is really easy. We need to have a Retina version. So we take this and it's 33 percent of the full size. So for Retina, we have to do double, so 66. And 66, and there we go. Now we can go ahead and save this. This is our Retina resolution graphic. So we want to go ahead and crop it down. You don't need to have it 100 percent exact, you just need it to be close. And as you can see, Photoshop automatically snaps to your graphic. Enter, make sure that you get rid of your background. This needs to be a transparent PNG. Okay, now you can do Ctrl-Alt-Shift S to save for web. And our save for web is like this. We want to make sure that we're using transparencies, and that we're using a PNG 24 bit. Then we go ahead and save. We want to go into Digital Tutors. Go into our game folder, individual sprites, and we want to save this as orange two at two x. We very specifically want to follow this naming convention. And we'll go over that in our development tutorials. But we wanted to start with making sure that we name our low-res version orange, and our high-res version orange at two x for the retina. Okay save, go ahead and close. Okay and then we can just, we would want to do that for all of our Retina graphics. And then we would just go ahead and do an image size. And we can quickly do it this way, 50 percent, and now you have Save For Web. Again we can do this. And now we can save our low-res images. We want to go ahead and have that in a separate folder to keep everything organized. Make sure it's named exactly the same as this. This was orange at two x, so this needs to be orange. And here we have it. So now that's prepared for sprite exporting. So we'll go ahead, now I am using our Texture Packer, so let me pull that up. So there's many programs, good free programs and stuff for sprite creation, I personally like Texture Packer. It works with Pixi.js and it's just, it's a really nice package. There's a free version, and you can purchase it. Personally I purchased it 'cause it's worth it for game development and stuff, so. We'll start with grabbing our sprites. So let's start with our at two x sprites. Let's do orange. And we'll go ahead and we can just drag and drop these into our Texture Packer. Okay so now we want to select for Pixi.js we need JSON Hash, we want to save the data file. So we would just save it to our game folder, under our sprites folder. And then we would go ahead and do, save this as fruits high DPI. And then the texture file. We want to make sure that's a PNG. Just go ahead and save it in the same folder, but do .PNG, same folder and same name. This just keeps things nice and organized. 72 DPI, so most of this is all going to be fine. We want to do border and shape padding. Because sometimes when you have no border padding, you'll notice a tiny one pixel discrepancy between objects, for Retina screens and stuff. So just be careful with that. We do not want to allow rotation. And everything else is fine as default. And we would just go ahead and publish. If everything works out, there we have our... done, everything worked, OK, so. We would do the exact same thing for low DPI. So if we open our low DPI folder, we want to go in here, clear out our high DPI files. Go here, grab our low DPI files, drop in, exactly as before. Make sure all of our defaults are still the same. And we would want to change this to low DPI. And we would want to change this to the same naming convention. Everything the same. We need our padding the same and everything and we go ahead and publish that. So that's static sprite creation. Now if we wanted to create our animation sprites, we would go ahead and load up our explosion. Okay we need to make sure that we, we move all of our extra file data. And then we want to go ahead and see how many frames we really need here. We want to drag our timeline to only the frames we need. So, we want to end on an empty frame. We don't want to end on the last explosion frame. Because then it will just, you know, we want the explosion to explode and dissipate. So, we make sure that we end it on a blank frame. Okay so here we are, that will be that. And then we'll need to go ahead and Export. So file, I'll drag this over a little bit, so you can see this File, Export, Render to Video. And then we'd want to go ahead and make sure work area is zero to 31. 'Cause we already selected that, that's good. Straight un-matted. Want to make sure that it's saved to its own individual folder. Because this is going to be exporting 32 frames. You want to make sure that it's in its own folder. Explosion.PNG is fine, go ahead and render. Replace any files there. And we get our export image sequence. Okay, now we have those files exported. So if we go back to our Texture Packer, there we are, clear out our files again. Go ahead and pull up our explosion. We've got our explosions file we just exported. Shift select all 32 frames, drop in. Now you'll notice, we have an error down here. Four not fitting sprites, try using multi-packing. So for here, we can just go ahead, this is actually bigger than our final animations are going to be. Again, we would have to make sure that we made the same low DPI and high DPI sprite animations. But we just left it default just to go over how this works. So we're going to go ahead, and we'll need to make this whole stage bigger. So that we don't run into the problem of our sprites falling out of screen. And now everything works properly. We need to make sure, again, that we aren't allowing rotation. Always defaults are the same. And then we would want to change this to our explosion. Explosion, again, this would be high or low DPI. And explosion. And we would go ahead and publish. So the sprite sheet, writing, and everything worked out good. Okay so that's it for sprite creation. Both your animations and your static sprites. Now to keep this, things organized and stuff, generally I figure out how to export these into sheets that make sense. For an example, for a game, I might do all the UI elements, like our little icons here, our background image, all the sort of static stuff, I would export as, and like our scoreboards and stuff, I would export as, like, UI assets. And then I might take the live files, like the peaches, and orange, and all those, and export as, say fruits, or live objects. For one thing you have to be careful how big your sprite sheets are. Certain devices can't handle huge sprite sheets. And depending on your resolutions and stuff, you need to make sure that your sprite sheets don't get out of hand and that everything's set up properly. And also just, things are organized and clear. So that's basically it for sprite creation. Just make sure that you have high DPI and low DPI graphics. And that your organization is all proper, so that when things, especially when things get a little bit more complicated, you're definitely going to want a separate UI sprite sheet, and then a separate animation sprite sheets. And then separate, you know, live static graphic sprite sheets. So next up, we will start getting into actual Pixi.js development.
Setting up Our Initial HTML5 Tools, Libraries and Frameworks
Implementing the pixi.js Library
So in this lesson, we're going to go over implementing the Pixi stage. So first off, we want to clear out our temporary functions here. And we'll start by writing the Pixi stage. So we just want to make our very first this .stage variable. So I have new Pixi.stage. And set a default color for the background. Just do a green here. We want to create a root Pixi object container, in order to handle scaling/rotation, et cetera. This.scene is going to be new Pixi display object container. And add to stage. Okay, so this is just a good practice to create a root container object. All your children, all your Pixi children and stuff will be added to this main display object container. So, you know, if you're dealing with Retina scaling and different device sizes, and orientation, you need to rotate the whole scene and stuff. It's really good to have a main container to deal with all that. So that's just good to keep in mind. And next we want to create a renderer instance and set size to defaults. So here will go this .renderer. Inspite of Pixi.autoDetectRenderer. And do the defaults that we set up top. .canvassize., so the width, and the height. Okay so this is just the Pixi auto detect renderer. So if you have webGL, it'll be using webGL, and if not it'll default to the canvas renderer, so. It's just a simple renderer setup. And next we'll need to actually add the renderer to the DOM. So we'll use our jQuery selector at this .element, .append this .renderer.view. And we'll want to start our animation ticker using request NM frame. This .animate.bind this. So we'll want to go ahead and create our animate function here. Animate function, which will then call request atom frame. And this is what will make our animation actually happen. So to go over the request atom frame a little bit, that was originally proposed through request animation frame function. Which was eventually adopted and improved by the WebKit team on the makers of Chrome and Safari. So request animation frame provides a native API for running any type of animation in the browser window. From webGL and canvas, to DOM element, CSS, et cetera. You don't need, you'll see here we didn't specify any kind of a frame write, because you don't need to specify an interval write. Because the request atom frame function depends on the frame write of your browser and device. But it's typically 60 frames per second. The same as most computer screen refresh rate, which is 60 hertz. But basically this is requesting a new frame and the next available opportunity, not predetermined intervals. And another nice thing that request atom frame does for you is when you switch to new browsers, or new tabs, it will throttle your animation. So you aren't just wasting energy when you aren't actively using that canvas. Plus, another thing is that it groups all of your animations, basically to happen on every step. So that saves a lot of energy instead of, you know, a bunch of things happening all sort of pell-mell. It just groups them all so that it's happening at 60 frames a second. Everything happens on that 60th frame, so. So that's nice. Any way it's a great new feature browsers have implemented, and it works really well. So after we do our request atom frame, we're going to let it go ahead and just set up our load assets. So this.load assets function. Oh, and another thing to notice, is we did this .animate.bind. So here we're going to need to do this.stage. Wait, this.renderer.render this.stage. So we need to, of course, render our Pixi renderer on every frame, or every step. So we need to call that in our animate function. And again, so we're using this to animate.bind this, because we have to reference this.stage. We have to reference our main game prototype variables, so we need to bind this to the main prototype. So that's how that's working. So any way, we'll create our load assets. So let's load assets function, then comma separated. This will be var loader. So we're going to do Pixi.setLoader. So this is just using Pixi's asset loader, it's built in. And we're just loading our one file. This is an array as you can see, so you can load multiple files. For now we're just loading our interface. Pixi sprite sheet and then loader.on. Again, we'll probably want to eventually have progress loader indicators and stuff. But for now let's just do our on complete. Again, we'll want to bind this and loader.load. That just tells the loader to start. And we'll need to go ahead and create our on load assets function, comma separated. And then get our first texture. And create our sprite. Okay, and then we want to add that sprite to our scene that we discussed earlier. So that we can actually visually see it. So we just created a texture. And this is pulling the sprite in from adjacent files. So we don't have to do the folder name or anything, we just do it based on the names within adjacent files. So that's nice, okay so let's save this and go over to Chrome, and make sure everything's working. If we refresh, we saw that loaded in there, our image and everything. We don't have any bugs, so that all worked. Okay we created our Pixi stage, we created our request atom ticker, we loaded in our assets, and we added them to the stage. So the next thing that we'll discuss is creating our high definition images. So we'll have to load up a separate file and stuff, and so how do we handle Retina images and multiple files. So we'll go over that in the next.
Making Our Game Responsive
In this tutorial will go over making the game responsive, and loading in multiple sets of assets for different DPRs. So first off, we'll start with our DPR variable. So this will just be our device pixel ratio. And we might want to switch this out to one or two for testing and stuff, but we'll go ahead and just start with our actual device pixel ratio. Okay, and then here we'll want to go ahead and create our event listeners. So add event listeners for resize. So we'll just use jQuery on resize and device orientation. And we'll want to this.renderer resize.bind this. And then we'll want to when our game first loads, we need to trigger the resize. So window, again we can just use jQuery's trigger for resize. Okay, then we'll want to create the renderer resize function here. Okay, and then our renderer resize this is where we're actually going to scale the scene proportionally, the game proportionally to fit in your screen. So let's start with our create target scale. Our target scale gets a value of one. So if DPR is greater than 1.2 load and scale everything based on a DPR of two. So if our device pixel ratio is greater than 1.2, we're going to want to go ahead and just default our DPR to two, load in our double-sized graphics, and scale everything according to those double-sized graphics. So we aren't loading in multiple sets for our DPR three or anything, so we just want to handle two sets of graphics. So we're just defaulting if it's greater than 1.2. We're defaulting to, and I chose 1.2 for this game because after 1.2, you start to notice that the textures are degrading a little bit, so we just made the jump from 1.2 to two. And for your game, if it's a more higher fidelity, not hand-drawn game, you might want to put that to one, or you might want to just go ahead and switch that over to two. For me I wanted to save a little bandwidth, and just switch it over at 1.2. And that's where you start to notice textures aren't looking as clear, so. So, if our DPR is greater than 1.2, defaults.DPR is value of two. So now we'll just create a few variables up top, to make our code a little faster to type. And Iw for enter width. Okay, so our new width is going to be enter width divided by canvas default width, and same for height. So next we'll need to constrain scene scale to a max of 1.0. So if new height is greater than one, and new width is greater than one, we're going to just leave our target scale to one. So we don't want our scene to be scaled bigger than what we designed it for. Because textures will start looking pretty bad. So we want to just go ahead and say if, if the screen is smaller than our game, we want to scale the game proportionally to fit the in the screen. But if it's larger, if the screen is larger than the game, we don't want to scale the game up past what we designed it for. So if height scale is greater than width scale, then set new target scale based on width scale. So we just want to scale, we want to scale it on the width. If the new height scale is greater than the new width scale, or else we want to scale it on the height. So you'll see what that looks like here in a second. Target scale is value of new width. Else target scale gets a value of new height. Okay. So we need to set our width and height variables based on our new adjusted target scale and DPR. So if our width is going to get the value of target scale times, and now we convert it to our actual pixels, times CV.x. And the same thing for the height, y. And resize our renderer and scale scene container. So this .renderer, so you just go ahead, and get our renderer and do a resize for width and height. And then we want to scale our scene to our target scale divided by our DPR. Okay, so the next thing we'll do is go ahead and load up our separate sets, our high-res and low-res assets. So right here we just have our low-res, so now we need to create, load in our high-res and handle that. So load texture sprite sets based on device pixel ratio. So if defaults.DPR is greater than 1.2, again, we'll load in our high-res folder, so. That slash to x else, put in our low-res, okay. You'll remember we named our assets like orange.PNG and orange at two x.PNG, so the sprites in this high-res adjacent file will be named at two x. So we need to go ahead and create a little function to handle that, so. Go ahead and go there, and then SP res function. So here we'll go fix sprite name for screen resolution. And Pixi.js auto handles the at two x naming convention. Okay, so Pixi.js actually automatically knows if you load in an image called at two x that it's double-sized. And it'll automatically scale it down. So it loads in high-res, but scales it to fit your design appropriately. So that's nice, and we can just go ahead and make use of that. Name is already at two x, else NM gets a value of nothing. And then we'll want to return NM. Alright, so we'll take a look and see if that's working appropriately. Okay so it's scaling on the height, as you can see. And then if we try to scale on the width, yes. When it hangs out of the width, then it will scale based on the width. So that works nicely for just a simple, responsive design. So here we have, in Chrome Developer Tools, we can actually test different devices and pixel ratios. So we'll go ahead and select device, say the iPhone 5, which is DPR of two. And now it looks like it isn't working. That's just 'cause you'll probably need to reload your screen. You might need to reload the page for proper user agent spoofing and view port rendering. So let's try a reload. Okay, so we'll see we have our high res images loaded in, and everything is still scaling appropriately. And then again we can test iPhone 6 Plus to see if it's handling a DPR of three correctly, and still loading in the double-sized images, and everything's still scaling appropriately. So, we can see that our code works. So that's, Chrome Developer Tools here are really great for checking our responsive designs and stuff. And clear everything out, refresh, and it's all working well. Okay so here we went over how to make a simple, responsive layout, and load in high and low-res graphics for different DPRs. So next up we will be looking at adding Box2D.
Integrating Box2dweb with pixi.js
Implementing the Box2d Debugger
In this lesson, we'll go over implementing the Box2D debugger. So first off, we'll want to go ahead and get into our index.HTML and add the debug canvas. So div ID debug width 500 pixels and height 900 pixels style. And that width and height will be adjusted by our code, but for now we'll just leave those settings. And do position absolute. And add our canvas ID debug canvas. And canvas close, and close our div, okay. So there's our debug canvas. And now we'll want to go back to our game main js, and start implementing the canvas. So we'll want to scroll down here to our physics world. And then we'll go ahead and just set up our canvas here. Set up debugger canvas. So this.b2debug. And then add a function here. And then set up debug draw var debug draw is value new Box2D.dynamics.b2debugDraw. So just create our new debug draw variable. And then get our debug canvas. So you get jQuery selector, get our DBC element. And debug canvas attributes, and here's where we're setting our width and height to be the same as our game. And our width, and now it will always match up with our game canvas. And then set sprite. Get our DBC element context. Oops, we'll want to get to context. And so we're just getting our canvas context for debug drawing. And then scale our debug graphics and alpha transparency, at set. Okay, so we debugDraw.setDrawScale, and we'll just want to use our defaults, our scale variable, to make sure the scaling is correct. And debugDraw.setFillAlpha. And just, we want to make this fairly transparent, so we can see our graphics underneath our debugger. And then we want to list, use our list of flags, to show our debug canvas we are showing shapes and joints only. There's a lot of different flags you can set here. For this game all we really need is the shapes and the joints, so. Draw set flags Then new Box2D.dynamics.b2debugDraw.eshapebit. And we also want to do the Box2D.dynamics.b2debugDraw.ejointbit, okay. And this.world.setdebugDraw debug draw. So we just set our debug draw using our debug draw variable. And then the next thing we'll want to do is we want to have, of course, redraw the debugger on every frame. So we just set this.world.DrawdebugData. Okay, so now we want to go and check to see if that's all working. So this is 05 s zero two l v 05. S zero two l v. Okay, and right now that we can see there's our Box2D shape for the floor. And it's not, we don't have this set up to be responsive yet. So this is just the debugger, so we'll need to go ahead and just scale up our screen so we can see the full thing. We move that, and there we go. Now we can see that that's where the ground plane is positioned. That looks about right, so the boxes, or fruit boxes, will be falling right there, kind of in the middle of the grass. So that all looks fine, we don't have any errors. So, our debug drawer is set up there. And in our next lesson we'll start actually discussing creating the falling fruit system and joints, and Box2D bodies, and stuff, so that's up next.
Implementing Our Primary Game Logic
So in this tutorial, we'll start setting up our fruit block falling system. But first we want to, just adjust this a little bit to make it work in our responsive stage. So we'll go to our renderer resize, and after our target scale, we will set adjust physics scale to match screen size. So defaults.scale gets a value of defaults.scale/targetScale. And then we'll want to go to our Box2D function here, for our debug draw. And then want to actually change this to 100 so everything's based on 100 and then adjusted based on the screen size. Okay. And then we'll start to go ahead and implement our random fruit fall. So if we go back up to our physics, you can do Ctrl-R and then type in physics, and it immediately jumps us to our physics world function. So physics, Ctrl-R to close back down. Okay, but first we're going to create a couple of walls for this game. We don't actually really need the walls, but just to future proof it, if we're going to add additional functionality where we need to detect the edges of the scene and stuff, it's nice to have. So we're going to go ahead and just add some wall bodies here. Let's do wall, this'll be the left wall. All this can be the same, and then we want to do the size though. We want to do, width should just be ten pixels, and then the height should be this scale height, I mean this canvas height. Okay, and then we want to do position.set. And again, we'll want to set that to the bottom of the screen, the y and zero on the x, so that should be correct. So right position that set, we want to set this, well it's the wall, so we'll put it like 30 pixels out from the edge, divided by scale. We want it to be right at the edge of the trees, actually. And then on the x we can go ahead, or on the y, we can go ahead and set that to zero. We want it to be right at the top of our screen, okay. And we'll want to do the same, well first of all, let's change this out to wall l, wall l, wall l, and wall l. Okay, copy and paste, right wall. Change everything else to right, right, right, and then this we're going to want to go ahead and position to the right-hand side. And we can go ahead and check that in our scene. See if everything is working, oh it looks like I made a typo there. Oh, we forgot to rename the wall l to a r. Okay and we can see we have our walls and our floor, and everything seems to be working fine. So next up here we're going to do a randomly generate some fruit falling. So this.randomfruitFall will create our random fruit fall function. New r random fix gets a value of defaults.fruitSize. And var then we'll store this for our set timeout function. So we're in the right scope. So this.createBlock. We want to create our first block, 75 pixels from the left-hand side, or all of our blocks need to be 75 pixels from the edge. So, 75 plus random x defaults.foodTypes. And we'll just choose, it doesn't matter, we'll just choose any of our fruit types, set up that functionality. And then we'll want to set our time out. We're just making two blocks here, just to test everything. So eventually we're going to change out this functionality, but for now we just want to create a quick test to make sure things are working properly. So, and then this we will offset. So this will just be two blocks falling in pretty quick succession next to each other. Okay and then we want to go ahead and create block function. And then this is where we'll be actually creating our fruit blocks and setting everything up. So set up initial fixture bars. So, we'll just, you know, you might want to mess with these for your game and figure out what works best. Here's kind of what I chose based on what seemed to look pretty good to start out with. And we may adjust this as we build out our game. So this.fixStaff.density. Start off our density, and have that at one. And restitution, let's try that at 0.5. And set our friction, oops, to 0.5 as well. Okay, make sure we are using a dynamic body. This can be set to static or dynamic, and obviously, we want these bodies to be dynamic because they're dynamically falling and interacting. B2 dynamic body, okay. So went ahead and divide fruit size by two set as Box uses half. And Box2D set as box function uses half measurements, so it's half width and half size. So we'll just set that up right here. To get our fruit size, which is 60 pixels, but we'll want to divide by half, by two. And then set up the shape and position for the fix, do our set as box. And do block scale that we had for above, but of course, we want to divide by scale, because we don't want a 300 meter block. And divided by scale, we just want a square box. And this.bodyDef.position and set our positioning x pos divided by scale and negative block scale divided by scale. And we'll want to go ahead and set a couple of variables here. As you can see we're setting our x position right up here, and our item type. So we're going to go x pos and item type, okay. And then we'll want to go ahead and create the block body. Give it a user data object, and add the item type name apple, pear, et cetera. So create r the block. And we'll want to go ahead and create our body using our body definition. And create fixture using our fixture definition. And then we block.user, create a blank user data object. And set our name to the item, item type. We'll want to create a prismatic joint to pin block to, movement to the y axis only. And this will be how we actually pin that block. As you remember when we first designed our game, we made note that we need to pin our block to one axis. We don't want them dynamically hitting each other and bouncing around, we just want them to be falling straight up and down. So, that's what the prismatic joint will do. Okay, so this.block, so we'll be creating a prismatic joint generator function. Here, and set our world to this.world. Axis, it's a new b2 vec, set a vector of 0.0 and 1.0. And this is what's going to tell it to pin only to the y-axis. And if that was 1.0 and 0.0, then it would be pinned to the x-axis. So we want to make sure that's pinned 100 percent to the y. Okay, so that would be 1.0. And then we'll want to set the body, a, for our joint as the block. And our body, b, for this joint will just be the world ground body. So this.world.getgroundbody. Okay, and now we'll go ahead and create that block prismatic joint function. With our staked object, okay. And you can set limits and stuff for your prismatic joint, but this is just our blocks are falling from above the stage, and falling all the way down until they hit the ground object. So we don't need to create limits and stuff. This is just a very basic prismatic joint. And the joint's name space, and then our b2 prismatic joint def. Jointdef., now here's where we'll initialize, oops, initialize. Then we get our variables that we set, so state.bodya, state.bodyb, state.bodya. And we want to center it to the center of your object. So get world center, so it just gets the center of our block object. And then set it for the axis, and then we'll want to add to our world. Create joint, joint f, okay let's take a look at that, and see if there's any errors. We can see we have a couple of errors here, so let's go to console. You can see at number 229, so let's go to line number Ctrl-G 229. And here we have restitutions, we forgot to do our equals. Okay refresh, we can see we still have one error left. Go ahead and check that on 243. So we'll switch back over, Ctrl-G to 243. Here the block is suddenly mistyped world, so let's fix that. Save, switch back over, refresh. And we see we have our falling block. However, we still have an error, so let's figure out what that would be. So 221 is, oh, we forgot to, we're not in the right scope there. So we'll have to go back up to set timeout and use the right scope for this. Okay, take a look at that, and no errors, and the blocks are falling in appropriately. So that's all working well. So next up, we will be taking a look at finishing out this whole fruit falling system, and adding the actual random functionality and stuff. As well as adding a bomb system, so that's up next.
Implementing Pineapple Bomb Functionality
So in this tutorial we'll start building out, finishing out our fruit, random fruit falling functionality. And we will start on our pineapple bomb. So first of all, we might as well just clear out all this temporary functionality. And we want to create our loop function. And then we'll want to create our random timer, for random. And we probably want to change this as we're messing with our game, but for now, we'll just add it to this. So, it'll be pretty often they'll be falling, this is 1000 a second, 500's half a second. So this is probably falling pretty fast, and we'll just have to mess with it and get what works best for our game play. And we'll want to do a set time out, and using our rand variable. Okay, and then we'll want to randomly generate falling fruit slash enemy blocks. Var random fruit gets a value of math.rand math.random times defaults., so this is just going through all of our different food types, our peach, apple, and orange, and bomb. And randomly generating from that array. Okay and then we'll want to go ahead and do our random positions. Random x position based on fruit size, add one pixel so they don't bounce. Okay, so we're just doing our fruits randomly positioned along the x. And we're just adding one pixel to our fruit size, just to make sure that they don't hit each other a little bit, and get stuck and whatnot. So r random x gets a value of math.round defaults.fruitsize plus one, and times math.round math.random. And here's our defaults.fruit, fruit on x, so that'll be eight across. Okay, and then ref.createBlock, so there's our create block. 75, that's 75 pixels from the left, is where it'll start. And then our random x, and set our food type to our random food type. Okay, and then we'll want to go ahead and loop, and do it all over again. Alright, so we can check that and just see if there are any issues. Okay we have an error here, so we obviously mistyped math on 224. So we'll just need to fix that, and check again. Okay everything appears to be working. Randomly generating and falling in, all positioned correctly. So this looks fine for now. Okay, so we'll want to go ahead and in our actual create block function, we're going to want to add a little bit of additional functionality here. So, if item type is a bomb, then we want to destroy nearby blocks. So, if item type is equal to bomb, set timeout. So, this is just setting up for our bomb functionality. When it explodes, we want to set a radius, a distance to remove the nearby fruit. And after four seconds, if we haven't caught it and removed it, then it'll need to explode. So if not tagged for removal, then explode bomb. So if the block.userdata.discovered is not equal to true, ref.blastradius. We'll want to do a blast radius from the position of the exploding block. So, get position.x, and the block.getposition.y. Okay but for now we're just going to comment this out, and we'll get back to that in a bit. Okay, else, so if it else, fruit should auto die after 15, 25 seconds of life. So like our original sketch up showed, we want our fruit to rot after it's been sitting there for a little while, keep the gameplay a little bit more engaging. So set timeout function ref.destroybodies.push. So this is just pushing our body that we'll need to destroy into our destroy bodies array. And we'll discuss why that is in a second. And then we'll want to create our math.random times 10 plus five and times 2000. So in 15, 25 seconds we want this to destroy. Okay so then we'll discuss that destroy bodies functionality. So down here, we'll just want to go ahead and create that. So, destroy all bodies in destroy list, have to wait. So, send bodies to a destruction list to be destroyed in your animation step. So, Box2D doesn't allow you to just destroy bodies if it's, if it hasn't completed a step it puts a lock on it. So that's why you have to go ahead and push it into an array, and wait until a step has happened, and then destroy. Destroy the bodies in the array and reset the array, so. I is five zero if I is less than this.destroybodies.length, oops misspelled bodies. And loop through. So, this.world.destroybody this.destroy. Then we grab the body out of the destroy bodies array. So we just loop through and destroy each one. And then we want to make sure to clear out your array after bodies have been destroyed. This.destroybodies gets a value of a blank array. And see if that's working. Okay we see we're having some errors around here. So we'll go ahead and go to our console. I've got reference error ref is not defined. So in 271, go to line 271, and here we are. So we have to, we actually have not set up that variable. So we need to set that up here. R, we referenced this for our set timeout scope. And refresh, I'm going to make sure this is working. Okay we don't have any errors running, and our blocks are destroying after a certain amount of time. Okay so all that seems to be basically up and running. So we'll go ahead and next we'll be finishing out our blast radius, and our bomb functionality, so that'll be up next.
Understanding Bitmask Collisions
So this tutorial, we'll create the bomb functionality, but first we want to go ahead and implement Box2D bitmasking. As you can see in our original function, the blocks can kind of get stuck on each other and stuff. So we want to make sure that they don't actually hit, collide with the blocks outside of their row. So, we'll go ahead and set up that functionality first off. So here we're going to set ground and walls to be, to collide with everything. So we want to make sure the ground and walls always collide with the blocks. So this.fixdef.filter.categorybits times zero x f f f f, okay. We'll want to go down to our block, create block functionality. And that's where we'll want to implement right here. We'll want to go ahead, first of all, and implement our masking functionality. So, separate this out. Go ahead and create for our fruit on x rand. We need to separate this functionality out a little bit. And we'll go ahead and grab this. That's what we're going to base our masking on, so we just need to separate it out. So that's each row, one through eight, that's each actual row of fruits. So, we need to separate that to create our masking functionality. Okay so, set bit masking for every other fruit line, so they don't affect each other. Okay so, var masking is a value of zero x zero zero zero two. And if fruit on x rand, we need our modulus. So, it's every other row equals zero. That will be so every row is zero zero zero two, and then every other row is zero zero zero zero zero zero four. So that way the bitmasks won't affect the rows next to each other. So, c masking equals zero x zero zero zero four. Okay, and we want to add now that variable here. Okay, and here. Okay so here, we'll have to go after this block. We'll do set mask bits in category do not collide with blocks not in their x position. So this.fixdef.filter.categoryfits, has a value of MC. This.fixdef.filter.maskbits gets a value of MC. Okay, now we can go ahead and test that, and see if there's any errors. Okay, we have no errors. So that should be working much better, 'cause you can see they're not accidentally colliding with each other. And everything's nice and smooth, and works much better. Okay so, we're going to want to go ahead and implement the rest of our bomb functionality. So let's just go ahead and uncomment that. And now we'll need to create that last radius function. So, after our prismatic joint function, we want to do blast radius function, x-point and y-point, comma separated, tight collision rectangle around blast radius. Okay so, so block pause, so new Box2D.common.math, and our b2 vec2, so that's our x-point and our y-point. So that's where our bomb explosion happened. AABB is a new Box2D.collision.b2AABB. And AABB is axis-aligned bounding boxes. So now we want to set our AABB lower bound set to our x-point, minus 0.5, and y-point, minus 0.5. So that's just setting a radius for our lower bound, and then we'll need to do one for our upper bound. This is making a radius around the original explosion point, okay. And this needs to be plus and plus, and that's 0.5 meters. And we might adjust that later on in the game depending on, you know, how many we want to explode and stuff. But for now we will just do 0.5 meters. So, test if there are any AABBs, axis-aligned bounding boxes, within the blast rectangle area. So var body and store our reference to this and this.world., now we do a query AABB. And leave fixture that was. So if we find a axis-aligned box within our, where we set our lower bound and upper bound area, then we'll create a function to handle that, the item that was found. So function is our fixture, and we only get bodies that are dynamic. We don't want to destroy walls, or floor, or any other static objects we might end up having. So if fixture.getBody.getType, so the fixture that we found, if the body type is not equal to Box2D.dynamicsb2body.b2staticbody, body gets a value of fixture.getbody. We want to go ahead and destroy body, and find next body within radius, so ref.destroy. So here's where we append it to our destroy bodies array. And push body, and then return true, and it'll continue looping through, and find any other bodies that are within the upper and lower bounds. Okay, and then here in this, okay after fixture. Fixture here, we need to go ahead and finish this function out. So, I need to get that AABB. Alright so let's go ahead and see if we... Just double-check that everything's closed here properly. Fixture, close here. Okay, so let's see if we have any errors there. If we refresh, and no errors are showing up yet. But see when we have an explosion, if everything works correctly. And got reference error fixtures, uh-oh, we misspelled fixture. So, go ahead and do the correct fixture, and refresh. Okay, no errors are showing yet, but let's see when we have our first explosion. And let everything explode, as you see, all close by, nearby objects exploded with it. So, everything's working well, all of our objects are dying, and they're exploding, and they aren't colliding. So, everything appears to be working correctly. So, we can just watch that, make sure everything works out. And then we'll want to go ahead and start adding in our Box2D mouse interaction, so that's up next.
Handling Box2d Mouse Interaction
In this lesson we'll handle our Box2D mouse interaction. So first of all, we'll want to start with our get body on click functionality. (Typing) Okay we'll want to start our function down here, after blast radius. So we'll get our reference to this. jQuery on double-click, let's use our jQuery double-click function. This is where we'll be creating interactivity to, for when our user's double-click on blocks. It removes touching ones, and adds score, and where you can remove the bomb before it explodes, et cetera. So find body at mouse position, where double-clicked. Okay, now we'll want to go ahead and give our clicked body. So, value of ref.getbody at mouse, and we'll want to get an e for event listener. So, e., so we want to get our mouse position. Divided by default slot scale, again we need to make sure that that's based on meters for Box2D. And, e.clientline divided by defaults.scale. Okay, so we'll go ahead and create that function. So here we'll go get body at mouse function, comma separated, and then this is where we'll want to go ahead and create our main mouse, get our mouse position, and handle bodies underneath the mouse. So, create collision rectangle around mouse pointer position. Okay this is actually pretty much just a copy of what we had over here for the blast radius. So we'll go ahead and just copy this bit of code, and adjust it for our mouse. Get body at mouse, go ahead and paste that in there. So, this should be mouse pos. We want to put our x-point positions in here, and our y-point. For y-point this is all fine, but here we want to just get right underneath the mouse pointer, so we want to make this very small, so it's just the size of a mouse pointer. But we still need to have our upper and lower bound. Okay then we get our body, we don't really need a store ref. We go through here, we get a fixture, make sure it's not static. And then we need to add another if in here. So, if fixture.getshape.testpoint fixture.getbody.gettransform and our mouse pause vector. So, only get bodies under the mouse point position. So, body gets a value of fixture. or here we already have this in here, so we'll just remove that. Okay, body, so we don't need, we aren't destroying any bodies. And we want to return false, 'cause we only want to get the one body. Return true, so keep going until we find a match. And then if we did find a match, we can go ahead and just shut this whole function down. Because we're obviously only looking for the one. There'll be one object underneath the mouse pointer. Okay, oh and then we'll want to go ahead and actually return this body. Okay, and then up here we can start finishing out this function. So this is where the actual functionality of finding nearby bodies and stuff is going to happen. So if we successfully double-clicked, start removal logic. So if clicked B, so if we actually clicked a body, and clickedB.userdatadiscovered. So, we wanted to set a discovered tag to flag to true here. And then we want to create array to add all matching blocks for destruction. And ref.destroybodies.push, we want to go ahead and destroy this clicked body. Then, next we need to get all contacts of clicked body. So, all the nearby blocks of our edge gets a value of clicked B. So every block that's touching our clicked area, going to get contact list. So that finds all the contacts. And then function to loop through and tag discovered bodies starting from our clicked body. So, function start B of our edge gets a value of startB.getcontactlist. Again, this is our starting body, and now we're finding contacts from that starting body. While edge far other side edge.other. So if other is of same fruit type as clicked body, and hasn't been discovered, then we'll add to removal list. Tag as discovered and loop through next set of contacts. So, we'll go ahead, we find all the match, we double-click a fruit block, we find the surrounding fruit blocks that are the same type and touching that block. If we find any of the same type, well they need to do the same thing on that fruit block. We go and find any surrounding, touching, and then we add them to the destroy list and so on and so forth. So, we have to just keep looping through until we've tagged everything as discovered, and put in the array for destruction. So if other.userdata.name is equal to clicked body.userdata.name and other.userdata.discovered Is equal to user, and other user body discovered is not equal to true, I believe not equal to true, other.userdata.discovered gets a value of true. Ref.destroyedbodies, and we want to push for destruction. And match other, other, and that's where we start looping through our function to match the next body. After edge gets a value of edge.next, we'll need to loop through to the next edge. Oh, this is supposed to be match other, okay. And then, why don't you go ahead and start this function off with our clicked body. Okay, so we can go ahead and take a look at that, and see if everything's working how it's supposed to. So, that's lesson S zero six. Okay, we aren't throwing any errors right off the bat. So let's see what happens if we double-click. Okay it appears to not be handling our clicks. Let's go ahead and refresh. Okay, so that's working, we're matching. And as you can see, we find multiple sets, it destroys the multiple boxes. And if there's just one matching, it just destroys that one. So this is all fine, it's not throwing any errors. The only thing is, we'll notice right now, if our stage is scaled down responsively, our mouse positioning is not set up to be responsive yet. So we'll get back to that later, but for now, that should be good. And that wraps up our Box2D mouse interaction.
Implementing the UI
Implementing Visuals and UI with Box2d Physics
Okay, so in this lesson we'll continue building out our interface and pixi sprite visuals. So after our random fruit fall, but before our create block function, we'll be dropping in some new functions here. So we'll start with begin, oops, begin game in here. Okay so this. and here's where we're now telling the game to become active. So game active equals true, this.randomfruitfall. So, now this function will be kicking off our game. Next one, we'll do restart game function. And in here we'll start, or clear all fruit blocks and scoreboards, for our... so we have to clear out all of our physics bodies. So we loop through our world, finding all of the bodies and clearing them out. Get body list, A B, so this is just going through and getting every single body. Loop through all the bodies and destroy only dynamic, we don't want to be destroying the walls or the floor. So we're only getting the dynamic objects. So if BB.gettype is not equal to Box2D.dynamics.b2body.b2staticbody go ahead and clear out. So this.destroy, and we're going to destroy bodies and actors. Because we want to, we'll be wanting to destroy both the actual physics body and the pixi sprite that was tied to the body. So that we can visually see what everything's doing. Okay so that would be our restart main functionality. We'll want to add a couple more things here in this. So we'll want to reset, reset score and the lives. Set score value of zero and this.lives get's a value of three. This. so here we're just going to be triggering a... we'll be triggering an event based on, throughout our game. Well and for several different things we'll be triggering events, so that we can tie our interface with our main game. So for an example, when we lose lives, or our score changes, we want to just go ahead and make an event trigger that can then update our interface and stuff. So score change this.score, so that's triggering a score change. And this will be adding some data, so that we know what the new score is. And scroll, and then this will be life lost. And this.lives, and this.UI.begincountdown. And when we originally designed our game, we had a clock that counts down before the active game starts. So that's what we're just telling it there. We're starting our begin countdown timer before, when we reset our game. So we just hit restart, our whole scene clears out, and a countdown starts before the next game starts back up. So now want to destroy bodies and actors. Then we put our body. Var the IDX, survive body.IDX, var pixi body. So this is where we're looping through our bodies and actors that we pushed our bodies into. And then i++. So if this.bodies and actors io.IDX is equal to the IDX, so like where we created the unique ID. We're now looping through bodies and actors and figuring out if this body, body to IDX, has the same ID as our sprite. Because the bodies and actors array, and actors array looks like this. And some multi-dimensional array, so we have, okay here we have our array. And then we have each individual one looks like this. So it's our body and our sprite. So, we're going ahead and saying, or here it's the other way around Sprite and body, see what I was saying was our sprite ID is equal to our body ID, and that way we can destroy them together. So pixi body gets a value of this.bodiesandactors. I, and then we're getting the actual body. Now pixibody.parent.removechildpixibody. So we get our sprite and remove it. This.bodiesandactors.splice. So now we want to go ahead and get rid of the... remove that part of the array so that it's not hanging around. Just make sure everything's cleared out. And then we might as well break our functionality. Because we just destroy one actor at a time. So, we'll just break our loop so it's not looping through when we don't need it. And then we want to destroybodies.push. And then this, since we have to destroy our bodies, as we discussed in an earlier lesson, on each step, when the world unlocks it, we're going to have to go ahead and push for the next step. And then, want to add one more function here, the end game function. This.UI.gameover, this.settings.gameactive is value of false. So we're just telling user interface a game over element's to happen, and that the game is no longer active. So, in the next lesson we will be working on fleshing out the rest of the functionality for our sprites, and tying to physics bodies and all that.
Implementing Visuals and Taking a First Look at Tweenmax
In this tutorial, we'll continue tying our sprite objects to our physics bodies, and tying our UI into our main game functionality. So here, we wrapped up with endgame in our last tutorial. So in create block, we'll start adding a bit more functionality. So after row collision or x-row information, want to go ahead and create pixi body based on type. So if our pixi container sprite gets a value of new pixi. The display object container, our pixi sprite is value new pixi.sprite.fromimage. So we're creating the sprite based on our item type. So our orange, peach, bomb, et cetera, plus we'll want to go ahead and use our resolution function to make sure we get the high dpi images when we need to, and .PNG. Okay, pixiSprite.anchor. And just set our anchor position to the middle of our sprite. Pixi, and we want to put our sprites in a container, so when we add animations and stuff, we'll need to have a container that it's all contained in. And we'll just start our pixi container position completely outside of the screen. So we never see it starting inside the screen, we want to make sure it starts outside. And then add child pixi sprite, this.scene.addchildpixicontainersprite. So we just add them to the scene, so add individual functionality to the sprites. So here we're just going to have to offset our motion sprites a little bit. So that they, or our active sprites, so that they align with our physics bodies, based on the actual graphics that we grew and saved out. Ideally you would probably have an adjacent file or something, you pull in with all your sprite information, but it's a little bit outside of the scope. And since this is a pretty simple game, we can just do some switch case functionality here. Okay, so this is a bomb, pixisprite.position.x equals... So just make sure they all aligned to their physics bodies. Okay, and here's where we'll be adding TweenMax functionality. So we will go take a look at that library in a second and add it to our file. So this is just telling the TweenMax engine that we want to animate the pixi sprite to 0.05. So we start it at negative 0.05, we animate it to 0.05, so this'll make a tiny, little, or not. Sorry, this is actually supposed to be, yeah that's correct. This is, and rotation's at 0.05 to 0.05 and it's animating. It's lasting 0.05 seconds, so it's just making this real tiny little shake on the bombs. The yoyo is set to true, so that tells it to continue to doing this, to rotate one way, rotate back, and repeat tells it to repeat infinitely. So, now it's making a little shaking motion. So, we want to go ahead and take a look at GreenSock. So, if we go ahead and go to GreenSock.com, then we can go ahead and download GSAP, we want to just get the CDN link. And then we'll go over to our index file and add this in. So here, again we'll want to add up at top, before our interface and game main. And save our index file, go back to gamemain.js. And now we'll be pulling in the TweenMax functionality. We'll be using TweenMax quite a bit in the next few tutorials. So, or lessons, so that's that, and then we want to go break. Case orange, so for the orange, seesprite.position.x. We're just adjusting the positioning so that it aligns properly. Break, case apple pixisprite.position.y is value of negative three. Break default, and just set our default position as value of zero. Okay so that's just aligning those graphics so they'll all look aligned now. And that's just based on our original drawings that we did, and how we need to adjust for them, a little bit, next. Okay so here will go the block.IDX gets divided, this.UIDX, so that's new our unique ID. We're telling our body to, or our sprite to contain this unique ID. So the body and the sprite are now both, will both be containing the same ID. This.bodiesandactors.push. So now we'll be pushing those sprites into the sprites and the bodies, into the bodies' and actors' array, so the block and pixi container sprite. And anchorment UIDX this.UDIXplus, gets a value of one. Okay so the next thing we'll want to change out, a little bit of functionality here in our bomb settings. So, first off we don't want these going off if our game is over. So we only tell these to go off when the game is active. Okay game subsettings, game active is true, so we want to add a bit of additional functionality here, oops. If ref.lives is greater than zero, ref.lives decrements by one element. And we're triggering our life lost functionality again. So we can update our UI, else ref.endgame. So we're losing lives as each bomb goes off, unless we've already lost all the lives we can. Then we want to go ahead and tell the whole game to end. With our, that functionality called endgame that we created a little bit earlier. Then blast radius, so that's fine. We'll want to go ahead and go here. Game active, else we want to return false. And then here, again we want to go if ref.settings.gameactive. So again, we don't want these to be rotting unless the game is over. I mean unless the game is active. And this'll be destroy bodies and actors now, instead. And then the block, since we want to be destroying both the visual pixi and the actual, physical Box2D physics object. And again, else return false. Okay so, we'll continue finishing out this, our main game JS, before we move on to our interface JS in a bit.
Reviewing the Live Game
Okay so this lesson, we'll wrap up our game main JS for our interface and sprite graphics. So first off, we're going to go ahead and switch out game active to true, and just see how we're doing, if we have any errors, if we need to fix anything. So let's take a quick look, refresh. We now have the game starting, but we have an undefined.PNG. So, we're going to check our new graphic pixi sprite. And we can see it probably has something to do with item type. So if we go up to create block, random fruit is where we're getting it. And we can see here we need to actually do our array, minus one. So let's go ahead and do minus one, save out. Switch back over, refresh, wait to see. We don't appear to be getting those errors any more. So let's see if we have any other issues that we might need to handle. Okay, we can see we're still getting stuck here, and we put in some code to fix that. So let's see if we, what's going on with that. So listener, there's our pre-solve listener, x-row, fix your B. So let's go find our x-row, row collision. So it probably has something to do with row collision. Go to our create block functionality. Oh, and here we don't actually have our row collision variable. So, and let's save that and take a look, refresh. Now our blocks aren't getting stuck any more, and things are working much more smoothly, we don't have any errors. We still don't see any of our visual sprites or anything. And that's what we will be adding in next. So we want to go ahead and scroll down to our... well there's my joint blast radius, so get body on click. So we're going to have to do a little bit of functionality changes to our get body on click functionality. So, before we move on, we want to go ahead and go in here. And add a little function in here. Make sure objects are actually touching, not just their AABBs. Because right now, we're detecting that items that, you know, they're touching each other on the edge, and we're looping through and finding all matching items. But actually it's based on their AABB, so we need to go ahead and go if edge.contact.istouching, axis line. And then we'll want to go ahead and do that here. So this will just fix it so our boxes are actually touching, and we'll go ahead and go to our debug function, just so you can see what this is doing, so if we go debug. And we're going to add one more flag here for our axis aligned bounding boxes. So Box2D.dynamics.b2debugdraw.E AABBbit. Okay, so now we switch back over here, we refresh. Now you can see we have this bounding box. So before whenever the bounding box hit was when we detected something was touching. So we just want to make sure we're detecting the actual object and not it's bounding box. So we want to go ahead and keep those flags on, but just remember that even though we're looping through and looking for touching items, if we don't tell it that it is actually touching. Otherwise, it'll go and just be reading these large bounding box edges. So, it's just something to keep in mind there. And then another thing we're going to want to add here is our triggering event. So ref, oops. Scorechangeref.score, because again, when we are double-clicking, we're destroying our bodies and adding scores. So we need to go ahead and trigger our score event, at the end there. Okay, and we'll want to scroll down to our final animate function, and this is where we'll be adding where you can see our sprites being attached to the Box2D bodies. So here, let's see after here, we'll go ahead and add that functionality. Match pixi sprite to physics bodies var in, so add this stuff. So this looking through our bodies and actors array. Okay, var body is value this.bodiesandactors I zero. So that's our body and our multi-dimensional array, and this is our sprite, our actor. Okay, actor.position.x get's a value of body.getposition.x times defaults.scale, and the same thing for the y. And again, we need to translate from the meters to the actual pixel position. Okay, and then the next thing we'll want to do is make sure that we have our destroy, destroy bodies. Okay, so take a look at this, see if we have any errors. Unexpected token, so, oh we made a typo here in our... Oh less than in, okay. So we don't have any errors found, but our actor position does not appear to being updated. Let's see, bodies and actors I zero. Body.getposition, oh, we need to do parentheses here. And now we have our actual visuals going on. So we want to make sure and watch this, and see if we notice any issues. And there are actors not being destroyed, so we need to figure out what's going on with that. So this, so we'll want to go ahead and go back to our destroy bodies and actors. And here we're getting body IDX, make sure that's correct. Getting this.UIDX. Okay so we need to change out our functionality of destroy bodies and actors. Where before we just were destroying our physics bodies, we now need to use our destroy bodies and actors functionality. Otherwise, it's only destroying the physics object. So let's find destroy bodies, so now we need to just change out that with our function. We have to destroy bodies, change that out. After we destroy bodies, change that out. And save, we can test. So let's just watch this and see if we come across any more errors. Now the physics body is being destroyed. Okay so everything appears to be functioning. Now when the physics bodies are destroyed, the sprite bodies are destroyed. So that is working correctly. So we'll continue on, building out our interface, and start taking a look at our interface.js file next.
Delving into Our Interface Functionality
So this tutorial will concentrate on finishing out gamemain.js. And get into adding our custom fonts, and start implementing functionality into interface.js. So, first of all, we'll just want to finish out game main JS. We noticed a couple of things, for example, right here we had defaults.scale. And defaults.scale, as you'll remember is adjusting based on our screen size. So that we could scale proportionally, our screen and stuff. But this get body at mouse, if we take a look at that functionality, get body at mouse here, x-point and y-point, that's actually our Box2D B2 vec point position. So, we need to think about that that's, should be based on our meter scaling which is divided by 100 instead of our stage scaling. So, we want to go ahead and switch that out to our original 100. So ref.settings.scale, so this'll be staying at our 100, instead of changing out to our default scale. Okay, and then the other thing is here, we switched destroy bodies and actors. We need to adjust our bodies, so this should be other, and this should be our clipped body. And then we can take a look and see if that's working correctly. So now if we double-click, it's now destroying properly then when we have a scaled stage. Okay, so here we now have our fonts folder. And I generated these fonts through, this is just an open source font, Later On Regular. And I generated it through fonty.floatingapps.com. It's real easy to use and it makes good quality fonts. So, you'll want to go ahead and take a look at this. Included, and when you generate web fonts, is usually a CSS file with your at font face information. So you'll want to grab that and add that to your style.CSS. Before I had amatic.sc temporarily loaded in there, but we want to add our custom fonts. So we paste that, we need to change this to assets/fonts/lateron. And then click and hold down Control to multi-select. And then paste, and switch this out to our lateron-regular. And save our style at CSS. Okay, and then go to interface.js. So the first thing we want to do is we had in here UI.gameover. So we're just going to go ahead and add that function, first off. So we don't have any errors in our code, when the game over happens. Function. Okay, and this.emitscreen, init screen function. And our init screen is where we're going to first create fruit fall game title and show play button. So var game main gets a value of newpixi.textfruitfallgame. Font 70 px, Lateron-Regular, and fill black. Okay that's just adding our Later On font, and then we want to set the position to the y-position. And then this.settings.pixiscene.addchildgamename. Okay, so now we're just adding it to the scene. And if you remember up here we have pixi scene, that's just set to null. But we switched it out over here, to our UI container. So now that's where we're adding all of our elements. So now we can, anywhere in our interface that's writing elements, we just add it to our this.settings.pixiscene adds it directly to our UI container element. So let's take a look at that and see if that's working as it's supposed to. You can see there our Later On font is not loading in properly. So let's make sure that it's loaded in. Assets/fonts/later on regular. And that's later on regular. Cursive so let's make sure we have the right CSS, there. Okay, just need to fix our at font face. There we go, there's our font that's working. So, we'll continue filling this out, but if this game was little bit more complicated, or if you were going to be doing different levels and stuff, you might want to think about separating out your interface layout from the rest of your script files. So you might for example, want to do an XML or adjacent file to pull in all your, especially like your names, because you might want to change that out to different languages. All of your text, or you might want to just go ahead and have positions and scales and stuff in an interface-specific document. It makes things a lot more organized, a lot easier to deal with down the road, as you're adding more functionality and levels and stuff. But for this game, it's a bit outside the scope of this game. So, we're just going to lay it all out in here. And it's very simple, so we can just see how, how pixi works with text, and then we'll get into the GreenSock animation platform to make sure everything's smoothly loading and moving and scaling and whatnot. So, that will be up next.
Adding Buttons and Placing UI Elements
So in this lesson will continue filling out our interface.js and adding button functionality and stuff. So, next up is we'll add our play button, so create play button. First of all, we want to get our sprite resolution function. That we created in our gamemain.js. This.playbutton get's value of newpixi.displayobjectcontainer. And add child newpixi.sprite.fromframe. Play button plus SP res plus.PNG, this.playbutton.position.x. I just center it underneath the text, the text title. And make it interactive. This.playbutton.buttonload get's a value of true. And this.playbutton.alpha. We want to start out our graphics being invisible and then we'll fade them in. So, just make sure game name and play button are both at a alpha zero. And this.settings.pixiscene addchild, this.playbutton. And next, we'll want to make them fade in. So using GreenSocks TimelineMax, we will create a timeline, basically, to fade in our title and our button. And then we'll be able to rewind that when we click play, and it needs to fade out. So, ease initial graphics in and add event listeners to start game. So this.navtimeline get's a value of new TimelineMax. This.navTM append between max.2gamename1alpha1. We want to delay when the game loads, want to delay this a second before it fades in. Then we're going to ease, use a power two. There's a power one, power two, strong, elastic, bounce, different kinds of eases, you can look it up on the GreenSock website. We're just doing a fairly, a not too strong power. Ease these in out, and then we'll also want to do this with our this.playbutton. But, the way Timeline works is that when game name is done animating, this one would animate in. We would actually want play button to animate in the same time as this starts. So we'll go ahead and do a negative two, and fade it on the alpha. And then let's add our click functionality. So this.playbutton.click get's a value function event. And then we'll want to add our, we have our reference there. So ref.begincountdown, this will add our clock countdown. For now we'll just comment that out. And ref.playbutton.interactive get's a value of false. I'm going to immediately disable the button when we click it, so you can't double-click it accidentally. And there we can do our timescale to make our timeline animation here rewind real quickly, on game start, and do a reverse. So let's take a look at that, and see if we have any issues. So now it's fading in, we have our play button here. Play button interactive in button mode. This.playbutton.click, so for some reason this is not being interactive. This.playbutton.interactive equals true. The button mode get's a value of true. I guess our buttons are not interacting. So actually should be a conflict with the debug canvas. So if we go to style, we can go ahead and remove the debug canvas for now. Now that we've got our main physics done, we're concentrating on the UI. If we remove the debug canvas, now we can click on our graphic, and we see that worked fine and went through, so this is all working. So the next thing we'll want to do is continue just fleshing this out. So, after our play button event listener, we want to go ahead and create the countdown clock. So, we'll have a bit of repetitive code coming up here. So I'm just going to go ahead and fill this out. It's just going to be a lot of positions, and anchor positions and stuff. So I'll fill that out and then quickly review it and when I come back here in a couple seconds. Okay so we filled out all the additional sprites and stuff for our init screen function. So here we have our clock, and we create a display object container, and we positioned and added to stage. And then we created the clock hand from the sprite, clock hand. And we set our anchor position, and we wanted to set the anchor position at 0.8 and 0.4. So we want it to be rotating based on the needle middle. So this worked out to work for rotating the clock hand from the middle. So then we set the position, added a stage, and then we did the same thing, added or added, we didn't add stage we added to our clock container. And we added our background to our clock container as well. And then we create a clock, then we created our replay buttons. We still need a timeline here. So then we created our replay buttons, replay button pixi to sprites. So again, we're just setting our position, setting in to button mode. And then accessing our restart game function in our main game JS. And then we're creating high score graphics, so we're creating our score button. Again setting up positioning, button mode, invisibility to false. And adding to scene in graphic tiler max. So then we added another timeline, in game timeline, for our... and we used TweenMax.alltwo to fade all our in-graphics in, which is our score button and our replay button. So now if we take a look, we have all of this set to zero when we first start our game. But if we set, for example clock to one in the alpha, and we take a look at that, you can see we have our clock there. We'll put that back to zero, you can see we have our replay button here. Oh, we need to remove our visibility there. And we have our replay button. And then we have our high score button. So we'll reset all of that, and next up we will look at creating our clock countdown functionality. And filling in the rest of our game over. And basically just flesh this out a little bit more, so we have our full game working. So that'll be up next.
Building a Countdown Timer
In this tutorial, we'll go over finishing out our countdown functionality, and our game over, so we'll have a full working game loop. So we'll begin by creating our begin countdown function here. Begin countdown function. Okay, and we'll do create countdown clock before game starts. Var clock gets a value of zero, var countdown ID gets a value of zero, var ref gets a value of this. Move the clock into view to begin countdown. So this.clocktimeline.from, from to this.clock 1.5 seconds. So it'll take 1.5 seconds to move into view. And so we'll start at outside of the left-hand side of the screen, and then we will move to, oops, should be 100. And we'll set alpha to one and x to 215, ease, we'll use a strong ease out. And we'll want to make sure end game graphics animation reverses out. So this.endgame timeline reversed. So, if we're at the end of the game and we're hitting reset, and the clock is moving in from the left to the right, the end game graphics will need to reverse out. And then we'll want to create our countdown function. If clock is less than three, that will be the countdown ticker. So clock gets a value of clock plus one, so it's incrementing by one. Or alternatively, we can just do a plus plus TweenMax.two. Go ahead and, on each tick, every second, we want our clock hand to rotate by 90 degrees. So it'll take a half second per rotation, rotation will be clock times 90. That'll make it rotate around 90 degrees each tick. And then we want to make sure it's degrees, so divided by 180, divided by math.pi. And then ease, we'll want to use our elastic ease. So this one will just have a nice elastic look. And then else stop countdown and start active game play. So we'll want to clear our interval after three seconds. And ref.settings.fruitfall, and we get our main fruit fall begin game function. Then we'll go reset clock and move out of view, TweenMax.2. We'll animate to ref.clockhand one rotation zero delay 0.25. And then ease, again we'll want to use elastic. So this is just resetting the clock hand for next time we'll want to use it. So then rotation, and then ref.clocktimeline.2. Ref.clock one alpha zero x-position 615 ease strong.ease in out. So that's just resetting our clock hand, and sliding it out to the right-hand side of the stage. Okay and then we're going to want to do countdown ID gets a five set interval. And this makes the countdown happen every second. Okay, so we can take a look at that and see if there's any errors. Unexpected, so interface 132. It's here, 132, rotation, light ease. So we want to add. Oh, we don't have a comma here. Okay take a look at that, there's no immediate errors. If we hit start, we have a from to undefined. So we want to go to clock TL, oh we need to define our clock timeline. So here, after clock, go this.clockTL get's a value of new TimelineMax. And get to our console. And the rest comes in, we have our ticker. Three, two, one, go, resets, slides out to the right, and our gameplay starts, so. All that appears to be working fine. So we're going to want to go ahead and fill out our game over functionality. Show end game graphics, play and, play again, and high score buttons. So this.scorebutton.visible get's a value of true. And this.replaybutton is visible, and then this.endgametimeline.play. So we'll want to play our end game timeline and make those graphics show up. So we'll save again, back over here, take a look. And play our whole game through and see if everything appears to be working. There's our clock, so we'll have to wait for four bombs to go off before our game over screen comes in. Just make sure everything is working correctly. Can't re-populate play, so we see here, in our interface we have endgame.TL. And that's supposed to be a capital G, so. Let's take a look at that, restart. Our clock countdown, we'll just pause and come back at the end of our game. Okay so that worked, our end game graphics faded in. If we hit reset, our countdown starts again. And our game plays without a hitch. Okay so that looks good, and then next thing we'll do is we'll be finishing out the last of our functionality to have a fully functioning game. So, we're going to go ahead and create our top scoreboard to show our lives, and look at how we tie that into our custom triggered events. So that will be up next.
Building a Score System
Okay in this lesson, we're going to be adding our scoreboard. So up at the top, we will have our lives lost and our current score. So, let's go ahead and add that function so this.initscreen. Wait, not init screen, this is supposed to be scoreboard, okay. Scoreboard function, so setup scoreboard visuals and events. Okay so first off, our sprite res, set up our high DPI functionality. Var high score icon gets a value of new pixi.sprite from image. Gain point process B resolution plus .PNG. Okay, highscoreicon.anchor.x, divide highscoreanchor.y. So just set our anchor position to the middle. Okay and then highscoreicon.position,x gets a value of 262. So just setting our positions on the y-axis, so this'll be right up at the top. This.settings.pixiscene.addchild high score icon. Okay this.highscoretext gets a value of newpixi.text. We want to start it out with zeros, we'll have zero points when we first start. For the font 50 px, use our Later On Regular font. And set our fill color, and then this will be our nice green color. This.highscoretext.position.x and y 250 and 42. And this.settings.pixiscene.addchild. And this.highscoretext. Okay so next up we'll be filling out our, will be a bit of repetitive code here. So, we'll be back in a second when we've filled out our lives text and add it to the scene. Okay so here we just went ahead and added our lives icon. Which is that little red icon, and then we added our lives text. Just like we did for our high score text. And then, but we had to separate this out where our lives text used. 'Cause we want to say zero out of, for example, three lives, have been used. And then we'll want to change that to be two out of three, et cetera. So we changed this out to several bits of text. So we have our lives text used zero, we have our lives text of was zero dash three, so zero of three. And then we have our separator between the two elements. So we can go ahead and take a look at our Pixi scene. And see if that's working, uncaught type error, unexpected string. So let's go here to high score text. High score text zero, some new Pixi text. We need to add our colon there. Okay now we can see we added our icons at the top and we added our text fields. So the next thing we're going to want to do is add our event listeners. So that our UI updates as we lose lives and gain points. So first of all, we want to get a reference to this. On life lost change our lives' visuals. So we'll create our function, switch copy lives. And then here, this.element. And here's where we're going to be getting our life lost. Where we, in our game main JS, where we were triggering life lost and score change from our this.element, which is our main game element. We'll now be wanting to pick that up here. So, on life lost function E data, in var lives, oops. Get's a value of... we wanted the data will come in as a string variable, so we want to go ahead and parse that as a number. So parse int data, so that's lives lost. Looking here we're getting this.lives, so that's pulling it in here. So, we're going to want to actually center, if we look at our layout, we want to center our text field. So, based on the width, so we're going to go ahead and create that function. So .centertext 265. Wait, we don't need to do this actually for life lost. No, 'cause this will only be one, we'll need a center text for the score points, but not for lives lost 'cause it'll never be longer than one character long. So we don't need to worry about that for now. Let's go ahead and do lives and then TweenMax.2livesicon.scale. So this'll be every quarter of a second, going to use the bezier function, values. And the y zero x scale zero. And then we'll want to go to y one and x one. Okay so what that will do is it's just going to animate the lives icon scale. So it'll just, on each change, this is just going to bob up and down. So it'll go to zero scale and pop back up It just adds a little bit of nice functionality to that. So we'll take a look at that in a second. And then we'll wan to do TweenMax.2ref.livestext. Again, 0.25 alpha zero. So we're going to fade out the lives text as, when the score changes. So on complete, after it's faded out, we're going to change out the copy, and then we'll fade it back up. And we want to add our parameters to that function. 'Cause here we want to get our lives information, our new lives total. So here in our switch copy we're going to go ahead and set ref.livestext, lives text used outset text, three minus lives. Ideally we should set a global lives total, since we'll have this number several times throughout the game. But when it, we ought to have this, eventually we'll put this as a top-level global. But for now we're just going to leave that to three. And ref.livestextremaining.settext. So now we change out the visual of our text. So let's take a look at that. If we play our game, wait for our countdown, go to our console. So wait till this explodes, and watch our, so it disappeared. And we lost our life, but it didn't come back. So we have... we need to make sure that we set that to TweenMax.2, ref.livestext 0.25. And we'll make it fade back in, and we'll give that a look. So we start, countdown. Okay wait for our first bomb to come in, and keep an eye on our lives. And there we go, as we can see the text is actually offset a little bit with this font. Depending on the font, it moves around. So we do want to create our center functionality. So we'll go ahead and do that here. So, ref.centertext 342 ref.livestextused. And we're going to go ahead and do lives remaining. And that will be 382, since it's a little bit farther to the right. So now it'll center it based on these points. So, we'll want to go ahead and create that function down here. So this'll just be a quick centering function we'll use several times throughout our interface. So center text function x mid TXT, comma separated. So var new x get's a value of x mid minus TXT.width, divided by two. And TXT.position.x gets a value of new x. And we can take a look at that. You should have a nicer layout now, so if we play, wait for our countdown, and wait for our first bomb. Okay if we keep an eye on our lives here. And now, this is much nicer, it's keeping all the text nice and centered. Okay so we'll continue fleshing out the last bit of this. We'll add our score functionality up next.
Building a Lives Lost System
Okay in this lesson, we'll just pick up where we left off. We just created our life lost interface functionality. So we need to do the same thing, pretty much, for our scoreboard. So first of all, start with on score change, event update score visuals. Okay so function switch score copy score. So this is really similar to our previous function, but now we're just updating our score text. Set text to the our score ref.centertext 265, it's a little bit more to the left-hand side. High score text then TweenMax.2ref highscoretext. This is just fading it back up, after the copy is switched out. Then get our main element in, wait for our score change event. It's a E data, score get's value of parse int and TweenMax.2highscoreicon.scale and quarter of a second. We'll go ahead and just copy our bezier, paste. And we'll go ahead and copy this functionality, as well. And change out this to high score text. Okay, let's take a look at that. Refresh, if we start a game, wait for our timer, and see if our score, well our score's not switching out. So we need to take a look at that. On complete, oh we need to switch this to switch score copy. Control K B, get rid of our side panel, and change that out to score. And save, and test, restart. Wait for our timer. No, our text is still not changing out. If we switch to on score change. We're changing out high score text alpha to zero and alpha back to one. For some reason, we're not picking up the score. High score text, set text. So let me double-check our game main functionality. Let's run a console log here, ref.score. And see if we're running into any problems in our double-click function. Okay, we can see our score's actually not updating. So, I'm going to go ahead and figure out where that's happening. Okay so we'll want to go ahead and add a bit of functionality here. So that when we've selected our icons, we're going to want to add to our score. So ref.score plus plus, now our score should be updating properly. Wait for our countdown. So if we have two touching items, now our score increments. We actually want to go ahead and add one score value. Just when we click one body, too. So if we refresh, start our game, wait for our countdown. And now we're always updating, even if it's just one. Okay. So we can see we're having a bit of an issue here, where our bomb isn't going off if he's sitting in the corner. So we need to figure out where that problem is coming from, so if we get blast radius. Oh, we have our, we're only returning true when we aren't touching a static... a static body isn't within our blast radius. So we need to make sure that we always return true, and we keep looping through our function. Or else, if it's sitting here, where it's touching the wall and the floor, then we run into that problem of our function thinking that the blast radius, it didn't actually hit. So, we just want to put that there, and take a look at this. Start. So we should be good to go here, and then next thing we're going to look at is starting to implement a bit more animation flourishes and things to, sort of, make this a little bit more exciting. As well as adding sound effects, so that'll be up next.
Implementing Movieclip Animation
So in this lesson, we'll discuss adding Pixi js animation. And how to extend our main Pixi movie clip. So, let's see here, we'll first off we'll want to go ahead and add animation function. So we'll want item type the container and the body. Movie clip offset fixes, so first off we'll start with our offset fixes, just like we did with our static sprites. Again, this is something, ideally you would have a separate XML adjacent file, and you would store all of your clip offsets and stuff. But, we're just going to do another switch case. So this will be var x pause offset gets five zero. Switch item type, so for our bomb movie clip. Okay, and then title length of 31 x pause offset get's a value of negative seven. So this is the title, how many frames our explosion has, and this is the name of our PNG sprite sheet graphic. Okay, and we'll want to do a break. And our next case, or we'll just do a default. And this is for all of our rot animations, we only have explosion and rot animations for our movie clips. So we just have the two different possibilities. So this'll be var sprite any get's a value of item type plus dash rot. So this'll make an individual rot, sprite animations for each different type of fruit. And, total, there's nine frames in each rot animation. And this we can go ahead and be at zero, 'cause they're perfectly aligned to their sprite, their static sprite positions. Okay, use our explosion textures for I get's a value of zero, if I is less than or equal to the total frames idle increment. So first of all we'll fix naming convention zero zero. And this is just how we happen to export our frames, so we have to deal with, make this little fix in our naming convention. As you can see we start with zeros. We have four characters, so if we have 10, now we only have two zeros, if we have 100, then we only have one zero, so we need to account for that here. So this'll be zeros gets a value of 1234.substring, start at character position zero, and then we want to base this on total characters in the I. And now that'll be handled correctly. So var texture is five new, oops, spot, just pixi.texture.fromframe sprite ani. Plus this is going to handle our high-res animations, plus deal with our naming convention there, plus I, plus .PNG. Okay, so there's our texture and then explosiontextures.pushtexture. We're pushing that into our explosion textures array. So we're looping through and getting all of our textures. And adding to our array, this should actually be, since it's explosion and rot, so we'll just do animation textures. That makes a little bit more sense. Okay, create an animated bomb/rot movie clip, based on our extended movie clip functionality, oops, functionality. So var animation, so I have new Pixi.fruitMC. So normally this would be Pixi.movie clip, but we're going to extend our movie clip functionality over in our extend Pixi js. And we'll get to that in a bit. So explosion textures, where this is now animation textures. Then animation.anchorx, so we just want to set our anchor position to the middle. So that our animation sprites will line up with our static sprites. So animation.position.x gets a value of x. So this is just the x position offset to make sure everything is aligned correctly. Animation.position.y3, again just making sure all of our animations are aligned with our original static sprites. And animation.loop gets a value of false. Animation.gotoandstop zero, again using all the Pixi js movie clip functionality. Animation.visible gets a value of false. The container.addchildanimation, so we're adding that to our Pixi js. Sprite has a container where we can put any of our additional animations. And then just swap them out with the visibility function. So now here's our extended functionality to the movie clip. So this animation container gets a value of the container, oops. This should be animation, and then animation.default. Sprite gets a value of the container.getchild at zero, and animation.physicsbody get's a value of the body. And then we're going to want to return our animation movie clip. Okay so we'll go ahead and switch over to our extend Pixi js and take a look at what that would be. So, extend Pixi.rootVclip with fruit, oops fruitMC. Handles animation and removal of the Box2D based fruit bodies and attached sprites. Okay, because our auto ticker for all of our animations are happening in our fruit, basically our explosion or the rot, the three different rots. Every time that happens, at the end of it, the physics body needs to be removed and the sprite body needs to be removed. So we need to make some functionality to detect when the movie clip is done playing, and then remove the whole fruit object. So that's what this movie clip extension's going to do. So, we'll start with Pixi.fruitMC.constructor gets a value of Pixi.fruitMC. And Pixi.fruitMC.prototype gets a value of object. Create Pixi.movieclip.prototype. Go Pixi., and I'm sticking to the PIxi name space, so I'm doing this as Pixi.fruitMC. Gets a value of function textures. Pixi.movieclip, call this one textures. And set up some additional variables for the animation container, our default static image, and the attached physics body. Okay, so this.container, this.defaultsprite, this.physicsbody. So Pixi.fruitMC.prototype.playandkill gets a value of function element and type. Var ref gets a value of this, so switch out movie clip visibility and start playing the animation. So this.default, is our static sprite, needs to go invisible, and switches out so that this, the movie clip of the rotting or explosion movie clip is now going to become visible and replace our original static sprite. And then this.gotoandplay, we'll want to start our play functionality. And then trigger a kill event to remove both B2D body and fruit on animation over. So, our detect frame end gets a value of set interval function. So if current frame is equal to total frames minus one, want to go ahead and trigger our custom kill fruits event. And then set type to our type, body to our ref.physicsbody. So type will be whether it's an explosion or a rot event. And then clear interval detect frame end and set to null. So here's our set interval, we're going to want to go ahead and set it to 100 hertz, I mean 60 hertz, and save. And then we're going to want to go to our index.HTML. And make sure you set that after our Pixi library, since it's extending it. We'll want to change this to extend-Pixi. So let's take a look at our game, see if we have any errors. So, okay no errors are coming up. So, next up we'll be finishing out this and we'll start adding our sound effects.
Consolidating the Game Code
Okay in this lesson we'll continue finishing out our animation. So first off, we'll load up our sprite sheets. So, let's copy and paste that. And we'll start with our explosions sprite sheet. And then use our apple, orange, peach rotting sprite sheet, and last, we're going to do our drips and swirlies. And we'll get into this in a bit, and we're not loading in high-res texture, 'cause these are going to be quick, simple little sketches. So we won't really need a high-res version of them. So now we're going to go ahead and load the rest. And this is all low-res, and the same for drips and swirlies. And we can go, so we want to go down to our lock section, and here's where we're going to start adding in the rest of our animation functionality. So, var animated sprite gets a value of this.addanimation. Item type Pixi container sprite, and the physics block. Okay, and then we'll need to change out this functionality here. So we'll want to go ahead and clear out all of this. So we'll be handling it in a custom function now. So animatedsprite.playandkill, ref.element and then bomb blast. So, that's in our extended Pixi function. Oh, we can see we made a typo here, we're going to want to go ahead and fix that. So, frames negative one, okay. So any way, this is the type, and the physics bodies so we have the element. And the type, the element is just our main game element, and type is the type of, whether it's explosion or whatever. So, here is the bomb blast, and then down here in our game active, switching out this with fruit rot. Okay so, now we'll need to add our custom function. So up here after our event listeners, we'll handle custom kill event trigger. So, ref.element, again we're getting our main game element. And getting our kill fruits event and function E and data. So, we'll just do a switch case. So data.type case bomb blast. If bomb blast trigger, life lost and destroy nearby blocks. So if ref.lives is greater than zero, ref.lives decrements. Ref.updatescoreboard life change, ref.lives. So now we're updating our scoreboard. Else, ref.endgame. So when we've lost all of our lives, we'll want to go ahead and end game. And then, we want to go ahead and do our ref.blastradius data.body.getposition.x. So we're just adding in our blast radius that we had in our other function. And then break, and default for all of our fruit rots. Ref.destroybodiesandactors, data.body. And let's make sure we're going to have an additional bracket there. Okay let's save, and give that a test. Ref is not defined, so game main JS, we need to go ahead and define our reference to this. And refresh, get back to our console, start our game. Our clock ticker, and see if our functionality is working for animations. Our bomb was triggered and our animations happened, but we're having a bit of an error here. So update scoreboard, that's line 283. Okay so first of all we want to fix this, it's supposed to be underscore body. So extend the ePixi js so we can see it's underscore body. And we're going to want to go ahead and add our update scoreboard function. So, go ahead and add that functionality here. Function comma, okay so just update scores/lives, and trigger event will be handling these events, and the interface.js script. Okay so switch, we want to do type and this is switch. So case score change this.score get's a value of nom. This.element.trigger score change this.score, and else case life change this.lives get's value of nom. This.element.trigger life change this.lives. And handle any defaults, okay. So this just consolidates our score changing a little bit. So, we'll want to go ahead and switch out any of this functionality now. So we go ahead and go this.updatescoreboard. And now we can clear out this, or zero and three. So now we can go ahead and go like this, zero and three, and this'll just consolidate stuff a bit. Okay and then get our life lost. We're switching this out life change. Since that won't necessarily always be lives lost, we just want to change that naming convention to life change. And switch out any functionality. And life change, getting our new lives. So let's save that and take a look. Start, wait for our timer. Okay our explosions or animations are working, and our rotting animations are working. Okay however our... we need to update our interface. So interface.js, and now we need to change our lives lost to life change. And refresh, start, wait for our countdown. So our lives lost are working, however score change has an error. So, game main JS, this is to update scoreboard. So want to go ahead and go to 596, Ctrl-G 596. Game main, Ctrl-G 596, so we want to use ref. Get reference to this, okay, wait for our countdown, go back to our console. We're still having an issue with our point system. We need to go ahead and get our correct ref.score. And start, wait for our countdown. And now our points are updating and our animations are happening. So the next thing we'll want to do is take a look at howler.js to add a bit of sound effects. And then we will start adding some additional animation flourishes.
Implement Sound Effects
Accounting for Unique Dprs and Adding Animation Flourishes
Okay so now we'll start adding in our additional animation flourishes, but first we need to account for different DPRs. You can see all of our positions and stuff are just static 262, 23, et cetera. We're just, you know, figuring out where things need to be visually. But if we change our DPR, everything will be offset wrong. So we're going to have to go through here, and create a DPR variable. So, DPRs so this.settings.fruitfall.settings.DPR. So, that gets our main game JS DPR that we're using in our other script. So we'll want to go ahead and do that, and then just multiply by our DPR. So, we need to go through and all our positions and just add that. So, I will place that in here, and when I'm back, we can start looking at adding flourishes. Okay so we filled in all of our DPR positions. So, we just need to make sure that we go through everything and add our DPR offset. Also, real quick, we just went through and set our interactivity to false. We need to make sure that whenever you aren't using a button that it's either set to interactive gets a value of false, or the visibility is set to false. Cause just setting the alpha to zero is not necessarily going to make the buttons interactive, so they might, you might not see them, but they'll be in the, you know, overlaid on your game, where you can still click them. So you just need to make sure that we... after each button press, that we set the buttons to false, or true, based on our game state. So, that's that. So next up we'll work on these swirls, so we'll start with our DPR. Okay and do our swirls and drips for random creation. So, we just have an array of swirls and drips. And, so we just, this is our, where we imported here, swirls and drips. Here we imported our drips and swirlies, and so we just had to write down an array. So we know which ones to get, and how many frames each one has. So, we have swirls that kind of rise up and swirl away. And we have drips that will drop off. So, we need to just keep our multi-dimensional array here with our names and our frames. So, for swirls gets a value of array var rand swirl. So we'll get random drips and swirls, based on the length of our array. Drips anchor from on top and drop down. Swirls anchor from bottom and fly up. Okay so our drips are starting from, the anchor needs to be at the top so they drop down. And the swirls will be swirling off the blocks, and they need to start from the bottom and fly up. So, need to adjust our anchor position accordingly. So default to one if swirls rand swirl. So, we get the name index of swirlie. So this way we can just detect if it's for a swirl and anchor from the bottom. So I have a zero, okay. So var frame total gets a value of swirls rand swirl one. So that's getting our random swirl and getting the total frames. Var the swirl gets a value of swirls rand swirl, and get our swirl sprite name. And we can go ahead and just pull our movie clip texture from here. Just go ahead and pull our functionality, since it's pretty similar. This'll be swirl textures, and then we won't be using sprite res, because, as we mentioned over here, we aren't actually loading a high-res version of our drips and swirlies. So when I go ahead and switch this out to the swirl. So var animation gets a value of new Pixi.movieclip. Swirl textures animation.anchor.x, gets a value of 0.5. And then we handle our anchor on the y, based on whether it's a drip or swirl. Animation.position.x gets a value of x times DPR. Y, again, so animation.scale.x gets a value of animationscale.y. So we want to just randomize these a little bit more by adding a random scale. So animation, we don't need these to loop, they just need to happen once and then evaporate. So set no loop, animation.gotoandplay. And add to our Pixi scene, and then keep checking until clip is done and playing and then remove from scene. It's done playing. Okay so var remove drip on complete, gets a value of set interval function. So if animation is not playing, so when the animation is over, we're going to go ahead and do animation.parent.removechild. So, like we said, we want this to swirl up, stop playing, and be removed. And then we'll go ahead and clear our interval. And clear out our variable. And, again, we'll check this every 16th of a second. Okay so let's go ahead and add this to one of our button clicks to test. So on our first play button, let's go ahead and do ref.randomswirlsanddrips. And then we'll need to go ahead and get our actual mouse position. So, go ahead here var parent cords position gets a value of mousedata.getlocalposition.x, right here. Want to just get local position based on the Pixi scene object. And then we'll want to set this for the x and the y. And we can test that. Okay, we have a couple of errors here, so in 298, we have two pluses there, so it's just a typo. Ctrl-G 298, and get rid of that. And we have another error here, DTLs not defined. Oh, we didn't do our total. So that's not correct, we need to get frame total now. And refresh, and we had a swirly that comes off of there. So if we refresh that again, we can now see that we have totally random drips and swirls. So, want to go ahead and add that functionality. So, in the next lesson, we'll go ahead and finish out our drips and swirls animations. And flesh out our gameplay a little bit more.
Animating the Random Drips and Swirls Flourishing
Alright, in this lesson we'll finish out our random drips and swirls flourishing. We can go ahead and add this, copy through to the next click. So in our replay button, we also want to add our random drips and swirls. And then, okay there we have it. So we'll save our interface.js, we'll go to game main. So in here, we're going to want to add swirls to each hit. So when the objects hit the ground, we'll want a little, sort of, flourishes and stuff to fly off. It'll add a lot more interest here, so. We'll go ahead and fill that out. So, ref.UI.randomswirlsanddrips. So contact.getfixtureA.getbody.getposition.x times default scale. And then we'll want to do the same thing for the y. Defaults as scale, okay so let's test that. Load up our game, go to our console. These swirls work, but we want to add swirls to our clock. So, we can see we have our little swirls and splashes and stuff whenever our objects hit. Okay so we want to go ahead and add that to our clock, as well. So each time the clock ticks, we'll have our random drips and swirls. So let's go ahead and copy this, and paste. We don't need that, so here we'll just need to go ahead and get the clock hand position. So ref.clock.position.x, and the y. And we can go ahead and just do two sets of swirls and drips for each clock tick. And save, refresh, hit start, and now as this ticks, we have a nice little splashes happening. Okay so that pretty much wraps up the interface for now. So next up, we'll be looking at the high score functionality, and putting that into our interface. And loading up our PHP functionality and everything, and how we deal with pulling in our PHP information, our PHP scripts, and implementing into our actual game. So that will be up next.
Building a Highscore System
Okay in this tutorial we'll start building out our high score functionality. So first off, we'll look at this lesson folder structure. We have a new folder here called lang, and this file, a PHP file sensor.function. And this file, example.PHP, and all that is from the Banbuilder. This is just to censor user names, and there's various ways to do this. And it's almost impossible to censor all possible bad words, but Banbuilder does a good job. And it's for multiple languages, and it's very easy to implement. So, that's what we're using here. And the example.PHP is just an example of how you would use that. So we can go ahead and make use of that. So, we're going to want to go ahead and fill our tour DBcon.PHP and our displayhighscores.PHP. So first of all, our DB con is just to connect to our database, our SQL database. So, start by setting that up. So we want to put in our database information. Database is five my SQL connect to local host. And I just have a root user, since this is my local host. So I don't need a password or anything. Or die, psor die, could not connect .mySQLerror. So, we're just giving our SQL error message if we can't connect. My SQL select DB high scores or die could not select database. And close PHP, so we just connect to our database, and fill in our user name and our password. And load up our error message if nothing, if that doesn't work. And then we select our database, which is high scores or die could not select database. So, we'll want to go ahead and take a look at phpMyAdmin. If you're running XAMPP, you should have phpMyAdmin automatically installed. And you're going to want to go ahead and create a high scores database. So, in here we have the scores table. If we take a look at scores table, we can see the layout here. We have an ID, we have our user, player name, we have the score to go with the name, and then we have the game. And, this is kind of a good practice. So if you ever have multiple games or whatever, you might want to sort them out, and have them separated out by game name, so. Here we have our ID as an integer, we have it set to 100 characters. It's auto incrementing, and that's our primary index key. Then we have our name, our player name, it's a var chart, so they can enter numbers or characters up to eight. And we're defaulting to anon if they don't enter their name. And then we have our score, that's an integer, it's up to 10 characters. And we have our game name, it's a var chart, so numbers or characters and up to 100 characters. So, that's the basic structure of our table. And we just want to go ahead and connect into that. And then here to display high scores, we'll have to go ahead and fill this out. So, include our DBcon.PHP. And create our D game, so it would be the fruit fall. And then page gets a value of... we actually aren't going to implement pagination in this game. But we'll go ahead and just show what that functionality might, kind of, look like. Okay we want to go ahead and use my SQL real escape string, because this is using a get, and we don't want to allow users to just enter any data, and delete our database, so. We need to make sure, if we're ever using get, that we're doing my SQL real escape string. And then we want to do our query. Is equal to the game order by score descending limit page 10. Okay so that's just saying select all columns from scores, from the scores table, where the game name is equal to fruit fall. And order by score descending and limit to 10. So from page, we'll have it set to from page zero to, or whatever, entry zero to 10. And then we'll want to go ahead and do a result. My SQL query, and get the query or die. And we do query failed, and add our query error. And we get the number results, result. And our score array get's a value of array for dollar sign I gets a value of zero, where I is less than number of results. So we just want to loop through all of our, all 10 results. So row get's a value of my SQL fetch array far sign result. And name get's a value of row name and score array get's a value of array, name sub-stream name zero 11. Score dollar sign row score, and close. Okay, echo JSON encode dollar sign score array, and end PHP. Okay, so we're JSON encoding our score array, and sending that into our interface.js. And then we'll be handling a list of names and scores. So, we're just pulling in 10 results, looping through, wait We want to go ahead and do a dollar sign I here. Anyway looping through our results, and adding to our score array. So we'll go ahead and test our script here. So display high scores, so we'll want to go to localhost/dynamicgame/lesson, see what was our naming convention? Okay, and then we'll wanna go display high scores. And then we'll do the page, so we'll wanna do pagination gets a value of zero. Oops, equals zero. Undefined variable, non-results on line 14. Is less than results. We're getting the result, which is getting the query. We get select start from scores where game is equal to the game. Order by score sending limit page, okay so we have a typo here, non-results. Save, and check. Okay now we're loading in, and everything appears to be working well. So, in our next lesson we'll continue filling out our PHP documents, and then implementing in interface.js.
Creating a Highscore Submit PHP Script
Laying out a Simple Highscore UI
Okay so in this lesson, we'll start implementing our high score functionality. So in our init screen, we'll go ahead and do this.enterhighscore. And we'll need to create a new function. Enter high score function for our key limit, has a value of eight. Total keys, just want to, need to set up a couple of variables here. Var ref so this, var sprite resolution gets a value of thissettings.rootfile.SPres envir DPR gets a value of this.settings.fruitfile.settings.DPR. Okay, and then we'll be doing functionality that we've already gone over here of adding sprites and adding click events and stuff. So I'll just fill that in and be back in a second and go over functionality and layout. Okay, so here we just went ahead and added all of our new sprite items. So we have first of all, our high score list. There'll be a container for all 10 of our high scores. So we're just creating a display object, we're positioning it, and using our DPR. This, and then adding to our stage. And then a container for all high score submission graphics, so we'll need a prompter and a submit button. So we need a container for those. So here we create our name prompter. And we're using TweenMax2 for the name prompter. And every 0.35 seconds, we fade the alpha to 0.2. We're using a power one ease in and out, and doing yoyo and repeating. So that makes a nice little blinking name prompter. And we add it to our score submission container. And then we create our submit button and place it in our score container. And we don't want it to be interactive until the submit screen is loaded. So the submit button, when you click it, or button.click, or button.touchstart, if you're... it's a good idea to, on all of your .clicks, to also add a .touchstart, so that it works with touch events as well. So again, we're just doing the same as we had earlier. We're getting our parent position, and then we're playing our sound effect for our button click, and then we're using a TweenMax2 to do a little bit of a jitter as we click. And then we're adding our random swirls and drips to the click, so our button looks nice and interactive. And then we set the interactive defaults as soon as we click, so that we can't accidentally press twice. And then we'll want to add a function to submit score. So if we do submit score function, will be our score submit function. And then here, we'll want to do our player name text field for keyboard input. This.playername gets a value of new Pixi.text. We'll start it out as being blank, and then we'll get font. We wanna do 40 times DPR, again we have to adjust our font size based on DPR. Plus px LaterOn-Regular. And then fill number 42914E, and that'll make a nice green color. This.playername.text gets a value of null. Actually, we won't need to add that since we already have it set there. So this.scoresubmission.addchild. This.playername, add that to our score submission container. We want to set up keyboard prompter position and keyboard key down events. So this.totalkeys gets a value of. And this.playername.settext, this.totalkeys. And this.playername.position.x gets a value of 100 times DPR, minus this.playername.width divided by two. And TweenMax.two, this.name. Nameprompter.position zero x. This.playername.position.x. Plus this.playername.width. Okay, so that's just positioning the name prompter based on the text position. 'Cause it always needs to go, travel to the end of the text as you're typing. So, document.onkeydown, this.updatereadout.bindthis. Okay, we will want to do our update read out function. So now every time you press a key down, we'll go ahead and call our update read out. So, that's how we'll be updating our text. Okay, so let's go ahead and test our script here. Ctrl-S. And got type error, cannot read property parent of undefined. So, that's in our enter high score. Oh, this needs to be player, capital N, name. And now our error has been removed. Okay, so we'll continue building this out. In our next lesson, we will be creating the update read out, so we can see as we're typing in our score.
Handling Keyboard Input
Okay so in this tutorial, we'll be going over our update read out function, where we'll be taking in our keyboard input and visually implementing it into the Pixi js renderer. So, we'll start out with some variables up top. So our rev string, in spite of this.playername.text. Var keys string, var key limit, gets a value of eight. We want to limit to eight characters. And our DPR. Var keys gets a value of keyboardJS.activekeys. So if we've pressed a key, we'll go ahead and run our functionality. Get the pressed key string, convert to string from array. Keys string gets a value of keys.join. So, for this we're not allowing any spaces in our player's name. So if keysstring.index of space less than zero. So if there's no spaces or backspaces, we'll also need to handle our backspace separately. So, this is just for the main text entry. So if this.totalkeys.length is less than or equal to key limit, our limit of eight characters, convert key string back into an array to handle last key press. Make sure we are getting the actual character, for example, shift array might look like shift S, and capital S. If you were to try to get a capital S, our array would show a shift and a small S and a capital S. So, we want to get just the capital S, we get a capital S. So var R gets a value of keysstring.split. Split our array, so if R length minus one, .length is less than or equal to one, this.totalkeys plus gets a value of R.length Minus one. Supposed to be less than or equal to one, clear that. And here, remove last character on backspace press. So we wanna handle a backspace. So else if keysstring.index of backspace is greater than equal to zero. This.totalkeys gets a value of this.totalkeys.substring Zero this.totalkeys.length minus one. So our total keys is now getting our total key sub string, minus one. Okay, now handle our backspace, so. Else keys string gets a value of no key pressed. Okay, so change data and run effects only if there was a visual change. Don't need this on control, shift, et cetera keys. Okay so only if there's an actual visual change do we need to run in this functionality. So if string rev string is not equal to this.totalkeys. So we get our previous string array and see if it's the same, or not, as our new string array after a key is pressed. So this.settings.fruitfall.soundeffects, and we wanna do our typewriter sound effect. And this.playername.settext, this.totalkeys, and center text position based on width, this.playername.position.x gets a value of 100 times DPR, so we're positioning our player name. Minus this.playername.width, divided by two. So, we'll be centering it on the screen as we're typing. Then adjust flashing text prompt to end of text. So we can go ahead and just grab this, and place it down here. We want this to be pretty fast, so it zooms to the end of the text. And this.randomswirlsanddrips, this.nameprompter.position.x plus this.scoregetspermission.position.x times DPR and position y, position y. Okay, so now the random swirls and drips will happen as we're typing. Just to add a little bit more interest. So let's go ahead and test that. So if we go over to our game. Start game, wait for our clock ticker. And then we'll wait for our game over. Okay so there's our game over. Oh, we need to load up our, just score. So, on our... when we press our high score submission button, submit button, play button, so let's find our score button. Score button. Then find our score. So, we need to go ahead and add this functionality. This.scorebutton.click gets a value of this.scorebutton.touchstart gets a value of function mouse data. And then we'll want to graph.scoresubmission.alpha gets a value of one. Graph.scoresubmission.visible gets a value of true. And ref.submitbutton, should now be interactive. Okay and let's test that, refresh our game. Okay game over, so now here's our high score submission. So if we test our keyboard input, uncut type error, object is not a function. So, this.settings, so 437. Ctrl-G 437, oh, we have our sound effects. This isn't right, we need to do a .play. And a .play. And, let's test that again and restart our game. Okay and our game over, submit test. And there's our test, it's working fine. So then hit submit. And submit is disabled. Ok, so all that works well, so we'll continue on with our next part, which will be creating the high score board, and actually submitting our score. So that will be up next.
Using Ajax to Submit and View Scores
Okay in this lesson, we will continue building out our high score functionality. So we'll start with our submit high score function. This connects to a server-side PHP script that will add the name and score to a my SQL database. So, and just supply it with a string representing the player's name, score, and a secret key. So var name, gets a value of this.playername.text. Var score, gets a value of this.highscoretext.text. Var secret key, gets a value of fruit fall 198324GME. Let's make sure that's correct by checking our high score submit. And just copy from here, 'cause those have to match. Okay, now var hash, using our crypto JS MD five, and hash our name, score, and secret. Okay. So our hash and then go ahead and get a reference with this. And do our jQuery AJAX function to get our URL, should be highscoresubmit.PHP, and our variables. Name, gets a value of encode your I component. Name plus score plus score. And our hash, oops, this is supposed to be and. Okay and we'll wanna do type is get. So, done function E. So, was successful. And we'll go ahead and do fail function. Handle any errors here. And always put function E. And here we'll run our show scores function. So, ref.showscores. And here we'll create show scores function. Okay so we'll start our var DPR gets a value this.settings.fruitfall.settings.DPR. And get our reference to this again. Hide submit graphics and show top 10 scores. So this.scoresubmission, we don't want to show our prompter or anything anymore, so it's set to false. And then clear out children from previous score checks. So, if we've checked the score before, before we loaded in our new ones, we wanna make sure we have all of our sprite containers cleared out. So while this.iscorelist.children.length is greater than zero, var child gets a value of this.highscorelist.getchild at zero, this.highscorelist.removechild. Okay wanna do TweenMax2 this.highscorelist in a half second. Alpha needs to go to one, and fade in our new high score. So dollar sign, again we'll do our AJAX function. Get our URL, display high scores PHP, and we'll do our pagination. We're just setting it to be zero. Since we're just doing our 10 scores, and we aren't doing any paging graphics. And type will be get, again. And data type is going to be JSON. And then we'll do our done function, get our data, oops. And then loop through JSON array sent from display highscores.PHP and display into rows. So var our two columns, high score, name. Okay we'll get... set up a Pixi text to take in our JSON data, and convert to a visual Pixi text item. So, item.name plus item, wait we need to do our function. Oh, we need to go ahead and do our each function. So, var sign.each, and the data will loop through our JSON data here. And then item.score, we'll go right into our font. 20 px, depending on DPR plus px Later On Regular. And then our fill of just a solid black. So equals ref.highscore this.addchild high score name and position across two rows using the modulus two. Move over every other row, so we just want to make two columns here. So highscorename.position.x gets a value of our I modulus two, times 150, and then times DPR. So, if I modulus two is equal to zero. Else, so highscorename.position.y gets a value of I, minus one, times 10, plus 20, times DPR. And, otherwise we just want to do I times 10 times DPR. Okay so we make our two columns of text. So let's go ahead and take a look at how this is working. And start our game. Okay game over. So let's try submitting our high score. Hit submit, you can see we have an error here. Item score is not defined, so we made a typo there at line 502. So if we go to 502. Item, okay so let's return to our script. This should be item.score, and save. And reload our game, okay and game over. Let's check our high score, test, submit. And our scores are loading in, sorted by highest to lowest. So, that's all working. If we do a reset, our game restarts, but our scoring isn't clearing out. So we need to handle our additional functionality, when we do our game over. So when we come back, we'll be finishing up our game, and going over our, double-checking to see what we can do to optimize things a bit with the Chrome Tools.
Finalizing Development and Optimizing in Chrome
Japhia Olson has worked over 10 years in the creative design industry and operates Mint Design Company LLC where she specializes in highly interactive and award winning web/game development,...
Released26 Dec 2014