Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon

How-To Tutorials - 2D Game Development

64 Articles
article-image-building-untangle-game-canvas-and-drawing-api
Packt
06 Jul 2015
25 min read
Save for later

Building the Untangle Game with Canvas and the Drawing API

Packt
06 Jul 2015
25 min read
In this article by Makzan, the author of HTML5 Game Development by Example: Beginner's Guide - Second Edition has discussed the new highlighted feature in HTML5—the canvas element. We can treat it as a dynamic area where we can draw graphics and shapes with scripts. (For more resources related to this topic, see here.) Images in websites have been static for years. There are animated GIFs, but they cannot interact with visitors. Canvas is dynamic. We draw and modify the context in the Canvas, dynamically through the JavaScript drawing API. We can also add interaction to the Canvas and thus make games. In this article, we will focus on using new HTML5 features to create games. Also, we will take a look at a core feature, Canvas, and some basic drawing techniques. We will cover the following topics: Introducing the HTML5 canvas element Drawing a circle in Canvas Drawing lines in the canvas element Interacting with drawn objects in Canvas with mouse events The Untangle puzzle game is a game where players are given circles with some lines connecting them. The lines may intersect the others and the players need to drag the circles so that no line intersects anymore. The following screenshot previews the game that we are going to achieve through this article: You can also try the game at the following URL: http://makzan.net/html5-games/untangle-wip-dragging/ So let's start making our Canvas game from scratch. Drawing a circle in the Canvas Let's start our drawing in the Canvas from the basic shape—circle. Time for action – drawing color circles in the Canvas First, let's set up the new environment for the example. That is, an HTML file that will contain the canvas element, a jQuery library to help us in JavaScript, a JavaScript file containing the actual drawing logic, and a style sheet: index.html js/ js/jquery-2.1.3.js js/untangle.js js/untangle.drawing.js js/untangle.data.js js/untangle.input.js css/ css/untangle.css images/ Put the following HTML code into the index.html file. It is a basic HTML document containing the canvas element: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Drawing Circles in Canvas</title> <link rel="stylesheet" href="css/untangle.css"> </head> <body> <header>    <h1>Drawing in Canvas</h1> </header> <canvas id="game" width="768" height="400"> This is an interactive game with circles and lines connecting them. </canvas> <script src="js/jquery-2.1.3.min.js"></script> <script src="js/untangle.data.js"></script> <script src="js/untangle.drawing.js"></script> <script src="js/untangle.input.js"></script> <script src="js/untangle.js"></script> </body> </html> Use CSS to set the background color of the Canvas inside untangle.css: canvas { background: grey; } In the untangle.js JavaScript file, we put a jQuery document ready function and draw a color circle inside it: $(document).ready(function(){ var canvas = document.getElementById("game"); var ctx = canvas.getContext("2d"); ctx.fillStyle = "GOLD"; ctx.beginPath(); ctx.arc(100, 100, 50, 0, Math.PI*2, true); ctx.closePath(); ctx.fill(); }); Open the index.html file in a web browser and we will get the following screenshot: What just happened? We have just created a simple Canvas context with circles on it. There are not many settings for the canvas element itself. We set the width and height of the Canvas, the same as we have fixed the dimensions of real drawing paper. Also, we assign an ID attribute to the Canvas for an easier reference in JavaScript: <canvas id="game" width="768" height="400"> This is an interactive game with circles and lines connecting them. </canvas> Putting in fallback content when the web browser does not support the Canvas Not every web browser supports the canvas element. The canvas element provides an easy way to provide fallback content if the canvas element is not supported. The content also provides meaningful information for any screen reader too. Anything inside the open and close tags of the canvas element is the fallback content. This content is hidden if the web browser supports the element. Browsers that don't support canvas will instead display that fallback content. It is good practice to provide useful information in the fallback content. For instance, if the canvas tag's purpose is a dynamic picture, we may consider placing an <img> alternative there. Or we may also provide some links to modern web browsers for the visitor to upgrade their browser easily. The Canvas context When we draw in the Canvas, we actually call the drawing API of the canvas rendering context. You can think of the relationship of the Canvas and context as Canvas being the frame and context the real drawing surface. Currently, we have 2d, webgl, and webgl2 as the context options. In our example, we'll use the 2D drawing API by calling getContext("2d"). var canvas = document.getElementById("game"); var ctx = canvas.getContext("2d"); Drawing circles and shapes with the Canvas arc function There is no circle function to draw a circle. The Canvas drawing API provides a function to draw different arcs, including the circle. The arc function accepts the following arguments: Arguments Discussion X The center point of the arc in the x axis. Y The center point of the arc in the y axis. radius The radius is the distance between the center point and the arc's perimeter. When drawing a circle, a larger radius means a larger circle. startAngle The starting point is an angle in radians. It defines where to start drawing the arc on the perimeter. endAngle The ending point is an angle in radians. The arc is drawn from the position of the starting angle, to this end angle. counter-clockwise This is a Boolean indicating the arc from startingAngle to endingAngle drawn in a clockwise or counter-clockwise direction. This is an optional argument with the default value false. Converting degrees to radians The angle arguments used in the arc function are in radians instead of degrees. If you are familiar with the degrees angle, you may need to convert the degrees into radians before putting the value into the arc function. We can convert the angle unit using the following formula: radians = p/180 x degrees Executing the path drawing in the Canvas When we are calling the arc function or other path drawing functions, we are not drawing the path immediately in the Canvas. Instead, we are adding it into a list of the paths. These paths will not be drawn until we execute the drawing command. There are two drawing executing commands: one command to fill the paths and the other to draw the stroke. We fill the paths by calling the fill function and draw the stroke of the paths by calling the stroke function, which we will use later when drawing lines: ctx.fill(); Beginning a path for each style The fill and stroke functions fill and draw the paths in the Canvas but do not clear the list of paths. Take the following code snippet as an example. After filling our circle with the color red, we add other circles and fill them with green. What happens to the code is both the circles are filled with green, instead of only the new circle being filled by green: var canvas = document.getElementById('game'); var ctx = canvas.getContext('2d'); ctx.fillStyle = "red"; ctx.arc(100, 100, 50, 0, Math.PI*2, true); ctx.fill();   ctx.arc(210, 100, 50, 0, Math.PI*2, true); ctx.fillStyle = "green"; ctx.fill(); This is because, when calling the second fill command, the list of paths in the Canvas contains both circles. Therefore, the fill command fills both circles with green and overrides the red color circle. In order to fix this issue, we want to ensure we call beginPath before drawing a new shape every time. The beginPath function empties the list of paths, so the next time we call the fill and stroke commands, they will only apply to all paths after the last beginPath. Have a go hero We have just discussed a code snippet where we intended to draw two circles: one in red and the other in green. The code ends up drawing both circles in green. How can we add a beginPath command to the code so that it draws one red circle and one green circle correctly? Closing a path The closePath function will draw a straight line from the last point of the latest path to the first point of the path. This is called closing the path. If we are only going to fill the path and are not going to draw the stroke outline, the closePath function does not affect the result. The following screenshot compares the results on a half circle with one calling closePath and the other not calling closePath: Pop quiz Q1. Do we need to use the closePath function on the shape we are drawing if we just want to fill the color and not draw the outline stroke? Yes, we need to use the closePath function. No, it does not matter whether we use the closePath function. Wrapping the circle drawing in a function Drawing a circle is a common function that we will use a lot. It is better to create a function to draw a circle now instead of entering several code lines. Time for action – putting the circle drawing code into a function Let's make a function to draw the circle and then draw some circles in the Canvas. We are going to put code in different files to make the code simpler: Open the untangle.drawing.js file in our code editor and put in the following code: if (untangleGame === undefined) { var untangleGame = {}; }   untangleGame.drawCircle = function(x, y, radius) { var ctx = untangleGame.ctx; ctx.fillStyle = "GOLD"; ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI*2, true); ctx.closePath(); ctx.fill(); }; Open the untangle.data.js file and put the following code into it: if (untangleGame === undefined) { var untangleGame = {}; }   untangleGame.createRandomCircles = function(width, height) { // randomly draw 5 circles var circlesCount = 5; var circleRadius = 10; for (var i=0;i<circlesCount;i++) {    var x = Math.random()*width;    var y = Math.random()*height;    untangleGame.drawCircle(x, y, circleRadius); } }; Then open the untangle.js file. Replace the original code in the JavaScript file with the following code: if (untangleGame === undefined) { var untangleGame = {}; }   // Entry point $(document).ready(function(){ var canvas = document.getElementById("game"); untangleGame.ctx = canvas.getContext("2d");   var width = canvas.width; var height = canvas.height;   untangleGame.createRandomCircles(width, height);   }); Open the HTML file in the web browser to see the result: What just happened? The code of drawing circles is executed after the page is loaded and ready. We used a loop to draw several circles in random places in the Canvas. Dividing code into files We are putting the code into different files. Currently, there are the untangle.js, untangle.drawing.js, and untangle.data.js files. The untangle.js is the entry point of the game. Then we put logic that is related to the context drawing into untangle.drawing.js and logic that's related to data manipulation into the untangle.data.js file. We use the untangleGame object as the global object that's being accessed across all the files. At the beginning of each JavaScript file, we have the following code to create this object if it does not exist: if (untangleGame === undefined) { var untangleGame = {}; } Generating random numbers in JavaScript In game development, we often use random functions. We may want to randomly summon a monster for the player to fight, we may want to randomly drop a reward when the player makes progress, and we may want a random number to be the result of rolling a dice. In this code, we place the circles randomly in the Canvas. To generate a random number in JavaScript, we use the Math.random() function. There is no argument in the random function. It always returns a floating number between 0 and 1. The number is equal or bigger than 0 and smaller than 1. There are two common ways to use the random function. One way is to generate random numbers within a given range. The other way is generating a true or false value. Usage Code Discussion Getting a random integer between A and B Math.floor(Math.random()*B)+A Math.floor() function cuts the decimal point of the given number. Take Math.floor(Math.random()*10)+5 as an example. Math.random() returns a decimal number between 0 to 0.9999…. Math.random()*10 is a decimal number between 0 to 9.9999…. Math.floor(Math.random()*10) is an integer between 0 to 9. Finally, Math.floor(Math.random()*10) + 5 is an integer between 5 to 14. Getting a random Boolean (Math.random() > 0.495) (Math.random() > 0.495) means 50 percent false and 50 percent true. We can further adjust the true/false ratio. (Math.random() > 0.7) means almost 70 percent false and 30 percent true. Saving the circle position When we are developing a DOM-based game, we often put the game objects into DIV elements and accessed them later in code logic. It is a different story in the Canvas-based game development. In order to access our game objects after they are drawn in the Canvas, we need to remember their states ourselves. Let's say now we want to know how many circles are drawn and where they are, and we will need an array to store their position. Time for action – saving the circle position Open the untangle.data.js file in the text editor. Add the following circle object definition code in the JavaScript file: untangleGame.Circle = function(x,y,radius){ this.x = x; this.y = y; this.radius = radius; } Now we need an array to store the circles' positions. Add a new array to the untangleGame object: untangleGame.circles = []; While drawing every circle in the Canvas, we save the position of the circle in the circles array. Add the following line before calling the drawCircle function, inside the createRandomCircles function: untangleGame.circles.push(new untangleGame.Circle(x,y,circleRadius)); After the steps, we should have the following code in the untangle.data.js file: if (untangleGame === undefined) { var untangleGame = {}; }   untangleGame.circles = [];   untangleGame.Circle = function(x,y,radius){ this.x = x; this.y = y; this.radius = radius; };   untangleGame.createRandomCircles = function(width, height) { // randomly draw 5 circles var circlesCount = 5; var circleRadius = 10; for (var i=0;i<circlesCount;i++) {    var x = Math.random()*width;    var y = Math.random()*height;    untangleGame.circles.push(new      untangleGame.Circle(x,y,circleRadius));    untangleGame.drawCircle(x, y, circleRadius); } }; Now we can test the code in the web browser. There is no visual difference between this code and the last example when drawing random circles in the Canvas. This is because we are saving the circles but have not changed any code that affects the appearance. We just make sure it looks the same and there are no new errors. What just happened? We saved the position and radius of each circle. This is because Canvas drawing is an immediate mode. We cannot directly access the object drawn in the Canvas because there is no such information. All lines and shapes are drawn on the Canvas as pixels and we cannot access the lines or shapes as individual objects. Imagine that we are drawing on a real canvas. We cannot just move a house in an oil painting, and in the same way we cannot directly manipulate any drawn items in the canvas element. Defining a basic class definition in JavaScript We can use object-oriented programming in JavaScript. We can define some object structures for our use. The Circle object provides a data structure for us to easily store a collection of x and y positions and the radii. After defining the Circle object, we can create a new Circle instance with an x, y, and radius value using the following code: var circle1 = new Circle(100, 200, 10); For more detailed usage on object-oriented programming in JavaScript, please check out the Mozilla Developer Center at the following link: https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript Have a go hero We have drawn several circles randomly on the Canvas. They are in the same style and of the same size. How about we randomly draw the size of the circles? And fill the circles with different colors? Try modifying the code and then play with the drawing API. Drawing lines in the Canvas Now we have several circles here, so how about connecting them with lines? Let's draw a straight line between each circle. Time for action – drawing straight lines between each circle Open the index.html file we just used in the circle-drawing example. Change the wording in h1 from drawing circles in Canvas to drawing lines in Canvas. Open the untangle.data.js JavaScript file. We define a Line class to store the information that we need for each line: untangleGame.Line = function(startPoint, endPoint, thickness) { this.startPoint = startPoint; this.endPoint = endPoint; this.thickness = thickness; } Save the file and switch to the untangle.drawing.js file. We need two more variables. Add the following lines into the JavaScript file: untangleGame.thinLineThickness = 1; untangleGame.lines = []; We add the following drawLine function into our code, after the existing drawCircle function in the untangle.drawing.js file. untangleGame.drawLine = function(ctx, x1, y1, x2, y2, thickness) { ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.lineWidth = thickness; ctx.strokeStyle = "#cfc"; ctx.stroke(); } Then we define a new function that iterates the circle list and draws a line between each pair of circles. Append the following code in the JavaScript file: untangleGame.connectCircles = function() { // connect the circles to each other with lines untangleGame.lines.length = 0; for (var i=0;i< untangleGame.circles.length;i++) {    var startPoint = untangleGame.circles[i];    for(var j=0;j<i;j++) {      var endPoint = untangleGame.circles[j];      untangleGame.drawLine(startPoint.x, startPoint.y,        endPoint.x,      endPoint.y, 1);      untangleGame.lines.push(new untangleGame.Line(startPoint,        endPoint,      untangleGame.thinLineThickness));    } } }; Finally, we open the untangle.js file, and add the following code before the end of the jQuery document ready function, after we have called the untangleGame.createRandomCircles function: untangleGame.connectCircles(); Test the code in the web browser. We should see there are lines connected to each randomly placed circle: What just happened? We have enhanced our code with lines connecting each generated circle. You may find a working example at the following URL: http://makzan.net/html5-games/untangle-wip-connect-lines/ Similar to the way we saved the circle position, we have an array to save every line segment we draw. We declare a line class definition to store some essential information of a line segment. That is, we save the start and end point and the thickness of the line. Introducing the line drawing API There are some drawing APIs for us to draw and style the line stroke: Line drawing functions Discussion moveTo The moveTo function is like holding a pen in our hand and moving it on top of the paper without touching it with the pen. lineTo This function is like putting the pen down on the paper and drawing a straight line to the destination point. lineWidth The lineWidth function sets the thickness of the strokes we draw afterwards. stroke The stroke function is used to execute the drawing. We set up a collection of moveTo, lineTo, or styling functions and finally call the stroke function to execute it on the Canvas. We usually draw lines by using the moveTo and lineTo pairs. Just like in the real world, we move our pen on top of the paper to the starting point of a line and put down the pen to draw a line. Then, keep on drawing another line or move to the other position before drawing. This is exactly the flow in which we draw lines on the Canvas. We just demonstrated how to draw a simple line. We can set different line styles to lines in the Canvas. For more details on line styling, please read the styling guide in W3C at http://www.w3.org/TR/2dcontext/#line-styles and the Mozilla Developer Center at https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors. Using mouse events to interact with objects drawn in the Canvas So far, we have shown that we can draw shapes in the Canvas dynamically based on our logic. There is one part missing in the game development, that is, the input. Now, imagine that we can drag the circles around on the Canvas, and the connected lines will follow the circles. In this section, we will add mouse events to the canvas to make our circles draggable. Time for action – dragging the circles in the Canvas Let's continue with our previous code. Open the html5games.untangle.js file. We need a function to clear all the drawings in the Canvas. Add the following function to the end of the untangle.drawing.js file: untangleGame.clear = function() { var ctx = untangleGame.ctx; ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); }; We also need two more functions that draw all known circles and lines. Append the following code to the untangle.drawing.js file: untangleGame.drawAllLines = function(){ // draw all remembered lines for(var i=0;i<untangleGame.lines.length;i++) {    var line = untangleGame.lines[i];    var startPoint = line.startPoint;    var endPoint = line.endPoint;    var thickness = line.thickness;    untangleGame.drawLine(startPoint.x, startPoint.y,      endPoint.x,    endPoint.y, thickness); } };   untangleGame.drawAllCircles = function() { // draw all remembered circles for(var i=0;i<untangleGame.circles.length;i++) {    var circle = untangleGame.circles[i];    untangleGame.drawCircle(circle.x, circle.y, circle.radius); } }; We are done with the untangle.drawing.js file. Let's switch to the untangle.js file. Inside the jQuery document-ready function, before the ending of the function, we add the following code, which creates a game loop to keep drawing the circles and lines: // set up an interval to loop the game loop setInterval(gameloop, 30);   function gameloop() { // clear the Canvas before re-drawing. untangleGame.clear(); untangleGame.drawAllLines(); untangleGame.drawAllCircles(); } Before moving on to the input handling code implementation, let's add the following code to the jQuery document ready function in the untangle.js file, which calls the handleInput function that we will define: untangleGame.handleInput(); It's time to implement our input handling logic. Switch to the untangle.input.js file and add the following code to the file: if (untangleGame === undefined) { var untangleGame = {}; }   untangleGame.handleInput = function(){ // Add Mouse Event Listener to canvas // we find if the mouse down position is on any circle // and set that circle as target dragging circle. $("#game").bind("mousedown", function(e) {    var canvasPosition = $(this).offset();    var mouseX = e.pageX - canvasPosition.left;    var mouseY = e.pageY - canvasPosition.top;      for(var i=0;i<untangleGame.circles.length;i++) {      var circleX = untangleGame.circles[i].x;      var circleY = untangleGame.circles[i].y;      var radius = untangleGame.circles[i].radius;      if (Math.pow(mouseX-circleX,2) + Math.pow(        mouseY-circleY,2) < Math.pow(radius,2)) {        untangleGame.targetCircleIndex = i;        break;      }    } });   // we move the target dragging circle // when the mouse is moving $("#game").bind("mousemove", function(e) {    if (untangleGame.targetCircleIndex !== undefined) {      var canvasPosition = $(this).offset();      var mouseX = e.pageX - canvasPosition.left;      var mouseY = e.pageY - canvasPosition.top;      var circle = untangleGame.circles[        untangleGame.targetCircleIndex];      circle.x = mouseX;      circle.y = mouseY;    }    untangleGame.connectCircles(); });   // We clear the dragging circle data when mouse is up $("#game").bind("mouseup", function(e) {    untangleGame.targetCircleIndex = undefined; }); }; Open index.html in a web browser. There should be five circles with lines connecting them. Try dragging the circles. The dragged circle will follow the mouse cursor and the connected lines will follow too. What just happened? We have set up three mouse event listeners. They are the mouse down, move, and up events. We also created the game loop, which updates the Canvas drawing based on the new position of the circles. You can view the example's current progress at: http://makzan.net/html5-games/untangle-wip-dragging-basic/. Detecting mouse events in circles in the Canvas After discussing the difference between DOM-based development and Canvas-based development, we cannot directly listen to the mouse events of any shapes drawn in the Canvas. There is no such thing. We cannot monitor the event in any shapes drawn in the Canvas. We can only get the mouse event of the canvas element and calculate the relative position of the Canvas. Then we change the states of the game objects according to the mouse's position and finally redraw it on the Canvas. How do we know we are clicking on a circle? We can use the point-in-circle formula. This is to check the distance between the center point of the circle and the mouse position. The mouse clicks on the circle when the distance is less than the circle's radius. We use this formula to get the distance between two points: Distance = (x2-x1)2 + (y2-y1)2. The following graph shows that when the distance between the center point and the mouse cursor is smaller than the radius, the cursor is in the circle: The following code we used explains how we can apply distance checking to know whether the mouse cursor is inside the circle in the mouse down event handler: if (Math.pow(mouseX-circleX,2) + Math.pow(mouseY-circleY,2) < Math.pow(radius,2)) { untangleGame.targetCircleIndex = i; break; } Please note that Math.pow is an expensive function that may hurt performance in some scenarios. If performance is a concern, we may use the bounding box collision checking. When we know that the mouse cursor is pressing the circle in the Canvas, we mark it as the targeted circle to be dragged on the mouse move event. During the mouse move event handler, we update the target dragged circle's position to the latest cursor position. When the mouse is up, we clear the target circle's reference. Pop quiz Q1. Can we directly access an already drawn shape in the Canvas? Yes No Q2. Which method can we use to check whether a point is inside a circle? The coordinate of the point is smaller than the coordinate of the center of the circle. The distance between the point and the center of the circle is smaller than the circle's radius. The x coordinate of the point is smaller than the circle's radius. The distance between the point and the center of the circle is bigger than the circle's radius. Game loop The game loop is used to redraw the Canvas to present the later game states. If we do not redraw the Canvas after changing the states, say the position of the circles, we will not see it. Clearing the Canvas When we drag the circle, we redraw the Canvas. The problem is the already drawn shapes on the Canvas won't disappear automatically. We will keep adding new paths to the Canvas and finally mess up everything in the Canvas. The following screenshot is what will happen if we keep dragging the circles without clearing the Canvas on every redraw: Since we have saved all game statuses in JavaScript, we can safely clear the entire Canvas and draw the updated lines and circles with the latest game status. To clear the Canvas, we use the clearRect function provided by Canvas drawing API. The clearRect function clears a rectangle area by providing a rectangle clipping region. It accepts the following arguments as the clipping region: context.clearRect(x, y, width, height) Argument Definition x The top left point of the rectangular clipping region, on the x axis. y The top left point of the rectangular clipping region, on the y axis. width The width of the rectangular region. height The height of the rectangular region. The x and y values set the top left position of the region to be cleared. The width and height values define how much area is to be cleared. To clear the entire Canvas, we can provide (0,0) as the top left position and the width and height of the Canvas to the clearRect function. The following code clears all things drawn on the entire Canvas: ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); Pop quiz Q1. Can we clear a portion of the Canvas by using the clearRect function? Yes No Q2. Does the following code clear things on the drawn Canvas? ctx.clearRect(0, 0, ctx.canvas.width, 0); Yes No Summary You learned a lot in this article about drawing shapes and creating interaction with the new HTML5 canvas element and the drawing API. Specifically, you learned to draw circles and lines in the Canvas. We added mouse events and touch dragging interaction with the paths drawn in the Canvas. Finally, we succeeded in developing the Untangle puzzle game. Resources for Article: Further resources on this subject: Improving the Snake Game [article] Playing with Particles [article] Making Money with Your Game [article]
Read more
  • 0
  • 0
  • 3193

article-image-getting-started-multiplayer-game-programming
Packt
04 Jun 2015
33 min read
Save for later

Getting Started with Multiplayer Game Programming

Packt
04 Jun 2015
33 min read
In this article by Rodrigo Silveira author of the book Multiplayer gaming with HTML5 game development, if you're reading this, chances are pretty good that you are already a game developer. That being the case, then you already know just how exciting it is to program your own games, either professionally or as a highly gratifying hobby that is very time-consuming. Now you're ready to take your game programming skills to the next level—that is, you're ready to implement multiplayer functionality into your JavaScript-based games. (For more resources related to this topic, see here.) In case you have already set out to create multiplayer games for the Open Web Platform using HTML5 and JavaScript, then you may have already come to realize that a personal desktop computer, laptop, or a mobile device is not particularly the most appropriate device to share with another human player for games in which two or more players share the same game world at the same time. Therefore, what is needed in order to create exciting multiplayer games with JavaScript is some form of networking technology. We will be discussing the following principles and concepts: The basics of networking and network programming paradigms Socket programming with HTML5 Programming a game server and game clients Turn-based multiplayer games Understanding the basics of networking It is said that one cannot program games that make use of networking without first understanding all about the discipline of computer networking and network programming. Although having a deep understanding of any topic can be only beneficial to the person working on that topic, I don't believe that you must know everything there is to know about game networking in order to program some pretty fun and engaging multiplayer games. Saying that is the case is like saying that one needs to be a scholar of the Spanish language in order to cook a simple burrito. Thus, let us take a look at the most basic and fundamental concepts of networking. After you finish reading this article, you will know enough about computer networking to get started, and you will feel comfortable adding multiplayer aspects to your games. One thing to keep in mind is that, even though networked games are not nearly as old as single-player games, computer networking is actually a very old and well-studied subject. Some of the earliest computer network systems date back to the 1950s. Though some of the techniques have improved over the years, the basic idea remains the same: two or more computers are connected together to establish communication between the machines. By communication, I mean data exchange, such as sending messages back and forth between the machines, or one of the machines only sends the data and the other only receives it. With this brief introduction to the concept of networking, you are now grounded in the subject of networking, enough to know what is required to network your games—two or more computers that talk to each other as close to real time as possible. By now, it should be clear how this simple concept makes it possible for us to connect multiple players into the same game world. In essence, we need a way to share the global game data among all the players who are connected to the game session, then continue to update each player about every other player. There are several different techniques that are commonly used to achieve this, but the two most common approaches are peer-to-peer and client-server. Both techniques present different opportunities, including advantages and disadvantages. In general, neither is particularly better than the other, but different situations and use cases may be better suited for one or the other technique. Peer-to-peer networking A simple way to connect players into the same virtual game world is through the peer-to-peer architecture. Although the name might suggest that only two peers ("nodes") are involved, by definition a peer-to-peer network system is one in which two or more nodes are connected directly to each other without a centralized system orchestrating the connection or information exchange. On a typical peer-to-peer setup, each peer serves the same function as every other one—that is, they all consume the same data and share whatever data they produce so that others can stay synchronized. In the case of a peer-to-peer game, we can illustrate this architecture with a simple game of Tic-tac-toe. Once both the players have established a connection between themselves, whoever is starting the game makes a move by marking a cell on the game board. This information is relayed across the wire to the other peer, who is now aware of the decision made by his or her opponent, and can thus update their own game world. Once the second player receives the game's latest state that results from the first player's latest move, the second player is able to make a move of their own by checking some available space on the board. This information is then copied over to the first player who can update their own world and continue the process by making the next desired move. The process goes on until one of the peers disconnects or the game ends as some condition that is based on the game's own business logic is met. In the case of the game of Tic-tac-toe, the game would end once one of the players has marked three spaces on the board forming a straight line or if all nine cells are filled, but neither player managed to connect three cells in a straight path. Some of the benefits of peer-to-peer networked games are as follows: Fast data transmission: Here, the data goes directly to its intended target. In other architectures, the data could go to some centralized node first, then the central node (or the "server") contacts the other peer, sending the necessary updates. Simpler setup: You would only need to think about one instance of your game that, generally speaking, handles its own input, sends its input to other connected peers, and handles their output as input for its own system. This can be especially handy in turn-based games, for example, most board games such as Tic-tac-toe. More reliability: Here one peer that goes offline typically won't affect any of the other peers. However, in the simple case of a two-player game, if one of the players is unable to continue, the game will likely cease to be playable. Imagine, though, that the game in question has dozens or hundreds of connected peers. If a handful of them suddenly lose their Internet connection, the others can continue to play. However, if there is a server that is connecting all the nodes and the server goes down, then none of the other players will know how to talk to each other, and nobody will know what is going on. On the other hand, some of the more obvious drawbacks of peer-to-peer architecture are as follows: Incoming data cannot be trusted: Here, you don't know for sure whether or not the sender modified the data. The data that is input into a game server will also suffer from the same challenge, but once the data is validated and broadcasted to all the other peers, you can be more confident that the data received by each peer from the server will have at least been sanitized and verified, and will be more credible. Fault tolerance can be very low: If enough players share the game world, one or more crashes won't make the game unplayable to the rest of the peers. Now, if we consider the many cases where any of the players that suddenly crash out of the game negatively affect the rest of the players, we can see how a server could easily recover from the crash. Data duplication when broadcasting to other peers: Imagine that your game is a simple 2D side scroller, and many other players are sharing that game world with you. Every time one of the players moves to the right, you receive the new (x, y) coordinates from that player, and you're able to update your own game world. Now, imagine that you move your player to the right by a very few pixels; you would have to send that data out to all of the other nodes in the system. Overall, peer-to-peer is a very powerful networking architecture and is still widely used by many games in the industry. Since current peer-to-peer web technologies are still in their infancy, most JavaScript-powered games today do not make use of peer-to-peer networking. For this and other reasons that should become apparent soon, we will focus almost exclusively on the other popular networking paradigm, namely, the client-server architecture. Client-server networking The idea behind the client-server networking architecture is very simple. If you squint your eyes hard enough, you can almost see a peer-to-peer graph. The most obvious difference between them, is that, instead of every node being an equal peer, one of the nodes is special. That is, instead of every node connecting to every other node, every node (client) connects to a main centralized node called the server. While the concept of a client-server network seems clear enough, perhaps a simple metaphor might make it easier for you to understand the role of each type of node in this network format as well as differentiate it from peer-to-peer . In a peer-to-peer network, you can think of it as a group of friends (peers) having a conversation at a party. They all have access to all the other peers involved in the conversation and can talk to them directly. On the other hand, a client-server network can be viewed as a group of friends having dinner at a restaurant. If a client of the restaurant wishes to order a certain item from the menu, he or she must talk to the waiter, who is the only person in that group of people with access to the desired products and the ability to serve the products to the clients. In short, the server is in charge of providing data and services to one or more clients. In the context of game development, the most common scenario is when two or more clients connect to the same server; the server will keep track of the game as well as the distributed players. Thus, if two players are to exchange information that is only pertinent to the two of them, the communication will go from the first player to and through the server and will end up at the other end with the second player. Following the example of the two players involved in a game of Tic-tac-toe, we can see how similar the flow of events is on a client-server model. Again, the main difference is that players are unaware of each other and only know what the server tells them. While you can very easily mimic a peer-to-peer model by using a server to merely connect the two players, most often the server is used much more actively than that. There are two ways to engage the server in a networked game, namely in an authoritative and a non-authoritative way. That is to say, you can have the enforcement of the game's logic strictly in the server, or you can have the clients handle the game logic, input validation, and so on. Today, most games using the client-server architecture actually use a hybrid of the two (authoritative and non-authoritative servers). For all intents and purposes, however, the server's purpose in life is to receive input from each of the clients and distribute that input throughout the pool of connected clients. Now, regardless of whether you decide to go with an authoritative server instead of a non-authoritative one, you will notice that one of challenges with a client-server game is that you will need to program both ends of the stack. You will have to do this even if your clients do nothing more than take input from the user, forward it to the server, and render whatever data they receive from the server; if your game server does nothing more than forward the input that it receives from each client to every other client, you will still need to write a game client and a game server. We will discuss game clients and servers later. For now, all we really need to know is that these two components are what set this networking model apart from peer-to-peer. Some of the benefits of client-server networked games are as follows: Separation of concerns: If you know anything about software development, you know that this is something you should always aim for. That is, good, maintainable software is written as discrete components where each does one "thing", and it is done well. Writing individual specialized components lets you focus on performing one individual task at a time, making your game easier to design, code, test, reason, and maintain. Centralization: While this can be argued against as well as in favor of, having one central place through which all communication must flow makes it easier to manage such communication, enforce any required rules, control access, and so forth. Less work for the client: Instead of having a client (peer) in charge of taking input from the user as well as other peers, validating all the input, sharing data among other peers, rendering the game, and so on, the client can focus on only doing a few of these things, allowing the server to offload some of this work. This is particularly handy when we talk about mobile gaming, and how much subtle divisions of labor can impact the overall player experience. For example, imagine a game where 10 players are engaged in the same game world. In a peer-to-peer setup, every time one player takes an action, he or she would need to send that action to nine other players (in other words, there would need to be nine network calls, boiling down to more mobile data usage). On the other hand, on a client-server configuration, one player would only need to send his or her action to one of the peers, that is, the server, who would then be responsible for sending that data to the remaining nine players. Common drawbacks of client-server architectures, whether or not the server is authoritative, are as follows: Communication takes longer to propagate: In the very best possible scenario imaginable, every message sent from the first player to the second player would take twice as long to be delivered as compared to a peer-to-peer connection. That is, the message would be first sent from the first player to the server and then from the server to the second player. There are many techniques that are used today to solve the latency problem faced in this scenario, some of which we will discuss in much more depth later. However, the underlying dilemma will always be there. More complexity due to more moving parts: It doesn't really matter how you slice the pizza; the more code you need to write (and trust me, when you build two separate modules for a game, you will write more code), the greater your mental model will have to be. While much of your code can be reused between the client and the server (especially if you use well-established programming techniques, such as object-oriented programming), at the end of the day, you need to manage a greater level of complexity. Single point of failure and network congestion: Up until now, we have mostly discussed the case where only a handful of players participates in the same game. However, the more common case is that a handful of groups of players play different games at the same time. Using the same example of the two-player game of Tic-tac-toe, imagine that there are thousands of players facing each other in single games. In a peer-to-peer setup, once a couple of players have directly paired off, it is as though there are no other players enjoying that game. The only thing to keep these two players from continuing their game is their own connection with each other. On the other hand, if the same thousands of players are connected to each other through a server sitting between the two, then two singled out players might notice severe delays between messages because the server is so busy handling all of the messages from and to all of the other people playing isolated games. Worse yet, these two players now need to worry about maintaining their own connection with each other through the server, but they also hope that the server's connection between them and their opponent will remain active. All in all, many of the challenges involved in client-server networking are well studied and understood, and many of the problems you're likely to face during your multiplayer game development will already have been solved by someone else. Client-server is a very popular and powerful game networking model, and the required technology for it, which is available to us through HTML5 and JavaScript, is well developed and widely supported. Networking protocols – UDP and TCP By discussing some of the ways in which your players can talk to each other across some form of network, we have yet only skimmed over how that communication is actually done. Let us then describe what protocols are and how they apply to networking and, more importantly, multiplayer game development. The word protocol can be defined as a set of conventions or a detailed plan of a procedure [Citation [Def. 3,4]. (n.d.). In Merriam Webster Online, Retrieved February 12, 2015, from http://www.merriam-webster.com/dictionary/protocol]. In computer networking, a protocol describes to the receiver of a message how the data is organized so that it can be decoded. For example, imagine that you have a multiplayer beat 'em up game, and you want to tell the game server that your player just issued a kick command and moved 3 units to the left. What exactly do you send to the server? Do you send a string with a value of "kick", followed by the number 3? Otherwise, do you send the number first, followed by a capitalized letter "K", indicating that the action taken was a kick? The point I'm trying to make is that, without a well-understood and agreed-upon protocol, it is impossible to successfully and predictably communicate with another computer. The two networking protocols that we'll discuss in the section, and that are also the two most widely used protocols in multiplayer networked games, are the Transmission Control Protocol (TCP) and the User Datagram Protocol (UDP). Both protocols provide communication services between clients in a network system. In simple terms, they are protocols that allow us to send and receive packets of data in such a way that the data can be identified and interpreted in a predictable way. When data is sent through TCP, the application running in the source machine first establishes a connection with the destination machine. Once a connection has been established, data is transmitted in packets in such a way that the receiving application can then put the data back together in the appropriate order. TCP also provides built-in error checking mechanisms so that, if a packet is lost, the target application can notify the sender application, and any missing packets are sent again until the entire message is received. In short, TCP is a connection-based protocol that guarantees the delivery of the full data in the correct order. Use cases where this behavior is desirable are all around us. When you download a game from a web server, for example, you want to make sure that the data comes in correctly. You want to be sure that your game assets will be properly and completely downloaded before your users start playing your game. While this guarantee of delivery may sound very reassuring, it can also be thought of as a slow process, which, as we'll see briefly, may sometimes be more important than knowing that the data will arrive in full. In contrast, UDP transmits packets of data (called datagrams) without the use of a pre-established connection. The main goal of the protocol is to be a very fast and frictionless way of sending data towards some target application. In essence, you can think of UDP as the brave employees who dress up as their company's mascot and stand outside their store waving a large banner in the hope that at least some of the people driving by will see them and give them their business. While at first, UDP may seem like a reckless protocol, the use cases that make UDP so desirable and effective includes the many situations when you care more about speed than missing packets a few times, getting duplicate packets, or getting them out of order. You may also want to choose UDP over TCP when you don't care about the reply from the receiver. With TCP, whether or not you need some form of confirmation or reply from the receiver of your message, it will still take the time to reply back to you, at least acknowledging that the message was received. Sometimes, you may not care whether or not the server received the data. A more concrete example of a scenario where UDP is a far better choice over TCP is when you need a heartbeat from the client letting the server know if the player is still there. If you need to let your server know that the session is still active every so often, and you don't care if one of the heartbeats get lost every now and again, then it would be wise to use UDP. In short, for any data that is not mission-critical and you can afford to lose, UDP might be the best option. In closing, keep in mind that, just as peer-to-peer and client-server models can be built side by side, and in the same way your game server can be a hybrid of authoritative and non-authoritative, there is absolutely no reason why your multiplayer games should only use TCP or UDP. Use whichever protocol a particular situation calls for. Network sockets There is one other protocol that we'll cover very briefly, but only so that you can see the need for network sockets in game development. As a JavaScript programmer, you are doubtlessly familiar with Hypertext Transfer Protocol (HTTP). This is the protocol in the application layer that web browsers use to fetch your games from a Web server. While HTTP is a great protocol to reliably retrieve documents from web servers, it was not designed to be used in real-time games; therefore, it is not ideal for this purpose. The way HTTP works is very simple: a client sends a request to a server, which then returns a response back to the client. The response includes a completion status code, indicating to the client that the request is either in process, needs to be forwarded to another address, or is finished successfully or erroneously. There are a handful of things to note about HTTP that will make it clear that a better protocol is needed for real-time communication between the client and server. Firstly, after each response is received by the requester, the connection is closed. Thus, before making each and every request, a new connection must be established with the server. Most of the time, an HTTP request will be sent through TCP, which, as we've seen, can be slow, relatively speaking. Secondly, HTTP is by design a stateless protocol. This means that, every time you request a resource from a server, the server has no idea who you are and what is the context of the request. (It doesn't know whether this is your first request ever or if you're a frequent requester.) A common solution to this problem is to include a unique string with every HTTP request that the server keeps track of, and can thus provide information about each individual client on an ongoing basis. You may recognize this as a standard session. The major downside with this solution, at least with regard to real-time gaming, is that mapping a session cookie to the user's session takes additional time. Finally, the major factor that makes HTTP unsuitable for multiplayer game programming is that the communication is one way—only the client can connect to the server, and the server replies back through the same connection. In other words, the game client can tell the game server that a punch command has been entered by the user, but the game server cannot pass that information along to other clients. Think of it like a vending machine. As a client of the machine, we can request specific items that we wish to buy. We formalize this request by inserting money into the vending machine, and then we press the appropriate button. Under no circumstance will a vending machine issue commands to a person standing nearby. That would be like waiting for a vending machine to dispense food, expecting people to deposit the money inside it afterwards. The answer to this lack of functionality in HTTP is pretty straightforward. A network socket is an endpoint in a connection that allows for two-way communication between the client and the server. Think of it more like a telephone call, rather than a vending machine. During a telephone call, either party can say whatever they want at any given time. Most importantly, the connection between both parties remains open throughout the duration of the conversation, making the communication process highly efficient. WebSocket is a protocol built on top of TCP, allowing web-based applications to have two-way communication with a server. The way a WebSocket is created consists of several steps, including a protocol upgrade from HTTP to WebSocket. Thankfully, all of the heavy lifting is done behind the scenes by the browser and JavaScript. For now, the key takeaway here is that with a TCP socket (yes, there are other types of socket including UDP sockets), we can reliably communicate with a server, and the server can talk back to us as per the need. Socket programming in JavaScript Let's now bring the conversation about network connections, protocols, and sockets to a close by talking about the tools—JavaScript and WebSockets—that bring everything together, allowing us to program awesome multiplayer games in the language of the open Web. The WebSocket protocol Modern browsers and other JavaScript runtime environments have implemented the WebSocket protocol in JavaScript. Don't make the mistake of thinking that just because we can create WebSocket objects in JavaScript, WebSockets are part of JavaScript. The standard that defines the WebSocket protocol is language-agnostic and can be implemented in any programming language. Thus, before you start to deploy your JavaScript games that make use of WebSockets, ensure that the environment that will run your game uses an implementation of the ECMA standard that also implements WebSockets. In other words, not all browsers will know what to do when you ask for a WebSocket connection. For the most part, though, the latest versions, as of this writing, of the most popular browsers today (namely, Google Chrome, Safari, Mozilla Firefox, Opera, and Internet Explorer) implement the current latest revision of RFC 6455. Previous versions of WebSockets (such as protocol version - 76, 7, or 10) are slowly being deprecated and have been removed by some of the previously mentioned browsers. Probably the most confusing thing about the WebSocket protocol is the way each version of the protocol is named. The very first draft (which dates back to 2010), was named draft-hixie-thewebsocketprotocol-75. The next version was named draft-hixie-thewebsocketprotocol-76. Some people refer to these versions as 75 and 76, which can be quite confusing, especially since the fourth version of the protocol is named draft-ietf-hybi-thewebsocketprotocol-07, which is named in the draft as WebSocket Version 7. The current version of the protocol (RFC 6455) is 13. Let us take a quick look at the programming interface (API) that we'll use within our JavaScript code to interact with a WebSocket server. Keep in mind that we'll need to write both the JavaScript clients that use WebSockets to consume data as well as the WebSocket server, which uses WebSockets but plays the role of the server. The difference between the two will become apparent as we go over some examples. Creating a client-side WebSocket The following code snippet creates a new object of type WebSocket that connects the client to some backend server. The constructor takes two parameters; the first is required and represents the URL where the WebSocket server is running and expecting connections. The second URL, is an optional list of sub-protocols that the server may implement. var socket = new WebSocket('ws://www.game-domain.com'); Although this one line of code may seem simple and harmless enough, here are a few things to keep in mind: We are no longer in HTTP territory. The address to your WebSocket server now starts with ws:// instead of http://. Similarly, when we work with secure (encrypted) sockets, we would specify the server's URL as wss://, just like in https://. It may seem obvious to you, but a common pitfall that those getting started with WebSockets fall into is that, before you can establish a connection with the previous code, you need a WebSocket server running at that domain. WebSockets implement the same-origin security model. As you may have already seen with other HTML5 features, the same-origin policy states that you can only access a resource through JavaScript if both the client and the server are in the same domain. For those who are not familiar with the same-domain (also known as the same-origin) policy, the three things that constitute a domain, in this context, are the protocol, host, and port of the resource being accessed. In the previous example, the protocol, host, and port number were, respectively ws (and not wss, http, or ssh), www.game-domain.com (any sub-domain, such as game-domain.com or beta.game-domain.com would violate the same-origin policy), and 80 (by default, WebSocket connects to port 80, and port 443 when it uses wss). Since the server in the previous example binds to port 80, we don't need to explicitly specify the port number. However, had the server been configured to run on a different port, say 2667, then the URL string would need to include a colon followed by the port number that would need to be placed at the end of the host name, such as ws://www.game-domain.com:2667. As with everything else in JavaScript, WebSocket instances attempt to connect to the backend server asynchronously. Thus, you should not attempt to issue commands on your newly created socket until you're sure that the server has connected; otherwise, JavaScript will throw an error that may crash your entire game. This can be done by registering a callback function on the socket's onopen event as follows: var socket = new WebSocket('ws://www.game-domain.com'); socket.onopen = function(event) {    // socket ready to send and receive data }; Once the socket is ready to send and receive data, you can send messages to the server by calling the socket object's send method, which takes a string as the message to be sent. // Assuming a connection was previously established socket.send('Hello, WebSocket world!'); Most often, however, you will want to send more meaningful data to the server, such as objects, arrays, and other data structures that have more meaning on their own. In these cases, we can simply serialize our data as JSON strings. var player = {    nickname: 'Juju',    team: 'Blue' };   socket.send(JSON.stringify(player)); Now, the server can receive that message and work with it as the same object structure that the client sent it, by running it through the parse method of the JSON object. var player = JSON.parse(event.data); player.name === 'Juju'; // true player.team === 'Blue'; // true player.id === undefined; // true If you look at the previous example closely, you will notice that we extract the message that is sent through the socket from the data attribute of some event object. Where did that event object come from, you ask? Good question! The way we receive messages from the socket is the same on both the client and server sides of the socket. We must simply register a callback function on the socket's onmessage event, and the callback will be invoked whenever a new message is received. The argument passed into the callback function will contain an attribute named data, which will contain the raw string object with the message that was sent. socket.onmessage = function(event) {    event instanceof MessageEvent; // true      var msg = JSON.parse(event.data); }; Other events on the socket object on which you can register callbacks include onerror, which is triggered whenever an error related to the socket occurs, and onclose, which is triggered whenever the state of the socket changes to CLOSED; in other words, whenever the server closes the connection with the client for any reason or the connected client closes its connection. As mentioned previously, the socket object will also have a property called readyState, which behaves in a similar manner to the equally-named attribute in AJAX objects (or more appropriately, XMLHttpRequest objects). This attribute represents the current state of the connection and can have one of four values at any point in time. This value is an unsigned integer between 0 and 3, inclusive of both the numbers. For clarity, there are four accompanying constants on the WebSocket class that map to the four numerical values of the instance's readyState attribute. The constants are as follows: WebSocket.CONNECTING: This has a value of 0 and means that the connection between the client and the server has not yet been established. WebSocket.OPEN: This has a value of 1 and means that the connection between the client and the server is open and ready for use. Whenever the object's readyState attribute changes from CONNECTING to OPEN, which will only happen once in the object's life cycle, the onopen callback will be invoked. WebSocket.CLOSING: This has a value of 2 and means that the connection is being closed. WebSocket.CLOSED: This has a value of 3 and means that the connection is now closed (or could not be opened to begin with). Once the readyState has changed to a new value, it will never return to a previous state in the same instance of the socket object. Thus, if a socket object is CLOSING or has already become CLOSED, it will never OPEN again. In this case, you would need a new instance of WebSocket if you would like to continue to communicate with the server. To summarize, let us bring together the simple WebSocket API features that we discussed previously and create a convenient function that simplifies data serialization, error checking, and error handling when communicating with the game server: function sendMsg(socket, data) {    if (socket.readyState === WebSocket.OPEN) {      socket.send(JSON.stringify(data));        return true;    }      return false; }; Game clients Earlier, we talked about the architecture of a multiplayer game that was based on the client-server pattern. Since this is the approach we will take for the games that we'll be developing, let us define some of the main roles that the game client will fulfill. From a higher level, a game client will be the interface between the human player and the rest of the game universe (which includes the game server and other human players who are connected to it). Thus, the game client will be in charge of taking input from the player, communicating this to the server, receive any further instructions and information from the server, and then render the final output to the human player again. Depending on the type of game server used, the client can be more sophisticated than just an input application that renders static data received from the server. For example, the client could very well simulate what the game server will do and present the result of this simulation to the user while the server performs the real calculations and tells the results to the client. The biggest selling point of this technique is that the game would seem a lot more dynamic and real-time to the user since the client responds to input almost instantly. Game servers The game server is primarily responsible for connecting all the players to the same game world and keeping the communication going between them. However as you will soon realize, there may be cases where you will want the server to be more sophisticated than a routing application. For example, just because one of the players is telling the server to inform the other participants that the game is over, and the player sending the message is the winner, we may still want to confirm the information before deciding that the game is in fact over. With this idea in mind, we can label the game server as being of one of the two kinds: authoritative or non-authoritative. In an authoritative game server, the game's logic is actually running in memory (although it normally doesn't render any graphical output like the game clients certainly will) all the time. As each client reports the information to the server by sending messages through its corresponding socket, the server updates the current game state and sends the updates back to all of the players, including the original sender. This way we can be more certain that any data coming from the server has been verified and is accurate. In a non-authoritative server, the clients take on a much more involved part in the game logic enforcement, which gives the client a lot more trust. As suggested previously, what we can do is take the best of both worlds and create a mix of both the techniques. What we will do is, have a strictly authoritative server, but clients that are smart and can do some of the work on their own. Since the server has the ultimate say in the game, however, any messages received by clients from the server are considered as the ultimate truth and supersede any conclusions it came to on its own. Summary Overall, we discussed the basics of networking and network programming paradigms. We saw how WebSockets makes it possible to develop real-time, multiplayer games in HTML5. Finally, we implemented a simple game client and game server using widely supported web technologies and built a fun game of Tic-tac-toe. Resources for Article: Further resources on this subject: HTML5 Game Development – A Ball-shooting Machine with Physics Engine [article] Creating different font files and using web fonts [article] HTML5 Canvas [article]
Read more
  • 0
  • 0
  • 25405

article-image-playing-physics
Packt
03 Jun 2015
26 min read
Save for later

Playing with Physics

Packt
03 Jun 2015
26 min read
In this article by Maxime Barbier, author of the book SFML Blueprints, we will add physics into this game and turn it into a new one. By doing this, we will learn: What is a physics engine How to install and use the Box2D library How to pair the physics engine with SFML for the display How to add physics in the game In this article, we will learn the magic of physics. We will also do some mathematics but relax, it's for conversion only. Now, let's go! (For more resources related to this topic, see here.) A physics engine – késako? We will speak about physics engine, but the first question is "what is a physics engine?" so let's explain it. A physics engine is a software or library that is able to simulate Physics, for example, the Newton-Euler equation that describes the movement of a rigid body. A physics engine is also able to manage collisions, and some of them can deal with soft bodies and even fluids. There are different kinds of physics engines, mainly categorized into real-time engine and non-real-time engine. The first one is mostly used in video games or simulators and the second one is used in high performance scientific simulation, in the conception of special effects in cinema and animations. As our goal is to use the engine in a video game, let's focus on real-time-based engine. Here again, there are two important types of engines. The first one is for 2D and the other for 3D. Of course you can use a 3D engine in a 2D world, but it's preferable to use a 2D engine for an optimization purpose. There are plenty of engines, but not all of them are open source. 3D physics engines For 3D games, I advise you to use the Bullet physics library. This was integrated in the Blender software, and was used in the creation of some commercial games and also in the making of films. This is a really good engine written in C/C++ that can deal with rigid and soft bodies, fluids, collisions, forces… and all that you need. 2D physics engines As previously said, in a 2D environment, you can use a 3D physics engine; you just have to ignore the depth (Z axes). However, the most interesting thing is to use an engine optimized for the 2D environment. There are several engines like this one and the most famous ones are Box2D and Chipmunk. Both of them are really good and none of them are better than the other, but I had to make a choice, which was Box2D. I've made this choice not only because of its C++ API that allows you to use overload, but also because of the big community involved in the project. Physics engine comparing game engine Do not mistake a physics engine for a game engine. A physics engine only simulates a physical world without anything else. There are no graphics, no logics, only physics simulation. On the contrary, a game engine, most of the time includes a physics engine paired with a render technology (such as OpenGL or DirectX). Some predefined logics depend on the goal of the engine (RPG, FPS, and so on) and sometimes artificial intelligence. So as you can see, a game engine is more complete than a physics engine. The two mostly known engines are Unity and Unreal engine, which are both very complete. Moreover, they are free for non-commercial usage. So why don't we directly use a game engine? This is a good question. Sometimes, it's better to use something that is already made, instead of reinventing it. However, do we really need all the functionalities of a game engine for this project? More importantly, what do we need it for? Let's see the following: A graphic output Physics engine that can manage collision Nothing else is required. So as you can see, using a game engine for this project would be like killing a fly with a bazooka. I hope that you have understood the aim of a physics engine, the differences between a game and physics engine, and the reason for the choices made for the project. Using Box2D As previously said, Box2D is a physics engine. It has a lot of features, but the most important for the project are the following (taken from the Box2D documentation): Collision: This functionality is very interesting as it allows our tetrimino to interact with each other Continuous collision detection Rigid bodies (convex polygons and circles) Multiple shapes per body Physics: This functionality will allow a piece to fall down and more Continuous physics with the time of impact solver Joint limits, motors, and friction Fairly accurate reaction forces/impulses As you can see, Box2D provides all that we need in order to build our game. There are a lot of other features usable with this engine, but they don't interest us right now so I will not describe them in detail. However, if you are interested, you can take a look at the official website for more details on the Box2D features (http://box2d.org/about/). It's important to note that Box2D uses meters, kilograms, seconds, and radians for the angle as units; SFML uses pixels, seconds, and degrees. So we will need to make some conversions. I will come back to this later. Preparing Box2D Now that Box2D is introduced, let's install it. You will find the list of available versions on the Google code project page at https://code.google.com/p/box2d/downloads/list. Currently, the latest stable version is 2.3. Once you have downloaded the source code (from compressed file or using SVN), you will need to build it. Install Once you have successfully built your Box2D library, you will need to configure your system or IDE to find the Box2D library and headers. The newly built library can be found in the /path/to/Box2D/build/Box2D/ directory and is named libBox2D.a. On the other hand, the headers are located in the path/to/Box2D/Box2D/ directory. If everything is okay, you will find a Box2D.h file in the folder. On Linux, the following command adds Box2D to your system without requiring any configuration: sudo make install Pairing Box2D and SFML Now that Box2D is installed and your system is configured to find it, let's build the physics "hello world": a falling square. It's important to note that Box2D uses meters, kilograms, seconds, and radian for angle as units; SFML uses pixels, seconds, and degrees. So we will need to make some conversions. Converting radians to degrees or vice versa is not difficult, but pixels to meters… this is another story. In fact, there is no way to convert a pixel to meter, unless if the number of pixels per meter is fixed. This is the technique that we will use. So let's start by creating some utility functions. We should be able to convert radians to degrees, degrees to radians, meters to pixels, and finally pixels to meters. We will also need to fix the pixel per meter value. As we don't need any class for these functions, we will define them in a namespace converter. This will result as the following code snippet: namespace converter {    constexpr double PIXELS_PER_METERS = 32.0;    constexpr double PI = 3.14159265358979323846;      template<typename T>    constexpr T pixelsToMeters(const T& x){return x/PIXELS_PER_METERS;};      template<typename T>    constexpr T metersToPixels(const T& x){return x*PIXELS_PER_METERS;};      template<typename T>    constexpr T degToRad(const T& x){return PI*x/180.0;};      template<typename T>    constexpr T radToDeg(const T& x){return 180.0*x/PI;} } As you can see, there is no difficulty here. We start to define some constants and then the convert functions. I've chosen to make the function template to allow the use of any number type. In practice, it will mostly be double or int. The conversion functions are also declared as constexpr to allow the compiler to calculate the value at compile time if it's possible (for example, with constant as a parameter). It's interesting because we will use this primitive a lot. Box2D, how does it work? Now that we can convert SFML unit to Box2D unit and vice versa, we can pair Box2D with SFML. But first, how exactly does Box2D work? Box2D works a lot like a physics engine: You start by creating an empty world with some gravity. Then, you create some object patterns. Each pattern contains the shape of the object position, its type (static or dynamic), and some other characteristics such as its density, friction, and energy restitution. You ask the world to create a new object defined by the pattern. In each game loop, you have to update the physical world with a small step such as our world in the games we've already made. Because the physics engine does not display anything on the screen, we will need to loop all the objects and display them by ourselves. Let's start by creating a simple scene with two kinds of objects: a ground and square. The ground will be fixed and the squares will not. The square will be generated by a user event: mouse click. This project is very simple, but the goal is to show you how to use Box2D and SFML together with a simple case study. A more complex one will come later. We will need three functionalities for this small project to: Create a shape Display the world Update/fill the world Of course there is also the initialization of the world and window. Let's start with the main function: As always, we create a window for the display and we limit the FPS number to 60. I will come back to this point with the displayWorld function. We create the physical world from Box2D, with gravity as a parameter. We create a container that will store all the physical objects for the memory clean purpose. We create the ground by calling the createBox function (explained just after). Now it is time for the minimalist game loop:    Close event managements    Create a box by detecting that the right button of the mouse is pressed Finally, we clean the memory before exiting the program: int main(int argc,char* argv[]) {    sf::RenderWindow window(sf::VideoMode(800, 600, 32), "04_Basic");    window.setFramerateLimit(60);    b2Vec2 gravity(0.f, 9.8f);    b2World world(gravity);    std::list<b2Body*> bodies;    bodies.emplace_back(book::createBox(world,400,590,800,20,b2_staticBody));      while(window.isOpen()) {        sf::Event event;        while(window.pollEvent(event)) {            if (event.type == sf::Event::Closed)                window.close();        }        if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {            int x = sf::Mouse::getPosition(window).x;            int y = sf::Mouse::getPosition(window).y;            bodies.emplace_back(book::createBox(world,x,y,32,32));        }        displayWorld(world,window);    }      for(b2Body* body : bodies) {        delete static_cast<sf::RectangleShape*>(body->GetUserData());        world.DestroyBody(body);    }    return 0; } For the moment, except the Box2D world, nothing should surprise you so let's continue with the box creation. This function is under the book namespace. b2Body* createBox(b2World& world,int pos_x,int pos_y, int size_x,int size_y,b2BodyType type = b2_dynamicBody) {    b2BodyDef bodyDef;    bodyDef.position.Set(converter::pixelsToMeters<double>(pos_x),                         converter::pixelsToMeters<double>(pos_y));    bodyDef.type = type;    b2PolygonShape b2shape;    b2shape.SetAsBox(converter::pixelsToMeters<double>(size_x/2.0),                    converter::pixelsToMeters<double>(size_y/2.0));      b2FixtureDef fixtureDef;    fixtureDef.density = 1.0;    fixtureDef.friction = 0.4;    fixtureDef.restitution= 0.5;    fixtureDef.shape = &b2shape;      b2Body* res = world.CreateBody(&bodyDef);    res->CreateFixture(&fixtureDef);      sf::Shape* shape = new sf::RectangleShape(sf::Vector2f(size_x,size_y));    shape->setOrigin(size_x/2.0,size_y/2.0);    shape->setPosition(sf::Vector2f(pos_x,pos_y));                                                   if(type == b2_dynamicBody)        shape->setFillColor(sf::Color::Blue);    else        shape->setFillColor(sf::Color::White);      res->SetUserData(shape);      return res; } This function contains a lot of new functionalities. Its goal is to create a rectangle of a specific size at a predefined position. The type of this rectangle is also set by the user (dynamic or static). Here again, let's explain the function step-by-step: We create b2BodyDef. This object contains the definition of the body to create. So we set the position and its type. This position will be in relation to the gravity center of the object. Then, we create b2Shape. This is the physical shape of the object, in our case, a box. Note that the SetAsBox() method doesn't take the same parameter as sf::RectangleShape. The parameters are half the size of the box. This is why we need to divide the values by two. We create b2FixtureDef and initialize it. This object holds all the physical characteristics of the object such as its density, friction, restitution, and shape. Then, we properly create the object in the physical world. Now, we create the display of the object. This will be more familiar because we will only use SFML. We create a rectangle and set its position, origin, and color. As we need to associate and display SFML object to the physical object, we use a functionality of Box2D: the SetUserData() function. This function takes void* as a parameter and internally holds it. So we use it to keep track of our SFML shape. Finally, the body is returned by the function. This pointer has to be stored to clean the memory later. This is the reason for the body's container in main(). Now, we have the capability to simply create a box and add it to the world. Now, let's render it to the screen. This is the goal of the displayWorld function: void displayWorld(b2World& world,sf::RenderWindow& render) {    world.Step(1.0/60,int32(8),int32(3));    render.clear();    for (b2Body* body=world.GetBodyList(); body!=nullptr; body=body->GetNext())    {          sf::Shape* shape = static_cast<sf::Shape*>(body->GetUserData());        shape->setPosition(converter::metersToPixels(body->GetPosition().x),        converter::metersToPixels(body->GetPosition().y));        shape->setRotation(converter::radToDeg<double>(body->GetAngle()));        render.draw(*shape);    }    render.display(); } This function takes the physics world and window as a parameter. Here again, let's explain this function step-by-step: We update the physical world. If you remember, we have set the frame rate to 60. This is why we use 1,0/60 as a parameter here. The two others are for precision only. In a good code, the time step should not be hardcoded as here. We have to use a clock to be sure that the value will always be the same. Here, it has not been the case to focus on the important part: physics. We reset the screen, as usual. Here is the new part: we loop the body stored by the world and get back the SFML shape. We update the SFML shape with the information taken from the physical body and then render it on the screen. Finally, we render the result on the screen. As you can see, it's not really difficult to pair SFML with Box2D. It's not a pain to add it. However, we have to take care of the data conversion. This is the real trap. Pay attention to the precision required (int, float, double) and everything should be fine. Now that you have all the keys in hand, let's build a real game with physics. Adding physics to a game Now that Box2D is introduced with a basic project, let's focus on the real one. We will modify our basic Tetris to get Gravity-Tetris alias Gravitris. The game control will be the same as in Tetris, but the game engine will not be. We will replace the board with a real physical engine. With this project, we will reuse a lot of work previously done. As already said, the goal of some of our classes is to be reusable in any game using SFML. Here, this will be made without any difficulties as you will see. The classes concerned are those you deal with user event Action, ActionMap, ActionTarget—but also Configuration and ResourceManager. There are still some changes that will occur in the Configuration class, more precisely, in the enums and initialization methods of this class because we don't use the exact same sounds and events that were used in the Asteroid game. So we need to adjust them to our needs. Enough with explanations, let's do it with the following code: class Configuration {    public:        Configuration() = delete;        Configuration(const Configuration&) = delete;        Configuration& operator=(const Configuration&) = delete;               enum Fonts : int {Gui};        static ResourceManager<sf::Font,int> fonts;               enum PlayerInputs : int { TurnLeft,TurnRight, MoveLeft, MoveRight,HardDrop};        static ActionMap<int> playerInputs;               enum Sounds : int {Spawn,Explosion,LevelUp,};        static ResourceManager<sf::SoundBuffer,int> sounds;               enum Musics : int {Theme};        static ResourceManager<sf::Music,int> musics;               static void initialize();           private:        static void initTextures();        static void initFonts();        static void initSounds();        static void initMusics();        static void initPlayerInputs(); }; As you can see, the changes are in the enum, more precisely in Sounds and PlayerInputs. We change the values into more adapted ones to this project. We still have the font and music theme. Now, take a look at the initialization methods that have changed: void Configuration::initSounds() {    sounds.load(Sounds::Spawn,"media/sounds/spawn.flac");    sounds.load(Sounds::Explosion,"media/sounds/explosion.flac");    sounds.load(Sounds::LevelUp,"media/sounds/levelup.flac"); } void Configuration::initPlayerInputs() {    playerInputs.map(PlayerInputs::TurnRight,Action(sf::Keyboard::Up));    playerInputs.map(PlayerInputs::TurnLeft,Action(sf::Keyboard::Down));    playerInputs.map(PlayerInputs::MoveLeft,Action(sf::Keyboard::Left));    playerInputs.map(PlayerInputs::MoveRight,Action(sf::Keyboard::Right));  playerInputs.map(PlayerInputs::HardDrop,Action(sf::Keyboard::Space,    Action::Type::Released)); } No real surprises here. We simply adjust the resources to our needs for the project. As you can see, the changes are really minimalistic and easily done. This is the aim of all reusable modules or classes. Here is a piece of advice, however: keep your code as modular as possible, this will allow you to change a part very easily and also to import any generic part of your project to another one easily. The Piece class Now that we have the configuration class done, the next step is the Piece class. This class will be the most modified one. Actually, as there is too much change involved, let's build it from scratch. A piece has to be considered as an ensemble of four squares that are independent from one another. This will allow us to split a piece at runtime. Each of these squares will be a different fixture attached to the same body, the piece. We will also need to add some force to a piece, especially to the current piece, which is controlled by the player. These forces can move the piece horizontally or can rotate it. Finally, we will need to draw the piece on the screen. The result will show the following code snippet: constexpr int BOOK_BOX_SIZE = 32; constexpr int BOOK_BOX_SIZE_2 = BOOK_BOX_SIZE / 2; class Piece : public sf::Drawable {    public:        Piece(const Piece&) = delete;        Piece& operator=(const Piece&) = delete;          enum TetriminoTypes {O=0,I,S,Z,L,J,T,SIZE};        static const sf::Color TetriminoColors[TetriminoTypes::SIZE];          Piece(b2World& world,int pos_x,int pos_y,TetriminoTypes type,float rotation);        ~Piece();        void update();        void rotate(float angle);        void moveX(int direction);        b2Body* getBody()const;      private:        virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;        b2Fixture* createPart((int pos_x,int pos_y,TetriminoTypes type); ///< position is relative to the piece int the matrix coordinate (0 to 3)        b2Body * _body;        b2World& _world; }; Some parts of the class don't change such as the TetriminoTypes and TetriminoColors enums. This is normal because we don't change any piece's shape or colors. The rest is still the same. The implementation of the class, on the other side, is very different from the precedent version. Let's see it: Piece::Piece(b2World& world,int pos_x,int pos_y,TetriminoTypes type,float rotation) : _world(world) {    b2BodyDef bodyDef;    bodyDef.position.Set(converter::pixelsToMeters<double>(pos_x),    converter::pixelsToMeters<double>(pos_y));    bodyDef.type = b2_dynamicBody;    bodyDef.angle = converter::degToRad(rotation);    _body = world.CreateBody(&bodyDef);      switch(type)    {        case TetriminoTypes::O : {            createPart((0,0,type); createPart((0,1,type);            createPart((1,0,type); createPart((1,1,type);        }break;        case TetriminoTypes::I : {            createPart((0,0,type); createPart((1,0,type);             createPart((2,0,type); createPart((3,0,type);        }break;        case TetriminoTypes::S : {            createPart((0,1,type); createPart((1,1,type);            createPart((1,0,type); createPart((2,0,type);        }break;        case TetriminoTypes::Z : {            createPart((0,0,type); createPart((1,0,type);            createPart((1,1,type); createPart((2,1,type);        }break;        case TetriminoTypes::L : {            createPart((0,1,type); createPart((0,0,type);            createPart((1,0,type); createPart((2,0,type);        }break;        case TetriminoTypes::J : {            createPart((0,0,type); createPart((1,0,type);            createPart((2,0,type); createPart((2,1,type);        }break;        case TetriminoTypes::T : {            createPart((0,0,type); createPart((1,0,type);            createPart((1,1,type); createPart((2,0,type);        }break;        default:break;    }    body->SetUserData(this);    update(); } The constructor is the most important method of this class. It initializes the physical body and adds each square to it by calling createPart(). Then, we set the user data to the piece itself. This will allow us to navigate through the physics to SFML and vice versa. Finally, we synchronize the physical object to the drawable by calling the update() function: Piece::~Piece() {    for(b2Fixture* fixture=_body->GetFixtureList();fixture!=nullptr;    fixture=fixture->GetNext()) {        sf::ConvexShape* shape = static_cast<sf::ConvexShape*>(fixture->GetUserData());        fixture->SetUserData(nullptr);        delete shape;    }    _world.DestroyBody(_body); } The destructor loop on all the fixtures attached to the body, destroys all the SFML shapes and then removes the body from the world: b2Fixture* Piece::createPart((int pos_x,int pos_y,TetriminoTypes type) {    b2PolygonShape b2shape;    b2shape.SetAsBox(converter::pixelsToMeters<double>(BOOK_BOX_SIZE_2),    converter::pixelsToMeters<double>(BOOK_BOX_SIZE_2)    ,b2Vec2(converter::pixelsToMeters<double>(BOOK_BOX_SIZE_2+(pos_x*BOOK_BOX_SIZE)), converter::pixelsToMeters<double>(BOOK_BOX_SIZE_2+(pos_y*BOOK_BOX_SIZE))),0);      b2FixtureDef fixtureDef;    fixtureDef.density = 1.0;    fixtureDef.friction = 0.5;    fixtureDef.restitution= 0.4;    fixtureDef.shape = &b2shape;      b2Fixture* fixture = _body->CreateFixture(&fixtureDef);      sf::ConvexShape* shape = new sf::ConvexShape((unsigned int) b2shape.GetVertexCount());    shape->setFillColor(TetriminoColors[type]);    shape->setOutlineThickness(1.0f);    shape->setOutlineColor(sf::Color(128,128,128));    fixture->SetUserData(shape);       return fixture; } This method adds a square to the body at a specific place. It starts by creating a physical shape as the desired box and then adds this to the body. It also creates the SFML square that will be used for the display, and it will attach this as user data to the fixture. We don't set the initial position because the constructor will do it. void Piece::update() {    const b2Transform& xf = _body->GetTransform();       for(b2Fixture* fixture = _body->GetFixtureList(); fixture != nullptr;    fixture=fixture->GetNext()) {        sf::ConvexShape* shape = static_cast<sf::ConvexShape*>(fixture->GetUserData());        const b2PolygonShape* b2shape = static_cast<b2PolygonShape*>(fixture->GetShape());        const uint32 count = b2shape->GetVertexCount();        for(uint32 i=0;i<count;++i) {            b2Vec2 vertex = b2Mul(xf,b2shape->m_vertices[i]);            shape->setPoint(i,sf::Vector2f(converter::metersToPixels(vertex.x),            converter::metersToPixels(vertex.y)));        }    } } This method synchronizes the position and rotation of all the SFML shapes from the physical position and rotation calculated by Box2D. Because each piece is composed of several parts—fixture—we need to iterate through them and update them one by one. void Piece::rotate(float angle) {    body->ApplyTorque((float32)converter::degToRad(angle),true); } void Piece::moveX(int direction) {    body->ApplyForceToCenter(b2Vec2(converter::pixelsToMeters(direction),0),true); } These two methods add some force to the object to move or rotate it. We forward the job to the Box2D library. b2Body* Piece::getBody()const {return _body;}   void Piece::draw(sf::RenderTarget& target, sf::RenderStates states) const {    for(const b2Fixture* fixture=_body->GetFixtureList();fixture!=nullptr; fixture=fixture->GetNext()) {        sf::ConvexShape* shape = static_cast<sf::ConvexShape*>(fixture->GetUserData());        if(shape)            target.draw(*shape,states);    } } This function draws the entire piece. However, because the piece is composed of several parts, we need to iterate on them and draw them one by one in order to display the entire piece. This is done by using the user data saved in the fixtures. Summary Since the usage of a physics engine has its own particularities such as the units and game loop, we have learned how to deal with them. Finally, we learned how to pair Box2D with SFML, integrate our fresh knowledge to our existing Tetris project, and build a new funny game. Resources for Article: Further resources on this subject: Skinning a character [article] Audio Playback [article] Sprites in Action [article]
Read more
  • 0
  • 0
  • 3164

article-image-adding-graphical-user-interface
Packt
03 Jun 2015
12 min read
Save for later

Adding a Graphical User Interface

Packt
03 Jun 2015
12 min read
In this article by Dr. Edward Lavieri, the author of Getting Started with Unity 5, you will learn how to use Unity 5's new User Interface (UI) system. (For more resources related to this topic, see here.) An overview of graphical user interface Graphical User Interfaces or GUI (pronounced gooey) is a collection of visual components such as text, buttons, and images that facilitates a user's interaction with software. GUIs are also used to provide feedback to players. In the case of our game, the GUI allows players to interact with our game. Without a GUI, the user would have no visual indication of how to use the game. Imagine software without any on-screen indicators of how to use the software. The following image shows how early user interfaces were anything but intuitive: We use GUIs all the time and might not pay too close attention to them, unless they are poorly designed. If you've ever tried to figure out how to use an app on your Smartphone or could not figure out how to perform a specific action with desktop software, you've most likely encountered a poorly designed GUI. Functions of a GUI Our goal is to create a GUI for our game that both informs the user and allows for interaction between the game and the user. To that end, GUIs have two primary purposes: feedback and control. Feedback is generated by the game to the user and control is given to the user and managed by user input. Let's look at each of these more closely. Feedback Feedback can come in many forms. The most common forms of game feedback are visual and audio. Visual feedback can be something as simple as a text on a game screen. An example would be a game player's current score ever-present on the game screen. Games that include dialog systems where the player interacts with non-player characters (NPC) usually have text feedback on the screen that informs what the NPC's responses are. Visual feedback can also be non-textual, such as smoke, fire, explosions, or other graphic effect. Audio feedback can be as simple as a click sound when the user clicks or taps on a button or as complex as a radar ping when an enemy submarine is detected on long-distance sonar scans. You can probably think of all the audio feedback your favorite game provides. When you run your cart over a coin, an audio sound effect is played so there is no question that you earned the coin. If you take a moment to consider all of the audio feedback you are exposed to in games, you'll begin to appreciate the significance of them. Less typical feedback includes device vibration, which is sometimes used with smartphone applications and console games. Some attractions have taken feedback to another level through seat movement and vibration, dispensing liquid and vapor, and introducing chemicals that provide olfactory input. Control Giving players control of the game is the second function of GUIs. There is a wide gambit of types of control. The most simple is using buttons or menus in a game. A game might have a graphical icon of a backpack that, when clicked, gives the user access to the inventory management system of a game. Control seems like an easy concept and it is. Interestingly, most popular console games lack good GUI interfaces, especially when it comes to control. If you play console games, think about how many times you have to refer to the printed or in-game manual. Do you intuitively know all of the controller key mappings? How do you jump, switch weapons, crotch, throw a grenade, or go into stealth mode? In the defense of the game studios that publish these games, there is a lot of control and it can be difficult to make them intuitive. By extension, control is often physical in addition to graphical. Physical components of control include keyboards, mice, trackballs, console controllers, microphones, and other devices. Feedback and control Feedback and control GUI elements are often paired. When you click or tap a button, it usually has both visual and audio effects as well as executing the user's action. When you click (control) on a treasure chest, it opens (visual feedback) and you hear the creak of the old wooden hinges (audio feedback). This example shows the power of using adding feedback to control actions. Game Layers At a primitive level, there are three layers to every game. The core or base level is the Game Layer. The top layer is the User Layer; this is the actual person playing your game. So, it is the layer in between, the GUI Layer that serves as an intermediary between a game and its player. It becomes clear that designing and developing intuitive and well-functioning GUIs is important to a game's functionality, the user experience, and a game's success. Unity 5's UI system Unity's UI system has recently been re-engineered and is now more powerful than ever. Perhaps the most important concept to grasp is the Canvas object. All UI elements are contained in a canvas. Project and scenes can have more than one canvas. You can think of a canvas as a container for UI elements. Canvas To create a canvas, you simply navigate and select GameObject | UI | Canvas from the drop-down menu. You can see from the GameObject | UI menu pop-up that there are 11 different UI elements. Alternatively, you can create your first UI element, such as a button and Unity will automatically create a canvas for you and add it to your Hierarchy view. When you create subsequent UI elements, simply highlight the canvas in the Hierarchy view and then navigate to the GameObject | UI menu to select a new UI element. Here is a brief description of each of the UI elements: UI element Description Panel A frame object Button Standard button that can be clicked Text Text with standard text formatting Image Images can be simple, sliced, tiled, and filled Raw Image Texture file Slider Slider with min and max values Scrollbar Scrollbar with values between 0 and 1 Toggle Standard checkbox; can also be grouped Input Field Text input field Canvas The game object container for UI elements Event System Allows us to trigger scripts from UI elements. An Event System is automatically created when you create a canvas. You can have multiple canvases in your game. As you start building larger games, you'll likely find a use for more than one canvas. Render mode There are a few settings in the Inspector view that you should be aware of regarding your canvas game object. The first setting is the render mode. There are three settings: Screen Space – Overlay, Screen Space – Camera, and World Space: In this render mode, the canvas is automatically resized when the user changes the size or resolution of the game screen. The second render mode, Screen Space – Camera, has a plane distance property that determines how far the canvas is rendered from the camera. The third render mode is World Space. This mode gives you the most control and can be manipulated much like any other game object. I recommend experimenting with different render modes so you know which one you like best and when to use each one. Creating a GUI Creating a GUI in Unity is a relatively easy task. We first create a canvas, or have Unity create it for us when we create our first UI element. Next, we simply add the desired UI elements to our canvas. Once all the necessary elements are in our canvas, you can arrange and format them. It is often best to switch to 2D mode in the Scene view when placing the UI elements on the canvas. This simply makes the task a bit easier. If you have used earlier versions of Unity, you'll note that several things have changed regarding creating and referencing GUI elements. For example, you'll need to include the using UnityEngine.UI; statement before referencing UI components. Also, instead of referencing GUI text as public GUIText waterHeld; you now use public Text waterHeld;. Heads-up displays A game's heads-up display (HUD) is graphical and textual information available to the user at all times. No action should be required of the user other than to look at a specific region of the screen to read the displays. For example, if you are playing car-racing game, you might have an odometer, speedometer, compass, fuel tank level, air pressure, and other visual indicators always on the screen. Creating a HUD Here are the basic steps to create a HUD: Open the game project and load the scene. Navigate and select the GameObject | UI | Text option from the drop-down menu. This will result in a Canvas game object being added to the Hierarchy view, along with a text child item. Select the child item in the Hierarchy view. Then, in the Inspector view, change the text to what you want displayed on the screen. In the Inspector view, you can change the font size. If necessary, you can change the Horizontal Overflow option from Wrap to Overflow: Zoom out in the Scene view until you can see the GUI Canvas. Use the transform tools to place your new GUI element in the top-left corner of the screen. Depending on how you are viewing the scene in the Scene view, you might need to use the hand tool to rotate the scene. So, if your GUI text appears backwards, just rotate the scene until it is correct. Repeat steps 2 through 5 until you've created all the HUD elements you need for your game. Mini-maps Miniature-maps or mini-maps provide game players with a small visual aid that helps them maintain perspective and direction in a game. These mini-maps can be used for many different purposes, depending on the game. Some examples include the ability to view a mini-map that overlooks an enemy encampment; a zoomed out view of the game map with friendly and enemy force indicators; and a mini-map that has the overall tunnel map while the main game screen views the current section of tunnel. Creating a Mini-Map Here are the steps used to create a mini-map for our game: Navigate and select GameObject | Camera from the top menu. In the Hierarchy view, change the name from Camera to Mini-Map. With the mini-map camera selected, go to the Inspector view and click on the Layer button, then Add Layer in the pop-up menu. In the next available User Layer, add the name Mini-Map: Select the Mini-Map option in the Hierarchy view, and then select Layer | Mini-Map. Now the new mini-map camera is assigned to the Mini-Map layer: Next, we'll ensure the main camera is not rendering the Mini-Map camera. Select the Main Camera option in the Hierarchy view. In the Inspector view, select Culling Mask, and then deselect Mini-Map from the pop-up menu: Now we are ready to finish the configuration of our mini-map camera. Select the Mini-Map in the Hierarchy view. Using the transform tools in the Scene view, adjust the camera object so that it shows the area of the game environment you want visible via the mini-map. In the Inspector view, under Camera, make the settings match the following values: Setting Value Clear Flags Depth only Culling Mask Everything Projection Orthographic Size 25 Clipping Planes Near 0.3; Far 1000 Viewpoint Rect X 0.75; Y 0.75; W 1; H 1 Depth 1 Rendering Path User Player Settings Target Texture None Occlusion Culling Selected HDR Not Selected With the Mini-Map camera still selected, right-click on each of the Flare Layer, GUI Layer, and Audio Listener components in the Inspector view and select Remove Component. Save your scene and your project. You are ready to test your mini-map. Mini-maps can be very powerful game components. There are a couple of things to keep in mind if you are going to use mini-maps in your games: Make sure the mini-map size does not obstruct too much of the game environment. There is nothing worse than getting shot by an enemy that you could not see because a mini-map was in the way. The mini-map should have a purpose—we do not include them in games because they are cool. They take up screen real estate and should only be used if needed, such as helping the player make informed decisions. In our game, the player is able to keep an eye on Colt's farm animals while he is out gathering water and corn. Items should be clearly visible on the mini-map. Many games use red dots for enemies, yellow for neutral forces, and blue for friendlies. This type of color-coding provides users with a lot of information at a very quick glance. Ideally, the user should have the flexibility to move the mini-map to a corner of their choosing and toggle it on and off. In our game, we placed the mini-map in the top-right corner of the game screen so that the HUD objects would not be in the way. Summary In this article, you learned about the UI system in Unity 5. You gained an appreciation for the importance of GUIs in games we create. Resources for Article: Further resources on this subject: Bringing Your Game to Life with AI and Animations [article] Looking Back, Looking Forward [article] Introducing the Building Blocks for Unity Scripts [article]
Read more
  • 0
  • 0
  • 1633

article-image-writing-simple-behaviors
Packt
27 Apr 2015
18 min read
Save for later

Writing Simple Behaviors

Packt
27 Apr 2015
18 min read
In this article by Richard Sneyd, the author of Stencyl Essentials, we will learn about Stencyl's signature visual programming interface to create logic and interaction in our game. We create this logic using a WYSIWYG (What You See Is What You Get) block snapping interface. By the end of this article, you will have the Player Character whizzing down the screen, in pursuit of a zigzagging air balloon! Some of the things we will learn to do in this article are as follows: Create Actor Behaviors, and attach them to Actor Types. Add Events to our Behaviors. Use If blocks to create branching, conditional logic to handle various states within our game. Accept and react to input from the player. Apply physical forces to Actors in real-time. One of the great things about this visual approach to programming is that it largely removes the unpleasantness of dealing with syntax (the rules of the programming language), and the inevitable errors that come with it, when we're creating logic for our game. That frees us to focus on the things that matter most in our games: smooth, well wrought game mechanics and enjoyable, well crafted game-play. (For more resources related to this topic, see here.) The Player Handler The first behavior we are going to create is the Player Handler. This behavior will be attached to the Player Character (PC), which exists in the form of the Cowboy Actor Type. This behavior will be used to handle much of the game logic, and will process the lion's share of player input. Creating a new Actor Behavior It's time to create our very first behavior! Go to the Dashboard, under the LOGIC heading, select Actor Behaviors: Click on This game contains no Logic. Click here to create one. to add your first behavior. You should see the Create New... window appear: Enter the Name Player Handler, as shown in the previous screenshot, then click Create. You will be taken to the Behavior Designer: Let's take a moment to examine the various areas within the Behavior Designer. From left to right, as demonstrated in the previous screenshot, we have: The Events Pane: Here we can add, remove, and move between events in our Behavior. The Canvas: To the center of the screen, the Canvas is where we drag blocks around to click our game logic together. The blocks Palette: This is where we can find any and all of the various logic blocks that Stencyl has on offer. Simply browse to your category of choice, then click and drag the block onto the Canvas to configure it. Follow these steps: Click the Add Event button, which can be found at the very top of the Events Pane. In the menu that ensues, browse to Basics and click When Updating: You will notice that we now have an Event in our Events Pane, called Updated, along with a block called always on our Canvas. In Stencyl events lingo, always is synonymous with When Updating: Since this is the only event in our Behavior at this time, it will be selected by default. The always block (yellow with a red flag) is where we put the game logic that needs to be checked on a constant basis, for every update of the game loop (this will be commensurate with the framerate at runtime, around 60fps, depending on the game performance and system specs). Before we proceed with the creation of our conditional logic, we must first create a few attributes. If you have a programming background, it is easiest to understand attributes as being synonymous to local variables. Just like variables, they have a set data type, and you can retrieve or change the value of an attribute in real-time. Creating Attributes Switch to the Attributes context in the blocks palette: There are currently no attributes associated with this behavior. Let's add some, as we'll need them to store important information of various types which we'll be using later on to craft the game mechanics. Click on the Create an Attribute button: In the Create an Attribute… window that appears, enter the Name Target Actor, set Type to Actor, check Hidden?, and press OK: Congratulations! If you look at the lower half of the blocks palette, you will see that you have added your first attribute, Target Actor, of type Actors, and it is now available for use in our code. Next, let's add five Boolean attributes. A Boolean is a special kind of attribute that can be set to either true, or false. Those are the only two values it can accept. First, let's create the Can Be Hurt Boolean: Click Create an Attribute…. Enter the Name Can Be Hurt. Change the Type to Boolean. Check Hidden?. Press OK to commit and add the attribute to the behavior. Repeat steps 1 through 5 for the remaining four Boolean attributes to be added, each time substituting the appropriate name:     Can Switch Anim     Draw Lasso     Lasso Back     Mouse Over If you have done this correctly, you should now see six attributes in your attributes list - one under Actor, and five under Boolean - as shown in the following screenshot: Now let's follow the same process to further create seven attributes; only this time, we'll set the Type for all of them to Number. The Name for each one will be: Health (Set to Hidden?). Impact Force (Set to Hidden?). Lasso Distance (Set to Hidden?). Max Health (Don't set to Hidden?). Turn Force (Don't set to Hidden?). X Point (Set to Hidden?). Y Point (Set to Hidden?). If all goes well, you should see your list of attributes update accordingly: We will add just one additional attribute. Click the Create an Attribute… button again: Name it Mouse State. Change its Type to Text. Do not hide this attribute. Click OK to commit and add the attribute to your behavior. Excellent work, at this point, you have created all of the attributes you will need for the Player Handler behavior! Custom events We need to create a few custom events in order to complete the code for this game prototype. For programmers, custom events are like functions that don't accept parameters. You simply trigger them at will to execute a reusable bunch of code. To accept parameters, you must create a custom block: Click Add Event. Browse to Advanced.. Select Custom Event: You will see that a second event, simply called Custom Event, has been added to our list of events: Now, double-click on the Custom Event in the events stack to change its label to Obj Click Check (For readability purposes, this does not affect the event's name in code, and is completely ignored by Stencyl): Now, let's set the name as it will be used in code. Click between When and happens, and insert the name ObjectClickCheck: From now on, whenever we want to call this custom event in our code, we will refer to it as ObjectClickCheck. Go back to the When Updating event by selecting it from the events stack on the left. We are going to add a special block to this event, which calls the custom event we created just a moment ago: In the blocks palette, go to Behaviour | Triggers | For Actor, then click and drag the highlighted block onto the canvas: Drop the selected block inside of the Always block, and fill in the fields as shown (please note that I have deliberately excluded the space between Player and Handler in the behavior name, so as to demonstrate the debugging workflow. This will be corrected in a later part of the article): Now, ObjectClickCheck will be executed for every iteration of the game loop! It is usually a good practice to split up your code like this, rather than having it all in one really long event. That would be confusing, and terribly hard to sift through when behaviors become more complex! Here is a chance to assess what you have learnt from this article thus far. We will create a second custom event; see if you can achieve this goal using only the skeleton guide mentioned next. If you struggle, simply refer back to the detailed steps we followed for the ObjectClickCheck event: Click Add Event | Advanced | Custom Event. A new event will appear at the bottom of the events pane. Double Click on the event in the events pane to change its label to Handle Dir Clicks for readability purposes. Between When and happens, enter the name HandleDirectionClicks. This is the handle we will use to refer to this event in code. Go back to the Updated event, right click on the Trigger event in behavior for self block that is already in the always block, and select copy from the menu. Right-click anywhere on the canvas and select paste from the menu to create an exact duplicate of the block. Change the event being triggered from ObjectClickCheck to HandleDirectionClicks. Keep the value PlayerHandler for the behavior field. Drag and drop the new block so that it sits immediately under the original. Holding Alt on the keyboard, and clicking and dragging on a block, creates a duplicate of that block. Were you successful? If so, you should see these changes and additions in your behavior (note that the order of the events in the events pane does not affect the game logic, or the order in which code is executed). Learning to create and utilize custom events in Stencyl is a huge step towards mastering the tool, so congratulations on having come this far! Testing and debugging As with all fields of programming and software development, it is important to periodically and iteratively test your code. It's much easier to catch and repair mistakes this way. On that note, let's test the code we've written so far, using print blocks. Browse to and select Flow | Debug | print from the blocks palette: Now, drag a copy of this block into both of your custom events, snapping it neatly into the when happens blocks as you do so. For the ObjectClickCheck event, type Object Click Detected into the print block For the HandleDirectionClicks event, type Directional Click Detected into the print block. We are almost ready to test our code. Since this is an Actor Behavior, however, and we have not yet attached it to our Cowboy actor, nothing would happen yet if we ran the code. We must also add an instance of the Cowboy actor to our scene: Click the Attach to Actor Type button to the top right of the blocks palette: Choose the Cowboy Actor from the ensuing list, and click OK to commit. Go back to the Dashboard, and open up the Level 1 scene. In the Palette to the right, switch from Tiles to Actors, and select the Cowboy actor: Ensure Layer 0 is selected (as actors cannot be placed on background layers). Click on the canvas to place an instance of the actor in the scene, then click on the Inspector, and change the x and y Scale of the actor to 0.8: Well done! You've just added your first behavior to an Actor Type, and added your first Actor Instance to a scene! We are now ready to test our code. First, Click the Log Viewer button on the toolbar: This will launch the Log Viewer. The Log Viewer will open up, at which point we need only set Platform to Flash (Player), and click the Test Game Button to compile and execute our code: After a few moments, if you have followed all of the steps correctly, you will see that the game windows opens on the screen and a number of events appear on the Log Viewer. However, none of these events have anything to do with the print blocks we added to our custom events. Hence, something has gone wrong, and must be debugged. What could it be? Well, since the blocks simply are not executing, it's likely a typo of some kind. Let's look at the Player Handler again, and you'll see that within the Updated event, we've referred to the behavior name as PlayerHandler in both trigger event blocks, with no space inserted between the words Player and Handler: Update both of these fields to Player Handler, and be sure to include the space this time, so that it looks like the following (To avoid a recurrence of this error, you may wish to use the dropdown menu by clicking the downwards pointing grey arrow, then selecting Behavior Names to choose your behavior from a comprehensive list): Great work! You have successfully completed your first bit of debugging in Stencyl. Click the Test Game button again. After the game window has opened, if you scroll down to the bottom of the Log Viewer, you should see the following events piling up: These INFO events are being triggered by the print blocks we inserted into our custom events, and prove that our code is now working. Excellent job! Let's move on to a new Actor; prepare to meet Dastardly Dan! Adding the Balloon Let's add the balloon actor to our game, and insert it into Level 1: Go to the Dashboard, and select Actor Types from the RESOURCES menu. Press Click here to create a new Actor Type. Name it Balloon, and click Create. Click on This Actor Type contains no animations. Click here to add an animation. Change the text in the Name field to Default. Un-check looping?. Press the Click here to add a frame. button. The Import Frame from Image Strip window appears. Change the Scale to 4x. Click Choose Image... then browse to Game AssetsGraphicsActor Animations and select Balloon.png. Keep Columns and Rows set to 1, and click Add to commit this frame to the animation. All animations are created with a box collision shape by default. In actuality, the Balloon actor requires no collisions at all, so let's remove it. Go to the Collision context, select the Default box, and press Delete on the keyboard: The Balloon Actor Type is now free of collision shapes, and hence will not interact physically with other elements of our game levels. Next, switch to the Physics context: Set the following attributes: Set What Kind of Actor Type? to Normal. Set Can Rotate? To No. This will disable all rotational physical forces and interactions. We can still rotate the actor by setting its rotation directly in the code, however. Set Affected by Gravity? to No. We will be handling the downward trajectory of this actor ourselves, without using the gravity implemented by the physics engine. Just before we add this new actor to Level 1, let's add a behavior or two. Switch to the Behaviors context: Then, follow these steps: This Actor Type currently has no attached behaviors. Click Add Behavior, at the bottom left hand corner of the screen: Under FROM YOUR LIBRARY, go to the Motion category, and select Always Simulate. The Always Simulate behavior will make this actor operational, even if it is not on the screen, which is a desirable result in this case. It also prevents Stencyl from deleting the actor when it leaves the scene, which it would automatically do in an effort to conserve memory, if we did not explicitly dictate otherwise. Click Choose to add it to the behaviors list for this Actor Type. You should see it appear in the list: Click Add Behavior again. This time, under FROM YOUR LIBRARY, go the Motion category once more, and this time select Wave Motion (you'll have to scroll down the list to see it). Click Choose to add it to the behavior stack. You should see it sitting under the Always Simulate behavior: Configuring prefab behaviors Prefab behaviors (also called shipped behaviors) enable us to implement some common functionality, without reinventing the wheel, so to speak. The great thing about these prefab behaviors, which can be found in the behavior library, is that they can be used as templates, and modified at will. Let's learn how to add and modify a couple of these prefab behaviors now. Some prefab behaviors have exposed attributes which can be configured to suit the needs of the project. The Wave Motion behavior is one such example. Select it from the stack, and configure the attributes as follows: Set Direction to Horizontal from the dropdown menu. Set Start Speed to 5. Set Amplitude to 64. Set Wavelength to 128. Fantastic! Now let's add an Instance of the Balloon actor to Level 1: Click the Add to Scene button at the top right corner of your view. Select the Level 1 scene. Select the Balloon. Click on the canvas, below the Cowboy actor, to place an instance of the Balloon in the scene: Modifying prefab behaviors Before we test the game one last time, we must quickly add a prefab behavior to the Cowboy Actor Type, modifying it slightly to suit the needs of this game (for instance, we will need to create an offset value for the y axis, so the PC is not always at the centre of the screen): Go to the Dashboard, and double click on the Cowboy from the Actor Types list. Switch to the Behavior Context. Click Add Behavior, as you did previously when adding prefab behaviors to the Balloon Actor Type. This time, under FROM YOUR LIBRARY, go to the Game category, and select Camera Follow. As the name suggests, this is a simple behavior that makes the camera follow the actor it is attached to. Click Choose to commit this behavior to the stack, and you should see this: Click the Edit Behavior button, and it will open up in the Behavior Designer: In the Behavior Designer, towards the bottom right corner of the screen, click on the Attributes tab: Once clicked, you will see a list of all the attributes in this behavior appear in the previous window. Click the Add Attribute button: Perform the following steps: Set the Name to Y Offset. Change the Type to Number. Leave the attribute unhidden. Click OK to commit new attribute to the attribute stack: We must modify the set IntendedCameraY block in both the Created and the Updated events: Holding Shift, click and drag the set IntendedCameraY block out onto the canvas by itself: Drag the y-center of Self block out like the following: Click the little downward pointing grey arrow at the right of the empty field in the set intendedCameraY block , and browse to Math | Arithmetic | Addition block: Drag the y-center of Self block into the left hand field of the Add block: Next, click the small downward pointing grey arrow to the right of the right hand field of the Addition block to bring up the same menu as before. This time, browse to Attributes, and select Y Offset: Now, right click on the whole block, and select Copy (this will copy it to the clipboard), then simply drag it back into its original position, just underneath set intendedCameraX: Switch to the Updated Event from the events pane on the left, hold Shift, then click and drag set intendedCameraY out of the Always block and drop it in the trash can, as you won't need it anymore. Right-click and select Paste to place a copy of the new block configuration you copied to the clipboard earlier: Click and drag the pasted block so that it appears just underneath the set intendedCameraX block, and save your changes: Testing the changes Go back to the Cowboy Actor Type, and open the Behavior context; click File | Reload Document (Ctrl-R or Cmd-R) to update all the changes. You should see a new configurable attribute for the Camera Follow Behavior, called Y Offset. Set its value to 70: Excellent! Now go back to the Dashboard and perform the following: Open up Level 1 again. Under Physics, set Gravity (Vertical) to 8.0. Click Test Game, and after a few moments, a new game window should appear. At this stage, what you should see is the Cowboy shooting down the hill with the camera following him, and the Balloon floating around above him. Summary In this article, we learned the basics of creating behaviors, adding and setting Attributes of various types, adding and modifying prefab behaviors, and even some rudimentary testing and debugging. Give yourself a pat on the back; you've learned a lot so far! Resources for Article: Further resources on this subject: Form Handling [article] Managing and Displaying Information [article] Background Animation [article]
Read more
  • 0
  • 0
  • 1920

article-image-creating-cool-content
Packt
23 Apr 2015
26 min read
Save for later

Creating Cool Content

Packt
23 Apr 2015
26 min read
In this article by Alex Ogorek, author of the book Mastering Cocos2d Game Development you'll be learning how to implement the really complex, subtle game mechanics that not many developers do. This is what separates the good games from the great games. There will be many examples, tutorials, and code snippets in this article intended for adaption in your own projects, so feel free to come back at any time to look at something you may have either missed the first time, or are just curious to know about in general. In this article, we will cover the following topics: Adding a table for scores Adding subtle sliding to the units Creating movements on a Bézier curve instead of straight paths (For more resources related to this topic, see here.) Adding a table for scores Because "we want a way to show the user their past high scores, in the GameOver scene, we're going to add a table that displays the most recent high scores that are saved. For this, we're going to use CCTableView. It's still relatively new, but it works for what we're going to use it. CCTableView versus UITableView Although UITableView might be known to some of you who've made non-Cocos2d apps before, you "should be aware of its downfalls when it comes to using it within Cocos2d. For example, if you want a BMFont in your table, you can't add LabelBMFont (you could try to convert the BMFont into a TTF font and use that within the table, but that's outside the scope of this book). If you still wish to use a UITableView object (or any UIKit element for that matter), you can create the object like normal, and add it to the scene, like this (tblScores is the name of the UITableView object): [[[CCDirector sharedDirector] view] addSubview:tblScores]; Saving high scores (NSUserDefaults) Before "we display any high scores, we have to make sure we save them. "The easiest way to do this is by making use of Apple's built-in data preservation tool—NSUserDefaults. If you've never used it before, let me tell you that it's basically a dictionary with "save" mechanics that stores the values in the device so that the next time the user loads the device, the values are available for the app. Also, because there are three different values we're tracking for each gameplay, let's only say a given game is better than another game when the total score is greater. Therefore, let's create a saveHighScore method that will go through all the total scores in our saved list and see whether the current total score is greater than any of the saved scores. If so, it will insert itself and bump the rest down. In MainScene.m, add the following method: -(NSInteger)saveHighScore { //save top 20 scores //an array of Dictionaries... //keys in each dictionary: // [DictTotalScore] // [DictTurnsSurvived] // [DictUnitsKilled]   //read the array of high scores saved on the user's device NSMutableArray *arrScores = [[[NSUserDefaults standardUserDefaults] arrayForKey:DataHighScores] mutableCopy]; //sentinel value of -1 (in other words, if a high score was not found on this play through) NSInteger index = -1; //loop through the scores in the array for (NSDictionary *dictHighScore in arrScores) { //if the current game's total score is greater than the score stored in the current index of the array...    if (numTotalScore > [dictHighScore[DictTotalScore] integerValue])    { //then store that index and break out of the loop      index = [arrScores indexOfObject:dictHighScore];      break;    } } //if a new high score was found if (index > -1) { //create a dictionary to store the score, turns survived, and units killed    NSDictionary *newHighScore = @{ DictTotalScore : @(numTotalScore),    DictTurnsSurvived : @(numTurnSurvived),    DictUnitsKilled : @(numUnitsKilled) };    //then insert that dictionary into the array of high scores    [arrScores insertObject:newHighScore atIndex:index];    //remove the very last object in the high score list (in other words, limit the number of scores)    [arrScores removeLastObject];    //then save the array    [[NSUserDefaults standardUserDefaults] setObject:arrScores forKey:DataHighScores];    [[NSUserDefaults standardUserDefaults] synchronize]; }   //finally return the index of the high score (whether it's -1 or an actual value within the array) return index; } Finally, call "this method in the endGame method right before you transition to the next scene: -(void)endGame { //call the method here to save the high score, then grab the index of the high score within the array NSInteger hsIndex = [self saveHighScore]; NSDictionary *scoreData = @{ DictTotalScore : @(numTotalScore), DictTurnsSurvived : @(numTurnSurvived), DictUnitsKilled : @(numUnitsKilled), DictHighScoreIndex : @(hsIndex)}; [[CCDirector sharedDirector] replaceScene:[GameOverScene sceneWithScoreData:scoreData]]; } Now that we have our high scores being saved, let's create the table to display them. Creating the table It's "really simple to set up a CCTableView object. All we need to do is modify the contentSize object, and then put in a few methods that handle the size and content of each cell. So first, open the GameOverScene.h file and set the scene as a data source for the CCTableView: @interface GameOverScene : CCScene <CCTableViewDataSource> Then, in the initWithScoreData method, create the header labels as well as initialize the CCTableView: //get the high score array from the user's device arrScores = [[NSUserDefaults standardUserDefaults] arrayForKey:DataHighScores];    //create labels CCLabelBMFont *lblTableTotalScore = [CCLabelBMFont labelWithString:@"Total Score:" fntFile:@"bmFont.fnt"];   CCLabelBMFont *lblTableUnitsKilled = [CCLabelBMFont labelWithString:@"Units Killed:" fntFile:@"bmFont.fnt"];   CCLabelBMFont *lblTableTurnsSurvived = [CCLabelBMFont labelWithString:@"Turns Survived:" fntFile:@"bmFont.fnt"]; //position the labels lblTableTotalScore.position = ccp(winSize.width * 0.5, winSize.height * 0.85); lblTableUnitsKilled.position = ccp(winSize.width * 0.675, winSize.height * 0.85); lblTableTurnsSurvived.position = ccp(winSize.width * 0.875, winSize.height * 0.85); //add the labels to the scene [self addChild:lblTableTurnsSurvived]; [self addChild:lblTableTotalScore]; [self addChild:lblTableUnitsKilled]; //create the tableview and add it to the scene CCTableView * tblScores = [CCTableView node]; tblScores.contentSize = CGSizeMake(0.6, 0.4); CGFloat ratioX = (1.0 - tblScores.contentSize.width) * 0.75; CGFloat ratioY = (1.0 - tblScores.contentSize.height) / 2; tblScores.position = ccp(winSize.width * ratioX, winSize.height * ratioY); tblScores.dataSource = self; tblScores.block = ^(CCTableView *table){    //if the press a cell, do something here.    //NSLog(@"Cell %ld", (long)table.selectedRow); }; [self addChild: tblScores]; With the CCTableView object's data source being set to self we can add the three methods that will determine exactly how our table looks and what data goes in each cell (that is, row). Note that if we don't set the data source, the table view's method will not be called; and if we set it to anything other than self, the methods will be called on that object/class instead. That being" said, add these three methods: -(CCTableViewCell*)tableView:(CCTableView *)tableView nodeForRowAtIndex:(NSUInteger)index { CCTableViewCell* cell = [CCTableViewCell node]; cell.contentSizeType = CCSizeTypeMake(CCSizeUnitNormalized, CCSizeUnitPoints); cell.contentSize = CGSizeMake(1, 40); // Color every other row differently CCNodeColor* bg; if (index % 2 != 0) bg = [CCNodeColor nodeWithColor:[CCColor colorWithRed:0 green:0 blue:0 alpha:0.3]]; else bg = [CCNodeColor nodeWithColor: [CCColor colorWithRed:0 green:0 blue:0 alpha:0.2]]; bg.userInteractionEnabled = NO; bg.contentSizeType = CCSizeTypeNormalized; bg.contentSize = CGSizeMake(1, 1); [cell addChild:bg]; return cell; }   -(NSUInteger)tableViewNumberOfRows:(CCTableView *)tableView { return [arrScores count]; }   -(float)tableView:(CCTableView *)tableView heightForRowAtIndex:(NSUInteger)index { return 40.f; } The first method, tableView:nodeForRowAtIndex:, will format each cell "based on which index it is. For now, we're going to color each cell in one of two different colors. The second method, tableViewNumberOfRows:, returns the number of rows, "or cells, that will be in the table view. Since we know there are going to be 20, "we can technically type 20, but what if we decide to change that number later? "So, let's stick with using the count of the array. The third "method, tableView:heightForRowAtIndex:, is meant to return the height of the row, or cell, at the given index. Since we aren't doing anything different with any cell in particular, we can hardcode this value to a fairly reasonable height of 40. At this point, you should be able to run the game, and when you lose, you'll be taken to the game over screen with the labels across the top as well as a table that scrolls on the right side of the screen. It's good practice when learning Cocos2d to just mess around with stuff to see what sort of effects you can make. For example, you could try using some ScaleTo actions to scale the text up from 0, or use a MoveTo action to slide it from the bottom or the side. Feel free to see whether you can create a cool way to display the text right now. Now that we have the table in place, let's get the data displayed, shall we? Showing the scores Now that "we have our table created, it's a simple addition to our code to get the proper numbers to display correctly. In the nodeForRowAtIndex method, add the following block of code right after adding the background color to the cell: //Create the 4 labels that will be used within the cell (row). CCLabelBMFont *lblScoreNumber = [CCLabelBMFont labelWithString: [NSString stringWithFormat:@"%d)", index+1] fntFile:@"bmFont.fnt"]; //Set the anchor point to the middle-right (default middle-middle) lblScoreNumber.anchorPoint = ccp(1,0.5); CCLabelBMFont *lblTotalScore = [CCLabelBMFont labelWithString:[NSString stringWithFormat:@"%d", [arrScores[index][DictTotalScore] integerValue]] fntFile:@"bmFont.fnt"];   CCLabelBMFont *lblUnitsKilled = [CCLabelBMFont labelWithString:[NSString stringWithFormat:@"%d", [arrScores[index][DictUnitsKilled] integerValue]] fntFile:@"bmFont.fnt"];   CCLabelBMFont *lblTurnsSurvived = [CCLabelBMFont labelWithString:[NSString stringWithFormat:@"%d", [arrScores[index][DictTurnsSurvived] integerValue]] fntFile:@"bmFont.fnt"]; //set the position type of each label to normalized (where (0,0) is the bottom left of its parent and (1,1) is the top right of its parent) lblScoreNumber.positionType = lblTotalScore.positionType = lblUnitsKilled.positionType = lblTurnsSurvived.positionType = CCPositionTypeNormalized;   //position all of the labels within the cell lblScoreNumber.position = ccp(0.15,0.5); lblTotalScore.position = ccp(0.35,0.5); lblUnitsKilled.position = ccp(0.6,0.5); lblTurnsSurvived.position = ccp(0.9,0.5); //if the index we're iterating through is the same index as our High Score index... if (index == highScoreIndex) { //then set the color of all the labels to a golden color    lblScoreNumber.color =    lblTotalScore.color =    lblUnitsKilled.color =    lblTurnsSurvived.color = [CCColor colorWithRed:1 green:183/255.f blue:0]; } //add all of the labels to the individual cell [cell addChild:lblScoreNumber]; [cell addChild:lblTurnsSurvived]; [cell addChild:lblTotalScore]; [cell addChild:lblUnitsKilled]; And that's it! When you play the game and end up at the game over screen, you'll see the high scores being displayed (even the scores from earlier attempts, because they were saved, remember?). Notice the high score that is yellow. It's an indication that the score you got in the game you just played is on the scoreboard, and shows you where it is. Although the CCTableView might feel a bit weird with things disappearing and reappearing as you scroll, let's get some Threes!—like sliding into our game. If you're considering adding a CCTableView to your own project, the key takeaway here is to make sure you modify the contentSize and position properly. By default, the contentSize is a normalized CGSize, so from 0 to 1, and the anchor point is (0,0). Plus, make sure you perform these two steps: Set the data source of the table view Add the three table view methods With all that in mind, it should be relatively easy to implement a CCTableView. Adding subtle sliding to the units If you've ever played Threes! (or if you haven't, check out the trailer at http://asherv.com/threes/, and maybe even download the game on your phone), you would be aware of the sliding feature when a user begins to make "their move but hasn't yet completed the move. At the speed of the dragging finger, the units slide in the direction they're going to move, showing the user where each unit will go and how each unit will combine with another. This is useful as it not only adds that extra layer of "cool factor" but also provides a preview of the future for the user if they want to revert their decision ahead of time and make a different, more calculated move. Here's a side note: if you want your game to go really viral, you have to make the user believe it was their fault that they lost, and not your "stupid game mechanics" (as some players might say). Think Angry Birds, Smash Hit, Crossy Road, Threes!, Tiny Wings… the list goes on and on with more games that became popular, and all had one underlying theme: when the user loses, it was entirely in their control to win or lose, and they made the wrong move. This" unseen mechanic pushes players to play again with a better strategy in mind. And this is exactly why we want our users to see their move before it gets made. It's a win-win situation for both the developers and the players. Sliding one unit If we can get one unit to slide, we can surely get the rest of the units to slide by simply looping through them, modularizing the code, or some other form of generalization. That being said, we need to set up the Unit class so that it can detect how far "the finger has dragged. Thus, we can determine how far to move the unit. So, "open Unit.h and add the following variable. It will track the distance from the previous touch position: @property (nonatomic, assign) CGPoint previousTouchPos; Then, in the touchMoved method of Unit.m, add the following assignment to previousTouchPos. It sets the previous touch position to the touch-down position, but only after the distance is greater than 20 units: if (!self.isBeingDragged && ccpDistance(touchPos, self.touchDownPos) > 20) { self.isBeingDragged = YES; //add it here: self.previousTouchPos = self.touchDownPos; Once that's in place, we can begin calculating the distance while the finger is being dragged. To do that, we'll do a simple check. Add the following block of code at the end of touchMoved, after the end of the initial if block: //only if the unit is currently being dragged if (self.isBeingDragged) {    CGFloat dist = 0;    //if the direction the unit is being dragged is either UP or "     DOWN    if (self.dragDirection == DirUp || self.dragDirection == DirDown)    //then subtract the current touch position's Y-value from the "     previously-recorded Y-value to determine the distance to "     move      dist = touchPos.y - self.previousTouchPos.y;      //else if the direction the unit is being dragged is either "       LEFT or RIGHT    else if (self.dragDirection == DirLeft ||        self.dragDirection == DirRight)        //then subtract the current touch position's Y-value from "         the previously-recorded Y-value to determine the "         distance to move      dist = touchPos.x - self.previousTouchPos.x;   //then assign the touch position for the next iteration of touchMoved to work properly self.previousTouchPos = touchPos;   } The "assignment of previousTouchPos at the end will ensure that while the unit is being dragged, we continue to update the touch position so that we can determine the distance. Plus, the distance is calculated in only the direction in which the unit is being dragged (up and down are denoted by Y, and left and right are denoted by X). Now that we have the distance between finger drags being calculated, let's push "this into a function that will move our unit based on which direction it's being dragged in. So, right after you've calculated dist in the previous code block, "call the following method to move our unit based on the amount dragged: dist /= 2; //optional [self slideUnitWithDistance:dist "withDragDirection:self.dragDirection]; Dividing the distance by 2 is optional. You may think the squares are too small, and want the user to be able to see their square. So note that dividing by 2, or a larger number, will mean that for every 1 point the finger moves, the unit will move by 1/2 (or less) points. With that method call being ready, we need to implement it, so add the following method body for now. Since this method is rather complicated, it's going to be "added in parts: -(void)slideUnitWithDistance:(CGFloat)dist withDragDirection:(enum UnitDirection)dir { } The first thing "we need to do is set up a variable to calculate the new x and y positions of the unit. We'll call these newX and newY, and set them to the unit's current position: CGFloat newX = self.position.x, newY = self.position.y; Next, we want to grab the position that the unit starts at, that is, the position the "unit would be at if it was positioned at its current grid coordinate. To do that, "we're going to call the getPositionForGridCoordinate method from MainScene, (since that's where the positions are being calculated anyway, we might as well use that function): CGPoint originalPos = [MainScene "getPositionForGridCoord:self.gridPos]; Next, we're going to move the newX or newY based on the direction in which the unit is being dragged. For now, let's just add the up direction: if (self.dragDirection == DirUp) {    newY += dist;    if (newY > originalPos.y + self.gridWidth)      newY = originalPos.y + self.gridWidth;    else if (newY < originalPos.y)      newY = originalPos.y; } In this if block, we're first going to add the distance to the newY variable "(because we're going up, we're adding to Y instead of X). Then, we want to "make sure the position is at most 1 square up. We're going to use the gridWidth (which is essentially the width of the square, assigned in the initCommon method). Also, we need to make sure that if they're bringing the square back to its original position, it doesn't go into the square beneath it. So let's add the rest of the directions as else if statements: else if (self.dragDirection == DirDown) {    newY += dist;    if (newY < originalPos.y - self.gridWidth)      newY = originalPos.y - self.gridWidth;    else if (newY > originalPos.y)      newY = originalPos.y; } else if (self.dragDirection == DirLeft) {    newX += dist;    if (newX < originalPos.x - self.gridWidth)      newX = originalPos.x - self.gridWidth;    else if (newX > originalPos.x)      newX = originalPos.x; } else if (self.dragDirection == DirRight) {    newX += dist;    if (newX > originalPos.x + self.gridWidth)      newX = originalPos.x + self.gridWidth;    else if (newX < originalPos.x)      newX = originalPos.x; } Finally, we "will set the position of the unit based on the newly calculated "x and y positions: self.position = ccp(newX, newY); Running the game at this point should cause the unit you drag to slide along "with your finger. Nice, huh? Since we have a function that moves one unit, "we can very easily alter it so that every unit can be moved like this. But first, there's something you've probably noticed a while ago (or maybe just recently), and that's the unit movement being canceled only when you bring your finger back to the original touch down position. Because we're dragging the unit itself, we can "cancel" the move by dragging the unit back to where it started. However, the finger might be in a completely different position, so we need to modify how the cancelling gets determined. To do that, in your touchEnded method of Unit.m, locate this if statement: if (ccpDistance(touchPos, self.touchDownPos) > "self.boundingBox.size.width/2) Change it to the following, which will determine the unit's distance, and not the finger's distance: CGPoint oldSelfPos = [MainScene "getPositionForGridCoord:self.gridPos];   CGFloat dist = ccpDistance(oldSelfPos, self.position); if (dist > self.gridWidth/2) Yes, this means you no longer need the touchPos variable in touchEnded if "you're getting that "warning and wish to get rid of it. But that's it for sliding 1 unit. Now we're ready to slide all the units, so let's do it! Sliding all units Now "that we have the dragging unit being slid, let's continue and make all the units slide (even the enemy units so that we can better predict our troops' movement). First, we need a way to move all the units on the screen. However, since the Unit class only contains information about the individual unit (which is a good thing), "we need to call a method in MainScene, since that's where the arrays of units are. Moreover, we cannot simply call [MainScene method], since the arrays are "instance variables, and instance variables must be accessed through an instance "of the object itself. That being said, because we know that our unit will be added to the scene as "a child, we can use Cocos2d to our advantage, and call an instance method on the MainScene class via the parent parameter. So, in touchMoved of Unit.m, make the following change: [(MainScene*)self.parent slideAllUnitsWithDistance:dist "withDragDirection:self.dragDirection]; //[self slideUnitWithDistance:dist "withDragDirection:self.dragDirection]; Basically we've commented out (or deleted) the old method call here, and instead called it on our parent object (which we cast as a MainScene so that we know "which functions it has). But we don't have that method created yet, so in MainScene.h, add the following method declaration: -(void)slideAllUnitsWithDistance:(CGFloat)dist "withDragDirection:(enum UnitDirection)dir; Just in case you haven't noticed, the enum UnitDirection is declared in Unit.h, which is why MainScene.h imports Unit.h—so that we can make use of that enum in this class, and the function to be more specific. Then in MainScene.m, we're going to loop through both the friendly and enemy arrays, and call the slideUnitWithDistance function on each individual unit: -(void)slideAllUnitsWithDistance:(CGFloat)dist "withDragDirection:(enum UnitDirection)dir { for (Unit *u in arrFriendlies)    [u slideUnitWithDistance:dist withDragDirection:dir]; for (Unit *u in arrEnemies)    [u slideUnitWithDistance:dist withDragDirection:dir]; } However, that" still isn't functional, as we haven't declared that function in the "header file for the Unit class. So go ahead and do that now. Declare the function header in Unit.h: -(void)slideUnitWithDistance:(CGFloat)dist withDragDirection:(enum "UnitDirection)dir; We're almost done. We initially set up our slideUnitWithDistance method with a drag direction in mind. However, only the unit that's currently being dragged will have a drag direction. Every other unit will need to use the direction it's currently facing "(that is, the direction in which it's already going). To do that, we just need to modify how the slideUnitWithDistance method does its checking to determine which direction to modify the distance by. But first, we need to handle the negatives. What does that mean? Well, if you're dragging a unit to the left and a unit being moved is supposed to be moving to the left, it will work properly, as x-10 (for example) will still be less than the grid's width. However, if you're dragging left and a unit being moved is supposed to be moving right, it won't be moving at all, as it tries to add a negative value x -10, but because it needs to be moving to the right, it'll encounter the left-bound right away (of less than the original position), and stay still. The following diagram should help explain what is meant by "handling negatives." As you can see, in the top section, when the non-dragged unit is supposed to be going left by 10 (in other words, negative 10 in the x direction), it works. But when the non-dragged unit is going the opposite sign (in other words, positive 10 in the x direction), it doesn't. To" handle this, we set up a pretty complicated if statement. It checks when the drag direction and the unit's own direction are opposite (positive versus negative), and multiplies the distance by -1 (flips it). Add this to the top of the slideUnitWithDistance method, right after you grab the newX and the original position: -(void)slideUnitWithDistance:(CGFloat)dist withDragDirection:(enum UnitDirection)dir { CGFloat newX = self.position.x, newY = self.position.y; CGPoint originalPos = [MainScene getPositionForGridCoord:self.gridPos]; if (!self.isBeingDragged &&   (((self.direction == DirUp || self.direction == DirRight) && (dir == DirDown || dir == DirLeft)) ||   ((self.direction == DirDown || self.direction == DirLeft) && (dir == DirUp || dir == DirRight)))) {    dist *= -1; } } The logic of this if statement works is as follows: Suppose the unit is not being dragged. Also suppose that either the direction is positive and the drag direction is negative, or the direction is negative and the drag direction is positive. Then multiply by -1. Finally, as mentioned earlier, we just need to handle the non-dragged units. So, in every if statement, add an "or" portion that will check for the same direction, but only if the unit is not currently being dragged. In other words, in the slideUnitWithDistance method, modify your if statements to look like this: if (self.dragDirection == DirUp || (!self.isBeingDragged && self.direction == DirUp)) {} else if (self.dragDirection == DirDown || (!self.isBeingDragged && self.direction == DirDown)) {} else if (self.dragDirection == DirLeft || (!self.isBeingDragged && self.direction == DirLeft)) {} else if (self.dragDirection == DirRight || (!self.isBeingDragged && self.direction == DirRight)) {} Finally, we can run the game. Bam! All the units go gliding across the screen with our drag. Isn't it lovely? Now the player can better choose their move. That's it for the sliding portion. The key to unit sliding is to loop through the arrays to ensure that all the units get moved by an equal amount, hence passing the distance to the move function. Creating movements on a Bézier curve If you don't know what a Bézier curve is, it's basically a line that goes from point A to point B over a curve. Instead of being a straight line with two points, it uses a second set of points called control points that bend the line in a smooth way. When you want to apply movement with animations in Cocos2d, it's very tempting to queue up a bunch of MoveTo actions in a sequence. However, it's going to look a lot nicer ( in both the game and the code) if you use a smoother Bézier curve animation. Here's a good example of what a Bézier curve looks like: As you can see, the red line goes from point P0 to P3. However, the line is influenced in the direction of the control points, P1 and P2. Examples of using a Bézier curve Let's list" a few examples where it would be a good choice to use a Bézier curve instead of just the regular MoveTo or MoveBy actions: A character that will perform a jumping animation, for example, in Super Mario Bros A boomerang as a weapon that the player throws Launching a missile or rocket and giving it a parabolic curve A tutorial hand that indicates a curved path the user must make with their finger A skateboarder on a half-pipe ramp (if not done with Chipmunk) There are obviously a lot of other examples that could use a Bézier curve for their movement. But let's actually code one, shall we? Sample project – Bézier map route First, to make things go a lot faster—as this isn't going to be part of the book's project—simply download the project from the code repository or the website. If you open the project and run it on your device or a simulator, you will notice a blue screen and a square in the bottom-left corner. If you tap anywhere on the screen, you'll see the blue square make an M shape ending in the bottom-right corner. If you hold your finger, it will repeat. Tap again and the animation will reset. Imagine the path this square takes is over a map, and indicates what route a player will travel with their character. This is a very choppy, very sharp path. Generally, paths are curved, so let's make one that is! Here is a screenshot that shows a very straight path of the blue square: The following screenshot shows the Bézier path of the yellow square: Curved M-shape Open MainScene.h and add another CCNodeColor variable, named unitBezier: CCNodeColor *unitBezier; Then open MainScene.m and add the following code to the init method so that your yellow block shows up on the screen: unitBezier = [[CCNodeColor alloc] initWithColor:[CCColor colorWithRed:1 green:1 blue:0] width:50 height:50]; [self addChild:unitBezier]; CCNodeColor *shadow2 = [[CCNodeColor alloc] initWithColor:[CCColor blackColor] width:50 height:50]; shadow2.anchorPoint = ccp(0.5,0.5); shadow2.position = ccp(26,24); shadow2.opacity = 0.5; [unitBezier addChild:shadow2 z:-1]; Then, in the sendFirstUnit method, add the lines of code that will reset the yellow block's position as well as queue up the method to move the yellow block: -(void)sendFirstUnit { unitRegular.position = ccp(0,0); //Add these 2 lines unitBezier.position = ccp(0,0); [self scheduleOnce:@selector(sendSecondUnit) delay:2]; CCActionMoveTo *move1 = [CCActionMoveTo actionWithDuration:0.5 "position:ccp(winSize.width/4, winSize.height * 0.75)]; CCActionMoveTo *move2 = [CCActionMoveTo actionWithDuration:0.5 "position:ccp(winSize.width/2, winSize.height/4)]; CCActionMoveTo *move3 = [CCActionMoveTo actionWithDuration:0.5 "position:ccp(winSize.width*3/4, winSize.height * 0.75)]; CCActionMoveTo *move4 = [CCActionMoveTo actionWithDuration:0.5 "position:ccp(winSize.width - 50, 0)]; [unitRegular runAction:[CCActionSequence actions:move1, move2, "move3, move4, nil]]; } After this, you'll need to actually create the sendSecondUnit method, like this: -(void)sendSecondUnit { ccBezierConfig bezConfig1; bezConfig1.controlPoint_1 = ccp(0, winSize.height); bezConfig1.controlPoint_2 = ccp(winSize.width*3/8, "winSize.height); bezConfig1.endPosition = ccp(winSize.width*3/8, "winSize.height/2); CCActionBezierTo *bez1 = [CCActionBezierTo "actionWithDuration:1.0 bezier:bezConfig1]; ccBezierConfig bezConfig2; bezConfig2.controlPoint_1 = ccp(winSize.width*3/8, 0); bezConfig2.controlPoint_2 = ccp(winSize.width*5/8, 0); bezConfig2.endPosition = ccp(winSize.width*5/8, winSize.height/2); CCActionBezierBy *bez2 = [CCActionBezierTo "actionWithDuration:1.0 bezier:bezConfig2]; ccBezierConfig bezConfig3; bezConfig3.controlPoint_1 = ccp(winSize.width*5/8, "winSize.height); bezConfig3.controlPoint_2 = ccp(winSize.width, winSize.height); bezConfig3.endPosition = ccp(winSize.width - 50, 0); CCActionBezierTo *bez3 = [CCActionBezierTo "actionWithDuration:1.0 bezier:bezConfig3]; [unitBezier runAction:[CCActionSequence actions:bez1, bez2, bez3, nil]]; } The preceding method creates three Bézier configurations and attaches them to a MoveTo command that takes a Bézier configuration. The reason for this is that each Bézier configuration can take only two control points. As you can see in this marked-up screenshot, where each white and red square represents a control point, you can make only a U-shaped parabola with a single Bézier configuration. Thus, to make three U-shapes, you need three Bézier configurations. Finally, make sure that in the touchBegan method, you make the unitBezier stop all its actions (that is, stop on reset): [unitBezier stopAllActions]; And that's it! When you run the project and tap on the screen (or tap and hold), you'll see the blue square M-shape its way across, followed by the yellow square in its squiggly M-shape. If you" want to adapt the Bézier MoveTo or MoveBy actions for your own project, you should know that you can create only one U-shape with each Bézier configuration. They're fairly easy to implement and can quickly be copied and pasted, as shown in the sendSecondUnit function. Plus, as the control points and end position are just CGPoint values, they can be relative (that is, relative to the unit's current position, the world's position, or an enemy's position), and as a regular CCAction, they can be run with any CCNode object quite easily. Summary In this article, you learned how to do a variety of things, from making a score table and previewing the next move, to making use of Bézier curves. The code was built with a copy-paste mindset, so it can be adapted for any project without much reworking (if it is required at all). Resources for Article: Further resources on this subject: Cocos2d-x: Installation [article] Dragging a CCNode in Cocos2D-Swift [article] Animations in Cocos2d-x [article]
Read more
  • 0
  • 0
  • 1526
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-creating-games-cocos2d-x-easy-and-100-percent-free-0
Packt
01 Apr 2015
5 min read
Save for later

Creating Games with Cocos2d-x is Easy and 100 percent Free

Packt
01 Apr 2015
5 min read
In this article by Raydelto Hernandez, the author of the book Building Android games with Cocos2d-x, we will talk about the Cocos2d-x game engine, which is widely used to create Android games. The launch of the Apple App Store back in 2008 leveraged the reach capacity of indie game developers who since its occurrence are able to reach millions of users and compete with large companies, outperforming them in some situations. This reality led the trend of creating reusable game engines, such as Cocos2d-iPhone, which is written natively using Objective-C by the Argentine iPhone developer, Ricardo Quesada. Cocos2d-iPhone allowed many independent developers to reach the top charts of downloads. (For more resources related to this topic, see here.) Picking an existing game engine is a smart choice for indies and large companies since it allows them to focus on the game logic rather than rewriting core features over and over again. Thus, there are many game engines out there with all kinds of licenses and characteristics. The most popular game engines for mobile systems right now are Unity, Marmalade, and Cocos2d-x; the three of them have the capabilities to create 2D and 3D games. Determining which one is the best in terms of ease of use and availability of tools may be debatable, but there is one objective fact, which we can mention that could be easily verified. Among these three engines, Cocos2d-x is the only one that you can use for free no matter how much money you make using it. We highlighted in this article's title that Cocos2d-x is completely free. This was emphasized because the other two frameworks also allow some free usage; nevertheless, both of these at some point require a payment for the usage license. In order to understand why Cocos2d-x is still free and open source, we need to understand how this tool was born. Ricardo, an enthusiastic Python programmer, often participated in game creation challenges that required participants to develop games from scratch within a week. Back in those days, Ricardo and his team rewrote the core engine for each game until they came up with the idea of creating a framework to encapsulate core game capabilities. These capabilities could be used on any two-dimensional game to make it open source, so contributions could be received worldwide. This is why Cocos2d was originally written for fun. With the launch of the first iPhone in 2007, Ricardo led the development of the port of the Cocos2d Python framework to the iPhone platform using its native language, Objective-C. Cocos2d-iPhone quickly became popular among indie game developers, some of them turning into Appillionaires, as Chris Stevens called these individuals and enterprises that made millions of dollars during the App Store bubble period. This phenomenon made game development companies look at this framework created by hobbyists as a tool to create their products. Zynga was one of the first big companies to adopt Cocos2d as their framework to deliver their famous Farmville game to iPhone in 2009. This company has been trading on NASDAQ since 2011 and has more than 2,000 employees. In July 2010, a C++ port of the Cocos2d iPhone called Cocos2d-x, was written in China with the objective of taking the power of this framework to other platforms, such as the Android operating system, which by that time was gaining market share at a spectacular rate. In 2011, this Cocos2d port was acquired by Chukong Technologies, the third largest mobile game development company in China, who later hired the original Cocos2d-IPhone author to join their team. Today, Cocos2d-x-based games dominate the top grossing charts of Google Play and the App Store, especially in Asia. Recognized companies and leading studios, such as Konami, Zynga, Bandai Namco, Wooga, Disney Mobile, and Square Enix are using Cocos2d-x in their games. Currently, there are 400,000 developers working on adding new functionalities and making this framework as stable as possible. These include engineers from Google, ARM, Intel, BlackBerry, and Microsoft who officially support the ports of their products, such as Windows Phone, Windows, Windows Metro Interface, and they're planning to support Cocos2d-x for the Xbox in this year. Cocos2d-x is a very straightforward engine that requires a little learning to grasp it. I teach game development courses at many universities using this framework; during the first week, the students are capable of creating a game with the complexity of the famous title Doodle Jump. This can be easily achieved because the framework provides us all the single components that are required for our game, such as physics, audio handling, collision detection, animation, networking, data storage, user input, map rendering, scene transitions, 3D rendering, particle systems rendering, font handling, menu creation, displaying forms, threads handling, and so on. This abstracts us from the low-level logic and allows us to focus on the game logic. Summary In conclusion, if you are willing to learn how to develop games for mobile platforms, I strongly recommend you to learn and use the Cocos2d-x framework because it is easy to use, is totally free, is an open source. This means that you can better understand it by reading its source, you could modify it if needed, and you have the warranty that you will never be forced to pay a license fee if your game becomes a hit. Another big advantage of this framework is its highly available documentation, including the Packt Publishing collection of Cocos2d-x game development books. Resources for Article: Further resources on this subject: Moving the Space Pod Using Touch [article] Why should I make cross-platform games? [article] Animations in Cocos2d-x [article]
Read more
  • 0
  • 0
  • 2369

article-image-fun-sprites-sky-defense
Packt
25 Mar 2015
35 min read
Save for later

Fun with Sprites – Sky Defense

Packt
25 Mar 2015
35 min read
This article is written by Roger Engelbert, the author of Cocos2d-x by Example: Beginner's Guide - Second Edition. Time to build our second game! This time, you will get acquainted with the power of actions in Cocos2d-x. I'll show you how an entire game could be built just by running the various action commands contained in Cocos2d-x to make your sprites move, rotate, scale, fade, blink, and so on. And you can also use actions to animate your sprites using multiple images, like in a movie. So let's get started. In this article, you will learn: How to optimize the development of your game with sprite sheets How to use bitmap fonts in your game How easy it is to implement and run actions How to scale, rotate, swing, move, and fade out a sprite How to load multiple .png files and use them to animate a sprite How to create a universal game with Cocos2d-x (For more resources related to this topic, see here.) The game – sky defense Meet our stressed-out city of...your name of choice here. It's a beautiful day when suddenly the sky begins to fall. There are meteors rushing toward the city and it is your job to keep it safe. The player in this game can tap the screen to start growing a bomb. When the bomb is big enough to be activated, the player taps the screen again to detonate it. Any nearby meteor will explode into a million pieces. The bigger the bomb, the bigger the detonation, and the more meteors can be taken out by it. But the bigger the bomb, the longer it takes to grow it. But it's not just bad news coming down. There are also health packs dropping from the sky and if you allow them to reach the ground, you'll recover some of your energy. The game settings This is a universal game. It is designed for the iPad retina screen and it will be scaled down to fit all the other screens. The game will be played in landscape mode, and it will not need to support multitouch. The start project The command line I used was: cocos new SkyDefense -p com.rengelbert.SkyDefense -l cpp -d /Users/rengelbert/Desktop/SkyDefense In Xcode you must set the Devices field in Deployment Info to Universal, and the Device Family field is set to Universal. And in RootViewController.mm, the supported interface orientation is set to Landscape. The game we are going to build requires only one class, GameLayer.cpp, and you will find that the interface for this class already contains all the information it needs. Also, some of the more trivial or old-news logic is already in place in the implementation file as well. But I'll go over this as we work on the game. Adding screen support for a universal app Now things get a bit more complicated as we add support for smaller screens in our universal game, as well as some of the most common Android screen sizes. So open AppDelegate.cpp. Inside the applicationDidFinishLaunching method, we now have the following code: auto screenSize = glview->getFrameSize(); auto designSize = Size(2048, 1536); glview->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::EXACT_FIT); std::vector<std::string> searchPaths; if (screenSize.height > 768) {    searchPaths.push_back("ipadhd");    director->setContentScaleFactor(1536/designSize.height); } else if (screenSize.height > 320) {    searchPaths.push_back("ipad");    director->setContentScaleFactor(768/designSize.height); } else {    searchPaths.push_back("iphone");    director->setContentScaleFactor(380/designSize.height); } auto fileUtils = FileUtils::getInstance(); fileUtils->setSearchPaths(searchPaths); Once again, we tell our GLView object (our OpenGL view) that we designed the game for a certain screen size (the iPad retina screen) and once again, we want our game screen to resize to match the screen on the device (ResolutionPolicy::EXACT_FIT). Then we determine where to load our images from, based on the device's screen size. We have art for iPad retina, then for regular iPad which is shared by iPhone retina, and for the regular iPhone. We end by setting the scale factor based on the designed target. Adding background music Still inside AppDelegate.cpp, we load the sound files we'll use in the game, including a background.mp3 (courtesy of Kevin MacLeod from incompetech.com), which we load through the command: auto audioEngine = SimpleAudioEngine::getInstance(); audioEngine->preloadBackgroundMusic(fileUtils->fullPathForFilename("background.mp3").c_str()); We end by setting the effects' volume down a tad: //lower playback volume for effects audioEngine->setEffectsVolume(0.4f); For background music volume, you must use setBackgroundMusicVolume. If you create some sort of volume control in your game, these are the calls you would make to adjust the volume based on the user's preference. Initializing the game Now back to GameLayer.cpp. If you take a look inside our init method, you will see that the game initializes by calling three methods: createGameScreen, createPools, and createActions. We'll create all our screen elements inside the first method, and then create object pools so we don't instantiate any sprite inside the main loop; and we'll create all the main actions used in our game inside the createActions method. And as soon as the game initializes, we start playing the background music, with its should loop parameter set to true: SimpleAudioEngine::getInstance()-   >playBackgroundMusic("background.mp3", true); We once again store the screen size for future reference, and we'll use a _running Boolean for game states. If you run the game now, you should only see the background image: Using sprite sheets in Cocos2d-x A sprite sheet is a way to group multiple images together in one image file. In order to texture a sprite with one of these images, you must have the information of where in the sprite sheet that particular image is found (its rectangle). Sprite sheets are often organized in two files: the image one and a data file that describes where in the image you can find the individual textures. I used TexturePacker to create these files for the game. You can find them inside the ipad, ipadhd, and iphone folders inside Resources. There is a sprite_sheet.png file for the image and a sprite_sheet.plist file that describes the individual frames inside the image. This is what the sprite_sheet.png file looks like: Batch drawing sprites In Cocos2d-x, sprite sheets can be used in conjunction with a specialized node, called SpriteBatchNode. This node can be used whenever you wish to use multiple sprites that share the same source image inside the same node. So you could have multiple instances of a Sprite class that uses a bullet.png texture for instance. And if the source image is a sprite sheet, you can have multiple instances of sprites displaying as many different textures as you could pack inside your sprite sheet. With SpriteBatchNode, you can substantially reduce the number of calls during the rendering stage of your game, which will help when targeting less powerful systems, though not noticeably in more modern devices. Let me show you how to create a SpriteBatchNode. Time for action – creating SpriteBatchNode Let's begin implementing the createGameScreen method in GameLayer.cpp. Just below the lines that add the bg sprite, we instantiate our batch node: void GameLayer::createGameScreen() {   //add bg auto bg = Sprite::create("bg.png"); ...   SpriteFrameCache::getInstance()-> addSpriteFramesWithFile("sprite_sheet.plist"); _gameBatchNode = SpriteBatchNode::create("sprite_sheet.png"); this->addChild(_gameBatchNode); In order to create the batch node from a sprite sheet, we first load all the frame information described by the sprite_sheet.plist file into SpriteFrameCache. And then we create the batch node with the sprite_sheet.png file, which is the source texture shared by all sprites added to this batch node. (The background image is not part of the sprite sheet, so it's added separately before we add _gameBatchNode to GameLayer.) Now we can start putting stuff inside _gameBatchNode. First, the city: for (int i = 0; i < 2; i++) { auto sprite = Sprite::createWithSpriteFrameName   ("city_dark.png");    sprite->setAnchorPoint(Vec2(0.5,0)); sprite->setPosition(_screenSize.width * (0.25f + i * 0.5f),0)); _gameBatchNode->addChild(sprite, kMiddleground); sprite = Sprite::createWithSpriteFrameName ("city_light.png"); sprite->setAnchorPoint(Vec2(0.5,0)); sprite->setPosition(Vec2(_screenSize.width * (0.25f + i * 0.5f), _screenSize.height * 0.1f)); _gameBatchNode->addChild(sprite, kBackground); } Then the trees: //add trees for (int i = 0; i < 3; i++) { auto sprite = Sprite::createWithSpriteFrameName("trees.png"); sprite->setAnchorPoint(Vec2(0.5f, 0.0f)); sprite->setPosition(Vec2(_screenSize.width * (0.2f + i * 0.3f),0)); _gameBatchNode->addChild(sprite, kForeground);   } Notice that here we create sprites by passing it a sprite frame name. The IDs for these frame names were loaded into SpriteFrameCache through our sprite_sheet.plist file. The screen so far is made up of two instances of city_dark.png tiling at the bottom of the screen, and two instances of city_light.png also tiling. One needs to appear on top of the other and for that we use the enumerated values declared in GameLayer.h: enum { kBackground, kMiddleground, kForeground }; We use the addChild( Node, zOrder) method to layer our sprites on top of each other, using different values for their z order. So for example, when we later add three sprites showing the trees.png sprite frame, we add them on top of all previous sprites using the highest value for z that we find in the enumerated list, which is kForeground. Why go through the trouble of tiling the images and not using one large image instead, or combining some of them with the background image? Because I wanted to include the greatest number of images possible inside the one sprite sheet, and have that sprite sheet to be as small as possible, to illustrate all the clever ways you can use and optimize sprite sheets. This is not necessary in this particular game. What just happened? We began creating the initial screen for our game. We are using a SpriteBatchNode to contain all the sprites that use images from our sprite sheet. So SpriteBatchNode behaves as any node does—as a container. And we can layer individual sprites inside the batch node by manipulating their z order. Bitmap fonts in Cocos2d-x The Cocos2d-x Label class has a static create method that uses bitmap images for the characters. The bitmap image we are using here was created with the program GlyphDesigner, and in essence, it works just as a sprite sheet does. As a matter of fact, Label extends SpriteBatchNode, so it behaves just like a batch node. You have images for all individual characters you'll need packed inside a PNG file (font.png), and then a data file (font.fnt) describing where each character is. The following screenshot shows how the font sprite sheet looks like for our game: The difference between Label and a regular SpriteBatchNode class is that the data file also feeds the Label object information on how to write with this font. In other words, how to space out the characters and lines correctly. The Label objects we are using in the game are instantiated with the name of the data file and their initial string value: _scoreDisplay = Label::createWithBMFont("font.fnt", "0"); And the value for the label is changed through the setString method: _scoreDisplay->setString("1000"); Just as with every other image in the game, we also have different versions of font.fnt and font.png in our Resources folders, one for each screen definition. FileUtils will once again do the heavy lifting of finding the correct file for the correct screen. So now let's create the labels for our game. Time for action – creating bitmap font labels Creating a bitmap font is somewhat similar to creating a batch node. Continuing with our createGameScreen method, add the following lines to the score label: _scoreDisplay = Label::createWithBMFont("font.fnt", "0"); _scoreDisplay->setAnchorPoint(Vec2(1,0.5)); _scoreDisplay->setPosition(Vec2   (_screenSize.width * 0.8f, _screenSize.height * 0.94f)); this->addChild(_scoreDisplay); And then add a label to display the energy level, and set its horizontal alignment to Right: _energyDisplay = Label::createWithBMFont("font.fnt", "100%", TextHAlignment::RIGHT); _energyDisplay->setPosition(Vec2   (_screenSize.width * 0.3f, _screenSize.height * 0.94f)); this->addChild(_energyDisplay); Add the following line for an icon that appears next to the _energyDisplay label: auto icon = Sprite::createWithSpriteFrameName ("health_icon.png"); icon->setPosition( Vec2(_screenSize.   width * 0.15f, _screenSize.height * 0.94f) ); _gameBatchNode->addChild(icon, kBackground); What just happened? We just created our first bitmap font object in Cocos2d-x. Now let's finish creating our game's sprites. Time for action – adding the final screen sprites The last sprites we need to create are the clouds, the bomb and shockwave, and our game state messages. Back to the createGameScreen method, add the clouds to the screen: for (int i = 0; i < 4; i++) { float cloud_y = i % 2 == 0 ? _screenSize.height * 0.4f : _screenSize.height * 0.5f; auto cloud = Sprite::createWithSpriteFrameName("cloud.png"); cloud->setPosition(Vec2 (_screenSize.width * 0.1f + i * _screenSize.width * 0.3f, cloud_y)); _gameBatchNode->addChild(cloud, kBackground); _clouds.pushBack(cloud); } Create the _bomb sprite; players will grow when tapping the screen: _bomb = Sprite::createWithSpriteFrameName("bomb.png"); _bomb->getTexture()->generateMipmap(); _bomb->setVisible(false);   auto size = _bomb->getContentSize();   //add sparkle inside bomb sprite auto sparkle = Sprite::createWithSpriteFrameName("sparkle.png"); sparkle->setPosition(Vec2(size.width * 0.72f, size.height *   0.72f)); _bomb->addChild(sparkle, kMiddleground, kSpriteSparkle);   //add halo inside bomb sprite auto halo = Sprite::createWithSpriteFrameName   ("halo.png"); halo->setPosition(Vec2(size.width * 0.4f, size.height *   0.4f)); _bomb->addChild(halo, kMiddleground, kSpriteHalo); _gameBatchNode->addChild(_bomb, kForeground); Then create the _shockwave sprite that appears after the _bomb goes off: _shockWave = Sprite::createWithSpriteFrameName ("shockwave.png"); _shockWave->getTexture()->generateMipmap(); _shockWave->setVisible(false); _gameBatchNode->addChild(_shockWave); Finally, add the two messages that appear on the screen, one for our intro state and one for our gameover state: _introMessage = Sprite::createWithSpriteFrameName ("logo.png"); _introMessage->setPosition(Vec2   (_screenSize.width * 0.5f, _screenSize.height * 0.6f)); _introMessage->setVisible(true); this->addChild(_introMessage, kForeground);   _gameOverMessage = Sprite::createWithSpriteFrameName   ("gameover.png"); _gameOverMessage->setPosition(Vec2   (_screenSize.width * 0.5f, _screenSize.height * 0.65f)); _gameOverMessage->setVisible(false); this->addChild(_gameOverMessage, kForeground); What just happened? There is a lot of new information regarding sprites in the previous code. So let's go over it carefully: We started by adding the clouds. We put the sprites inside a vector so we can move the clouds later. Notice that they are also part of our batch node. Next comes the bomb sprite and our first new call: _bomb->getTexture()->generateMipmap(); With this we are telling the framework to create antialiased copies of this texture in diminishing sizes (mipmaps), since we are going to scale it down later. This is optional of course; sprites can be resized without first generating mipmaps, but if you notice loss of quality in your scaled sprites, you can fix that by creating mipmaps for their texture. The texture must have size values in so-called POT (power of 2: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, and so on). Textures in OpenGL must always be sized this way; when they are not, Cocos2d-x will do one of two things: it will either resize the texture in memory, adding transparent pixels until the image reaches a POT size, or stop the execution on an assert. With textures used for mipmaps, the framework will stop execution for non-POT textures. I add the sparkle and the halo sprites as children to the _bomb sprite. This will use the container characteristic of nodes to our advantage. When I grow the bomb, all its children will grow with it. Notice too that I use a third parameter to addChild for halo and sparkle: bomb->addChild(halo, kMiddleground, kSpriteHalo); This third parameter is an integer tag from yet another enumerated list declared in GameLayer.h. I can use this tag to retrieve a particular child from a sprite as follows: auto halo = (Sprite *)   bomb->getChildByTag(kSpriteHalo); We now have our game screen in place: Next come object pools. Time for action – creating our object pools The pools are just vectors of objects. And here are the steps to create them: Inside the createPools method, we first create a pool for meteors: void GameLayer::createPools() { int i; _meteorPoolIndex = 0; for (i = 0; i < 50; i++) { auto sprite = Sprite::createWithSpriteFrameName("meteor.png"); sprite->setVisible(false); _gameBatchNode->addChild(sprite, kMiddleground, kSpriteMeteor); _meteorPool.pushBack(sprite); } Then we create an object pool for health packs: _healthPoolIndex = 0; for (i = 0; i < 20; i++) { auto sprite = Sprite::createWithSpriteFrameName("health.png"); sprite->setVisible(false); sprite->setAnchorPoint(Vec2(0.5f, 0.8f)); _gameBatchNode->addChild(sprite, kMiddleground, kSpriteHealth); _healthPool.pushBack(sprite); } We'll use the corresponding pool index to retrieve objects from the vectors as the game progresses. What just happened? We now have a vector of invisible meteor sprites and a vector of invisible health sprites. We'll use their respective pool indices to retrieve these from the vector as needed as you'll see in a moment. But first we need to take care of actions and animations. With object pools, we reduce the number of instantiations during the main loop, and it allows us to never destroy anything that can be reused. But if you need to remove a child from a node, use ->removeChild or ->removeChildByTag if a tag is present. Actions in a nutshell If you remember, a node will store information about position, scale, rotation, visibility, and opacity of a node. And in Cocos2d-x, there is an Action class to change each one of these values over time, in effect animating these transformations. Actions are usually created with a static method create. The majority of these actions are time-based, so usually the first parameter you need to pass an action is the time length for the action. So for instance: auto fadeout = FadeOut::create(1.0f); This creates a fadeout action that will take one second to complete. You can run it on a sprite, or node, as follows: mySprite->runAction(fadeout); Cocos2d-x has an incredibly flexible system that allows us to create any combination of actions and transformations to achieve any effect we desire. You may, for instance, choose to create an action sequence (Sequence) that contains more than one action; or you can apply easing effects (EaseIn, EaseOut, and so on) to your actions. You can choose to repeat an action a certain number of times (Repeat) or forever (RepeatForever); and you can add callbacks to functions you want called once an action is completed (usually inside a Sequence action). Time for action – creating actions with Cocos2d-x Creating actions with Cocos2d-x is a very simple process: Inside our createActions method, we will instantiate the actions we can use repeatedly in our game. Let's create our first actions: void GameLayer::createActions() { //swing action for health drops auto easeSwing = Sequence::create( EaseInOut::create(RotateTo::create(1.2f, -10), 2), EaseInOut::create(RotateTo::create(1.2f, 10), 2), nullptr);//mark the end of a sequence with a nullptr _swingHealth = RepeatForever::create( (ActionInterval *) easeSwing ); _swingHealth->retain(); Actions can be combined in many different forms. Here, the retained _swingHealth action is a RepeatForever action of Sequence that will rotate the health sprite first one way, then the other, with EaseInOut wrapping the RotateTo action. RotateTo takes 1.2 seconds to rotate the sprite first to -10 degrees and then to 10. And the easing has a value of 2, which I suggest you experiment with to get a sense of what it means visually. Next we add three more actions: //action sequence for shockwave: fade out, callback when //done _shockwaveSequence = Sequence::create( FadeOut::create(1.0f), CallFunc::create(std::bind(&GameLayer::shockwaveDone, this)), nullptr); _shockwaveSequence->retain();   //action to grow bomb _growBomb = ScaleTo::create(6.0f, 1.0); _growBomb->retain();   //action to rotate sprites auto rotate = RotateBy::create(0.5f , -90); _rotateSprite = RepeatForever::create( rotate ); _rotateSprite->retain(); First, another Sequence. This will fade out the sprite and call the shockwaveDone function, which is already implemented in the class and turns the _shockwave sprite invisible when called. The last one is a RepeatForever action of a RotateBy action. In half a second, the sprite running this action will rotate -90 degrees and will do that again and again. What just happened? You just got your first glimpse of how to create actions in Cocos2d-x and how the framework allows for all sorts of combinations to accomplish any effect. It may be hard at first to read through a Sequence action and understand what's happening, but the logic is easy to follow once you break it down into its individual parts. But we are not done with the createActions method yet. Next come sprite animations. Animating a sprite in Cocos2d-x The key thing to remember is that an animation is just another type of action, one that changes the texture used by a sprite over a period of time. In order to create an animation action, you need to first create an Animation object. This object will store all the information regarding the different sprite frames you wish to use in the animation, the length of the animation in seconds, and whether it loops or not. With this Animation object, you then create a Animate action. Let's take a look. Time for action – creating animations Animations are a specialized type of action that require a few extra steps: Inside the same createActions method, add the lines for the two animations we have in the game. First, we start with the animation that shows an explosion when a meteor reaches the city. We begin by loading the frames into an Animation object: auto animation = Animation::create(); int i; for(i = 1; i <= 10; i++) { auto name = String::createWithFormat("boom%i.png", i); auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name->getCString()); animation->addSpriteFrame(frame); } Then we use the Animation object inside a Animate action: animation->setDelayPerUnit(1 / 10.0f); animation->setRestoreOriginalFrame(true); _groundHit = Sequence::create(    MoveBy::create(0, Vec2(0,_screenSize.height * 0.12f)),    Animate::create(animation),    CallFuncN::create(CC_CALLBACK_1(GameLayer::animationDone, this)), nullptr); _groundHit->retain(); The same steps are repeated to create the other explosion animation used when the player hits a meteor or a health pack. animation = Animation::create(); for(int i = 1; i <= 7; i++) { auto name = String::createWithFormat("explosion_small%i.png", i); auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name->getCString()); animation->addSpriteFrame(frame); }   animation->setDelayPerUnit(0.5 / 7.0f); animation->setRestoreOriginalFrame(true); _explosion = Sequence::create(      Animate::create(animation),    CallFuncN::create(CC_CALLBACK_1(GameLayer::animationDone, this)), nullptr); _explosion->retain(); What just happened? We created two instances of a very special kind of action in Cocos2d-x: Animate. Here is what we did: First, we created an Animation object. This object holds the references to all the textures used in the animation. The frames were named in such a way that they could easily be concatenated inside a loop (boom1, boom2, boom3, and so on). There are 10 frames for the first animation and seven for the second. The textures (or frames) are SpriteFrame objects we grab from SpriteFrameCache, which as you remember, contains all the information from the sprite_sheet.plist data file. So the frames are in our sprite sheet. Then when all frames are in place, we determine the delay of each frame by dividing the total amount of seconds we want the animation to last by the total number of frames. The setRestoreOriginalFrame method is important here. If we set setRestoreOriginalFrame to true, then the sprite will revert to its original appearance once the animation is over. For example, if I have an explosion animation that will run on a meteor sprite, then by the end of the explosion animation, the sprite will revert to displaying the meteor texture. Time for the actual action. Animate receives the Animation object as its parameter. (In the first animation, we shift the position of the sprite just before the explosion appears, so there is an extra MoveBy method.) And in both instances, I make a call to an animationDone callback already implemented in the class. It makes the calling sprite invisible: void GameLayer::animationDone (Node* pSender) { pSender->setVisible(false); } We could have used the same method for both callbacks (animationDone and shockwaveDone) as they accomplish the same thing. But I wanted to show you a callback that receives as an argument, the node that made the call and one that did not. Respectively, these are CallFuncN and CallFunc, and were used inside the action sequences we just created. Time to make our game tick! Okay, we have our main elements in place and are ready to add the final bit of logic to run the game. But how will everything work? We will use a system of countdowns to add new meteors and new health packs, as well as a countdown that will incrementally make the game harder to play. On touch, the player will start the game if the game is not running, and also add bombs and explode them during gameplay. An explosion creates a shockwave. On update, we will check against collision between our _shockwave sprite (if visible) and all our falling objects. And that's it. Cocos2d-x will take care of all the rest through our created actions and callbacks! So let's implement our touch events first. Time for action – handling touches Time to bring the player to our party: Time to implement our onTouchBegan method. We'll begin by handling the two game states, intro and game over: bool GameLayer::onTouchBegan (Touch * touch, Event * event){   //if game not running, we are seeing either intro or //gameover if (!_running) {    //if intro, hide intro message    if (_introMessage->isVisible()) {      _introMessage->setVisible(false);        //if game over, hide game over message    } else if (_gameOverMessage->isVisible()) {      SimpleAudioEngine::getInstance()->stopAllEffects();      _gameOverMessage->setVisible(false);         }       this->resetGame();    return true; } Here we check to see if the game is not running. If not, we check to see if any of our messages are visible. If _introMessage is visible, we hide it. If _gameOverMessage is visible, we stop all current sound effects and hide the message as well. Then we call a method called resetGame, which will reset all the game data (energy, score, and countdowns) to their initial values, and set _running to true. Next we handle the touches. But we only need to handle one each time so we use ->anyObject() on Set: auto touch = (Touch *)pTouches->anyObject();   if (touch) { //if bomb already growing... if (_bomb->isVisible()) {    //stop all actions on bomb, halo and sparkle    _bomb->stopAllActions();    auto child = (Sprite *) _bomb->getChildByTag(kSpriteHalo);    child->stopAllActions();    child = (Sprite *) _bomb->getChildByTag(kSpriteSparkle);    child->stopAllActions();       //if bomb is the right size, then create shockwave    if (_bomb->getScale() > 0.3f) {      _shockWave->setScale(0.1f);      _shockWave->setPosition(_bomb->getPosition());      _shockWave->setVisible(true);      _shockWave->runAction(ScaleTo::create(0.5f, _bomb->getScale() * 2.0f));      _shockWave->runAction(_shockwaveSequence->clone());      SimpleAudioEngine::getInstance()->playEffect("bombRelease.wav");      } else {      SimpleAudioEngine::getInstance()->playEffect("bombFail.wav");    }    _bomb->setVisible(false);    //reset hits with shockwave, so we can count combo hits    _shockwaveHits = 0; //if no bomb currently on screen, create one } else {    Point tap = touch->getLocation();    _bomb->stopAllActions();    _bomb->setScale(0.1f);    _bomb->setPosition(tap);    _bomb->setVisible(true);    _bomb->setOpacity(50);    _bomb->runAction(_growBomb->clone());         auto child = (Sprite *) _bomb->getChildByTag(kSpriteHalo);      child->runAction(_rotateSprite->clone());      child = (Sprite *) _bomb->getChildByTag(kSpriteSparkle);      child->runAction(_rotateSprite->clone()); } } If _bomb is visible, it means it's already growing on the screen. So on touch, we use the stopAllActions() method on the bomb and we use the stopAllActions() method on its children that we retrieve through our tags: child = (Sprite *) _bomb->getChildByTag(kSpriteHalo); child->stopAllActions(); child = (Sprite *) _bomb->getChildByTag(kSpriteSparkle); child->stopAllActions(); If _bomb is the right size, we start our _shockwave. If it isn't, we play a bomb failure sound effect; there is no explosion and _shockwave is not made visible. If we have an explosion, then the _shockwave sprite is set to 10 percent of the scale. It's placed at the same spot as the bomb, and we run a couple of actions on it: we grow the _shockwave sprite to twice the scale the bomb was when it went off and we run a copy of _shockwaveSequence that we created earlier. Finally, if no _bomb is currently visible on screen, we create one. And we run clones of previously created actions on the _bomb sprite and its children. When _bomb grows, its children grow. But when the children rotate, the bomb does not: a parent changes its children, but the children do not change their parent. What just happened? We just added part of the core logic of the game. It is with touches that the player creates and explodes bombs to stop meteors from reaching the city. Now we need to create our falling objects. But first, let's set up our countdowns and our game data. Time for action – starting and restarting the game Let's add the logic to start and restart the game. Let's write the implementation for resetGame: void GameLayer::resetGame(void) {    _score = 0;    _energy = 100;       //reset timers and "speeds"    _meteorInterval = 2.5;    _meteorTimer = _meteorInterval * 0.99f;    _meteorSpeed = 10;//in seconds to reach ground    _healthInterval = 20;    _healthTimer = 0;    _healthSpeed = 15;//in seconds to reach ground       _difficultyInterval = 60;    _difficultyTimer = 0;       _running = true;       //reset labels    _energyDisplay->setString(std::to_string((int) _energy) + "%");    _scoreDisplay->setString(std::to_string((int) _score)); } Next, add the implementation of stopGame: void GameLayer::stopGame() {       _running = false;       //stop all actions currently running    int i;    int count = (int) _fallingObjects.size();       for (i = count-1; i >= 0; i--) {        auto sprite = _fallingObjects.at(i);        sprite->stopAllActions();        sprite->setVisible(false);        _fallingObjects.erase(i);    }    if (_bomb->isVisible()) {        _bomb->stopAllActions();        _bomb->setVisible(false);        auto child = _bomb->getChildByTag(kSpriteHalo);        child->stopAllActions();        child = _bomb->getChildByTag(kSpriteSparkle);        child->stopAllActions();    }    if (_shockWave->isVisible()) {        _shockWave->stopAllActions();        _shockWave->setVisible(false);    }    if (_ufo->isVisible()) {        _ufo->stopAllActions();        _ufo->setVisible(false);        auto ray = _ufo->getChildByTag(kSpriteRay);       ray->stopAllActions();        ray->setVisible(false);    } } What just happened? With these methods we control gameplay. We start the game with default values through resetGame(), and we stop all actions with stopGame(). Already implemented in the class is the method that makes the game more difficult as time progresses. If you take a look at the method (increaseDifficulty) you will see that it reduces the interval between meteors and reduces the time it takes for meteors to reach the ground. All we need now is the update method to run the countdowns and check for collisions. Time for action – updating the game We already have the code that updates the countdowns inside the update. If it's time to add a meteor or a health pack we do it. If it's time to make the game more difficult to play, we do that too. It is possible to use an action for these timers: a Sequence action with a Delay action object and a callback. But there are advantages to using these countdowns. It's easier to reset them and to change them, and we can take them right into our main loop. So it's time to add our main loop: What we need to do is check for collisions. So add the following code: if (_shockWave->isVisible()) { count = (int) _fallingObjects.size(); for (i = count-1; i >= 0; i--) {    auto sprite = _fallingObjects.at(i);    diffx = _shockWave->getPositionX() - sprite->getPositionX();    diffy = _shockWave->getPositionY() - sprite->getPositionY();    if (pow(diffx, 2) + pow(diffy, 2) <= pow(_shockWave->getBoundingBox().size.width * 0.5f, 2)) {    sprite->stopAllActions();    sprite->runAction( _explosion->clone());    SimpleAudioEngine::getInstance()->playEffect("boom.wav");    if (sprite->getTag() == kSpriteMeteor) {      _shockwaveHits++;      _score += _shockwaveHits * 13 + _shockwaveHits * 2;    }    //play sound    _fallingObjects.erase(i); } } _scoreDisplay->setString(std::to_string(_score)); } If _shockwave is visible, we check the distance between it and each sprite in _fallingObjects vector. If we hit any meteors, we increase the value of the _shockwaveHits property so we can award the player for multiple hits. Next we move the clouds: //move clouds for (auto sprite : _clouds) { sprite->setPositionX(sprite->getPositionX() + dt * 20); if (sprite->getPositionX() > _screenSize.width + sprite->getBoundingBox().size.width * 0.5f)    sprite->setPositionX(-sprite->getBoundingBox().size.width * 0.5f); } I chose not to use a MoveTo action for the clouds to show you the amount of code that can be replaced by a simple action. If not for Cocos2d-x actions, we would have to implement logic to move, rotate, swing, scale, and explode all our sprites! And finally: if (_bomb->isVisible()) {    if (_bomb->getScale() > 0.3f) {      if (_bomb->getOpacity() != 255)        _bomb->setOpacity(255);    } } We give the player an extra visual cue to when a bomb is ready to explode by changing its opacity. What just happened? The main loop is pretty straightforward when you don't have to worry about updating individual sprites, as our actions take care of that for us. We pretty much only need to run collision checks between our sprites, and to determine when it's time to throw something new at the player. So now the only thing left to do is grab the meteors and health packs from the pools when their timers are up. So let's get right to it. Time for action – retrieving objects from the pool We just need to use the correct index to retrieve the objects from their respective vector: To retrieve meteor sprites, we'll use the resetMeteor method: void GameLayer::resetMeteor(void) {    //if too many objects on screen, return    if (_fallingObjects.size() > 30) return;       auto meteor = _meteorPool.at(_meteorPoolIndex);      _meteorPoolIndex++;    if (_meteorPoolIndex == _meteorPool.size())      _meteorPoolIndex = 0;      int meteor_x = rand() % (int) (_screenSize.width * 0.8f) + _screenSize.width * 0.1f;    int meteor_target_x = rand() % (int) (_screenSize.width * 0.8f) + _screenSize.width * 0.1f;       meteor->stopAllActions();    meteor->setPosition(Vec2(meteor_x, _screenSize.height + meteor->getBoundingBox().size.height * 0.5));    //create action    auto rotate = RotateBy::create(0.5f , -90);    auto repeatRotate = RepeatForever::create( rotate );    auto sequence = Sequence::create (                MoveTo::create(_meteorSpeed, Vec2(meteor_target_x, _screenSize.height * 0.15f)),                CallFunc::create(std::bind(&GameLayer::fallingObjectDone, this, meteor) ), nullptr);   meteor->setVisible ( true ); meteor->runAction(repeatRotate); meteor->runAction(sequence); _fallingObjects.pushBack(meteor); } We grab the next available meteor from the pool, then we pick a random start and end x value for its MoveTo action. The meteor starts at the top of the screen and will move to the bottom towards the city, but the x value is randomly picked each time. We rotate the meteor inside a RepeatForever action, and we use Sequence to move the sprite to its target position and then call back fallingObjectDone when the meteor has reached its target. We finish by adding the new meteor we retrieved from the pool to the _fallingObjects vector so we can check collisions with it. The method to retrieve the health (resetHealth) sprites is pretty much the same, except that swingHealth action is used instead of rotate. You'll find that method already implemented in GameLayer.cpp. What just happened? So in resetGame we set the timers, and we update them in the update method. We use these timers to add meteors and health packs to the screen by grabbing the next available one from their respective pool, and then we proceed to run collisions between an exploding bomb and these falling objects. Notice that in both resetMeteor and resetHealth we don't add new sprites if too many are on screen already: if (_fallingObjects->size() > 30) return; This way the game does not get ridiculously hard, and we never run out of unused objects in our pools. And the very last bit of logic in our game is our fallingObjectDone callback, called when either a meteor or a health pack has reached the ground, at which point it awards or punishes the player for letting sprites through. When you take a look at that method inside GameLayer.cpp, you will notice how we use ->getTag() to quickly ascertain which type of sprite we are dealing with (the one calling the method): if (pSender->getTag() == kSpriteMeteor) { If it's a meteor, we decrease energy from the player, play a sound effect, and run the explosion animation; an autorelease copy of the _groundHit action we retained earlier, so we don't need to repeat all that logic every time we need to run this action. If the item is a health pack, we increase the energy or give the player some points, play a nice sound effect, and hide the sprite. Play the game! We've been coding like mad, and it's finally time to run the game. But first, don't forget to release all the items we retained. In GameLayer.cpp, add our destructor method: GameLayer::~GameLayer () {       //release all retained actions    CC_SAFE_RELEASE(_growBomb);    CC_SAFE_RELEASE(_rotateSprite);    CC_SAFE_RELEASE(_shockwaveSequence);    CC_SAFE_RELEASE(_swingHealth);    CC_SAFE_RELEASE(_groundHit);    CC_SAFE_RELEASE(_explosion);    CC_SAFE_RELEASE(_ufoAnimation);    CC_SAFE_RELEASE(_blinkRay);       _clouds.clear();    _meteorPool.clear();    _healthPool.clear();    _fallingObjects.clear(); } The actual game screen will now look something like this: Now, let's take this to Android. Time for action – running the game in Android Follow these steps to deploy the game to Android: This time, there is no need to alter the manifest because the default settings are the ones we want. So, navigate to proj.android and then to the jni folder and open the Android.mk file in a text editor. Edit the lines in LOCAL_SRC_FILES to read as follows: LOCAL_SRC_FILES := hellocpp/main.cpp \                    ../../Classes/AppDelegate.cpp \                    ../../Classes/GameLayer.cpp Follow the instructions from the HelloWorld and AirHockey examples to import the game into Eclipse. Save it and run your application. This time, you can try out different size screens if you have the devices. What just happened? You just ran a universal app in Android. And nothing could have been simpler. Summary In my opinion, after nodes and all their derived objects, actions are the second best thing about Cocos2d-x. They are time savers and can quickly spice things up in any project with professional-looking animations. And I hope with the examples found in this article, you will be able to create any action you need with Cocos2d-x. Resources for Article: Further resources on this subject: Animations in Cocos2d-x [article] Moving the Space Pod Using Touch [article] Cocos2d-x: Installation [article]
Read more
  • 0
  • 0
  • 1859

article-image-introducing-gamemaker
Packt
24 Mar 2015
5 min read
Save for later

Introducing GameMaker

Packt
24 Mar 2015
5 min read
In this article by Nathan Auckett, author of the book GameMaker Essentials, you will learn what GameMaker is all about, who made it, what it is used for, and more. You will then also be learning how to install GameMaker on your computer that is ready for use. (For more resources related to this topic, see here.) In this article, we will cover the following topics: Understanding GameMaker Installing GameMaker: Studio What is this article about? Understanding GameMaker Before getting started with GameMaker, it is best to know exactly what it is and what it's designed to do. GameMaker is a 2D game creation software by YoYo Games. It was designed to allow anyone to easily develop games without having to learn complex programming languages such as C++ through the use of its drag and drop functionality. The drag and drop functionality allows the user to create games by visually organizing icons on screen, which represent actions and statements that will occur during the game. GameMaker also has a built-in programming language called GameMaker Language, or GML for short. GML allows users to type out code to be run during their game. All drag and drop actions are actually made up of this GML code. GameMaker is primarily designed for 2D games, and most of its features and functions are designed for 2D game creation. However, GameMaker does have the ability to create 3D games and has a number of functions dedicated to this. GameMaker: Studio There are a number of different versions of GameMaker available, most of which are unsupported because they are outdated; however, support can still be found in the GameMaker Community Forums. GameMaker: Studio is the first version of GameMaker after GameMaker HTML5 to allow users to create games and export them for use on multiple devices and operating systems including PC, Mac, Linux, and Android, on both mobile and desktop versions. GameMaker: Studio is designed to allow one code base (GML) to run on any device with minimal changes to the base code. Users are able to export their games to run on any supported device or system such as HTML5 without changing any code to make things work. GameMaker: Studio was also the first version available for download and use through the Steam marketplace. YoYo Games took advantage of the Steam workshop and allowed Steam-based users to post and share their creations through the service. GameMaker: Studio is sold in a number of different versions, which include several enhanced features and capabilities as the price gets higher. The standard version is free to download and use. However, it lacks some advanced features included in higher versions and only allows for exporting to the Windows desktop. The professional version is the second cheapest from the standard version. It includes all features, but only has the Windows desktop and Windows app exports. Other exports can be purchased at an extra cost ranging from $99.99 to $300. The master version is the most expensive of all the options. It comes with every feature and every export, including all future export modules in version 1.x. If you already own exports in the professional version, you can get the prices of those exports taken off the price of the master version. Installing GameMaker: Studio Installing GameMaker is performed much like any other program. In this case, we will be installing GameMaker: Studio as this is the most up-to-date version at this point. You can find the download at the YoYo Games website, https://www.yoyogames.com/. From the site, you can pick the free version or purchase one of the others. All the installations are basically the same. Once the installer is downloaded, we are ready to install GameMaker: Studio. This is just like installing any other program. Just run the file, and then follow the on-screen instructions to accept the license agreement, choose an install location, and install the software. On the first run, you may see a progress bar appear at the top left of your screen. This is just GameMaker running its first time setup. It will also do this during the update process as YoYo Games releases new features. Once it is done, you should see a welcome screen and will be prompted to enter your registration code. The key should be e-mailed to you when you make an account during the purchase/download process. Enter this key and your copy of GameMaker: Studio should be registered. You may be prompted to restart GameMaker at this time. Close GameMaker and re-open it and you should see the welcome screen and be able to choose from a number of options on it: What is this article about? We now have GameMaker: Studio installed and are ready to get started with it. In this article, we will be covering the essential things to know about GameMaker: Studio. This includes everything from drag and drop actions to programming in GameMaker using GameMaker Language (GML). You will learn about how things in GameMaker are structured, and how to organize resources to keep things as clean as possible. Summary In this article, we looked into what GameMaker actually is and learned that there are different versions available. We also looked at the different types of GameMaker: Studio available for download and purchase. We then learned how to install GameMaker: Studio, which is the final step in getting ready to learn the essential skills and starting to make our very own games. Resources for Article: Further resources on this subject: Getting Started – An Introduction to GML [Article] Animating a Game Character [Article] Why should I make cross-platform games? [Article]
Read more
  • 0
  • 0
  • 1869

article-image-animating-game-character
Packt
18 Feb 2015
8 min read
Save for later

Animating a Game Character

Packt
18 Feb 2015
8 min read
In this Article by Claudio Scolastici, author of the book Unity 2D Game Development Cookbook. we will cover the following recipes: Creating an animation tree Dealing with transitions (For more resources related to this topic, see here.) Now that we have imported the necessary graphic assets for a prototype, we can approach its actual building in Unity, starting by making an animation set for our character. Unity implements an easy-to-approach animation system, though quite powerful, called Mecanim. Mecanim" is a proprietary tool of Unity in which the animation clips belonging to a character are represented as boxes connected by directional arrows. Boxes represent states, which you can simply think of as idle, walk, run...you get the idea. Arrows, on" the other hand, represent the transitions between the states, which are responsible for actually blending between one animation clip and the next. Thanks to transitions, we can make characters that pass smoothly, for example, from a walking animation into a running one. The control of transitions is achieved" through parameters: variables belonging to different types that are stored in the character animator and are used to define and check the conditions that trigger an animation clip. The types available are common in programming and scripting languages: int, float, and bool. A distinctive type implemented in" Mecanim is the trigger, which is useful when you want a transition to be triggered as an all-or-nothing event. By the way, an animator is a built-in component of Unity, strictly connected with the Mecanim system, which is represented as a panel in the Unity interface. Inside this panel, the so-called animation tree of a character is actually built-up and the control parameters for the transitions are set and linked to the clips. Time for an image to help you better understand what we are talking about! The following picture shows an example of an animator of a "standard game character: As you can see, there are four states connected by transitions that configure the logic of the flow between one state and another. Inside these arrows, the parameters and their reference values to actually trigger the animations are stored. With Mecanim, it's quite easy to build the animation tree of a character and create the "logic that determines the conditions for the animations to be played. One example is to "use a float variable to blend between a walking and a running cycle, having the speed "of the character as the control parameter. Using a trigger or a boolean variable to add "a jumping animation to the character is another fairly common example. These are the subjects of our following two recipes, starting with trigger-based blending. Creating the animation tree In this recipe, we show you how to add animation clips to the animator component of a game object (our game character). This being done, we will be able to set the transitions between the clips and create the logic for the animations to be correctly played. Getting ready First of all, we need a set of "animation clips, imported in Unity and configured in Inspector. Before we proceed, be sure you have these four animation clips imported into your Unity project as FBX files: Char@Idle, Char@Run, Char@Jump, and Char@Walk. How to do it... The first operation is to create a folder to store the Animator Controller. From the project panel, select the Assets folder and create a new folder for the Animation Controller. Name this folder Animators. In the Animators folder, create a new Animator Controller option by navigating to Create | Animator Controller, as shown in the following screenshot: Name the "asset Character_Animator, or any other name you like. Double-click on Character_Animator to open the Animator panel in Unity. "Refer to the following screenshot; you should have an empty grid with a single magenta box called Any State: Access "the Models/Animations folder and select Char@Idle. Expand its hierarchy to access the actual animation clip named Idle; animation clips are represented by small play icons. Refer to the following screenshot for more clarity: Now drag" the clip into the Animator window. The clip should turn into a box inside the panel (colored in orange to represent that). Being the first clip imported into the Animator window, it is assumed to be the default animation for the character. That's exactly what we want! Repeat this operation with the clip named Jump, taken from the Char@Jump FBX file. The following screenshot shows what should appear in the Animator window: How it works... By dragging" animation clips from the project panel into the Animator editor, Mecanim creates a logic state for each of them. As states, the clips are available to connect through transitions and the animation tree of the character can come to life. With the Idle and Jump animations added to the Animator window, we can define the logic to control the conditions to switch between them. In the following recipe, we "create the transition to blend between these two animation clips. Dealing with transitions In this recipe, we create and set up the "transition for the character to switch between the Idle and Jump animation clips. For this task, we also need a parameter, which we will call bJump, to trigger the jump animation through code. Getting ready We will build on the previous recipe. Have the Animator window open, and be ready to follow our instructions. How to do it... As you move to the Animator panel in Unity, you should see a orange box representing the Idle animation, from our previous recipe. If it is not, right-click on it, and from the menu, select Set As Default. You can refer to the following screenshot: Right-click on the Idle clip and select Make Transition from the menu, as shown in the following screenshot: Drag the arrow that "appears onto the Jump clip and click to create the transition. "It should appear in the Inspector window, to the right of the Animator window. "Check the following screenshot to see whether you did it right: Now that we have got the "transition, we need a parameter to switch between Idle "and Jump. We use a boolean type for this, so we first need to create it. In the bottom-left corner of the Animator window, click on the small +, and from the "menu that appears, select Bool, as shown in the following screenshot: Name the newly created parameter bJump (the "b" stands for the boolean type; "it's a good habit to create meaningful variable names). Click on the white arrow representing the transition to access its properties in Inspector. There, a visual representation of the transition between the two clips "is available. By checking the "Conditions section in Inspector, you can see that the transition "is right now controlled by Exit Time, meaning that the Jump clip will be played only after the Idle clip has finished playing. The 0.97 value tells us that the transition is actually blending between the two clips for the last 3 percent of the idle animation. For your reference, you can adjust this value if you want to blend it a bit more or a "bit less. Please refer to the following screenshot: As we want our bJump parameter to control the transition, we need to change Exit Time using the tJump parameter. We do that by clicking on the drop-down menu on Exit Time and selecting tJump from the menu, as shown in the following screenshot: Note that "it is possible to add or remove conditions by acting on the small + "and - buttons in the interface if you need extra conditions to control one single transition. For now, we just want to be sure that the Atomic option is not flagged in the Inspector panel. The Atomic flag interrupts an animation, even if it has not finished playing yet. We don't want that to happen; when the character jumps, "the animation must get to its end before playing any other clip. The following screenshot highlights these options we just mentioned: How it works... We made our first "transition with Mecanim and used a boolean variable called bJump to control it. It is now possible to link bJump to an event, for example, pressing the spacebar "to trigger the Jump animation clip. Summary There was a time when building games was a cumbersome and almost exclusive activity, as you needed to program your own game engine, or pay a good amount of money to license one. Thanks to Unity, creating video games today is still a cumbersome activity, though less exclusive and expensive! With this article, we aim to provide you with a detailed guide to approach the development of an actual 2D game with Unity. As it is a complex process that requires several operations to be performed, we will do our best to support you at every step by providing all the relevant information to help you successfully make games with Unity. Resources for Article: Further resources on this subject: 2D Twin-stick Shooter [article] Components in Unity [article] Introducing the Building Blocks for Unity Scripts [article]
Read more
  • 0
  • 0
  • 2937
article-image-run-xcode-run
Packt
05 Feb 2015
9 min read
Save for later

Run Xcode Run

Packt
05 Feb 2015
9 min read
In this article by Jorge Jordán, author of the book Cocos2d Game Development Blueprints, we will see how to run the newly created project in Xcode. (For more resources related to this topic, see here.) Click on Run at the top-left of the Xcode window and it will run the project in the iOS Simulator, which defaults to an iOS 6.1 iPhone: Voilà! You've just built your first Hello World example with Cocos2d v3, but before going further, let's take a look at the code to understand how it works. We will be using iOS Simulator to run the game unless otherwise specified. Understanding the default project We are going to take an overview of the classes available in a new project, but don't worry if you don't understand everything; the objective of this section is just to get familiar with the look of a Cocos2d game. If you open the main.m class under the Supporting Files group, you will see: int main(int argc, char *argv[]) {    @autoreleasepool {        int retVal = UIApplicationMain(argc, argv, nil,         @"AppDelegate");        return retVal;    } } As you can see, the @autorelease block means that ARC is enabled by default on new Cocos2d projects so we don't have to worry about releasing objects or enabling ARC. ARC is the acronym for Automatic Reference Counting and it's a compiler iOS feature to provide automatic memory management of objects. It works by adding code at compile time, ensuring every object lives as long as necessary, but not longer. On the other hand, the block calls AppDelegate, a class that inherits from CCAppDelegate which implements the UIApplicationDelegate protocol. In other words, the starting point of our game and the place to set up our app is located in AppDelegate, like a typical iOS application. If you open AppDelegate.m, you will see the following method, which is called when the game has been launched: -(BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    [self setupCocos2dWithOptions:@{          CCSetupShowDebugStats: @(YES),    }];    return YES; } Here, the only initial configuration specified is to enable the debug stats, specifying the option CCSetupShowDebugStats: @(YES), that you can see in the previous block of code. The number on the top indicates the amount of draw calls and the two labels below are the time needed to update the frame and the frame rate respectively. The maximum frame rate an iOS device can have is 60 and it's a measure of the smoothness a game can attain: the higher the frame rate, the smoother the game. You will need to have the top and the bottom values in mind as the number of draw calls and the frame rate will let you know how efficient your game will be. The next thing to take care of is the startScene method: -(CCScene *)startScene {    // The initial scene will be GameScene    return [IntroScene scene]; } This method should be overriden to indicate the first scene we want to display in our game. In this case, it points to IntroScene where the init method looks like the following code: - (id)init {    // Apple recommends assigning self with super's return value    self = [super init];    if (!self) {        return(nil);      }    // Create a colored background (Dark Gray)    CCNodeColor *background = [CCNodeColor nodeWithColor:[CCColorcolorWithRed:0.2f green:0.2f blue:0.2f alpha:1.0f]];    [self addChild:background];    // Hello world    CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World"fontName:@"Chalkduster" fontSize:36.0f];    label.positionType = CCPositionTypeNormalized;    label.color = [CCColor redColor];    label.position = ccp(0.5f, 0.5f); // Middle of screen    [self addChild:label];    // Helloworld scene button    CCButton *helloWorldButton = [CCButton buttonWithTitle:@"[Start ]" fontName:@"Verdana-Bold" fontSize:18.0f];    helloWorldButton.positionType = CCPositionTypeNormalized;    helloWorldButton.position = ccp(0.5f, 0.35f);    [helloWorldButton setTarget:self     selector:@selector(onSpinningClicked:)];    [self addChild:helloWorldButton];    // done    return self; } This code first calls the initialization method for the superclass IntroScene by sending the [super init] message. Then it creates a gray-colored background with a CCNodeColor class, which is basically a solid color node, but this background won't be shown until it's added to the scene, which is exactly what [self addChild:background] does. The red "Hello World" label you can see in the previous screenshot is an instance of the CCLabelTTF class, whose position will be centered on the screen thanks to label.position = ccp(0.5f, 0.5f). Cocos2d provides the cpp(coord_x, coord_y) method, which is a precompiler macro for CGPointMake and both can be used interchangeably. The last code block creates CCButton that will call onSpinningClicked once we click on it. This source code isn't hard at all, but what will happen when we click on the Start button? Don't be shy, go back to the iOS Simulator and find out! If you take a look at the onSpinningClicked method in IntroScene.m, you will understand what happened: - (void)onSpinningClicked:(id)sender {    // start spinning scene with transition    [[CCDirector sharedDirector] replaceScene:[HelloWorldScene     scene]        withTransition:[CCTransitiontransitionPushWithDirection:CCTransitionDirectionLeftduration:1.0f]]; } This code presents the HelloWorldScene scene replacing the current one (InitScene) and it's being done by pushing HelloWorldScene to the top of the scene stack and using a horizontal scroll transition that will last for 1.0 second. Let's take a look at the HelloWorldScene.m to understand the behavior we just experienced: @implementation HelloWorldScene {    CCSprite *_sprite; } - (id)init {    // Apple recommends assigning self with super's return value    self = [super init];    if (!self) {        return(nil);    }    // Enable touch handling on scene node    self.userInteractionEnabled = YES;    // Create a colored background (Dark Gray)    CCNodeColor *background = [CCNodeColor nodeWithColor:[CCColorcolorWithRed:0.2f green:0.2f blue:0.2f alpha:1.0f]];    [self addChild:background];    // Add a sprite    _sprite = [CCSprite spriteWithImageNamed:@"Icon-72.png"];    _sprite.position =     ccp(self.contentSize.width/2,self.contentSize.height/2);    [self addChild:_sprite];    // Animate sprite with action    CCActionRotateBy* actionSpin = [CCActionRotateByactionWithDuration:1.5f angle:360];    [_sprite runAction:[CCActionRepeatForeveractionWithAction:actionSpin]];    // Create a back button    CCButton *backButton = [CCButton buttonWithTitle:@"[ Menu ]"fontName:@"Verdana-Bold" fontSize:18.0f];    backButton.positionType = CCPositionTypeNormalized;    backButton.position = ccp(0.85f, 0.95f); // Top Right ofscreen    [backButton setTarget:self     selector:@selector(onBackClicked:)];    [self addChild:backButton];    // done    return self; } This piece of code is very similar to the one we saw in IntroScene.m, which is why we just need to focus on the differences. If you look at the top of the class, you can see how we are declaring a private instance for a CCSprite class, which is also a subclass of CCNode, and its main role is to render 2D images on the screen. The CCSprite class is one of the most-used classes in Cocos2d game development, as it provides a visual representation and a physical shape to the objects in view. Then, in the init method, you will see the instruction self.userInteractionEnabled = YES, which is used to enable the current scene to detect and manage touches by implementing the touchBegan method. The next thing to highlight is how we initialize a CCSprite class using an image, positioning it in the center of the screen. If you read a couple more lines, you will understand why the icon rotates as soon as the scene is loaded. We create a 360-degree rotation action thanks to CCRotateBy that will last for 1.5 seconds. But why is this rotation repeated over and over? This happens thanks to CCActionRepeatForever, which will execute the rotate action as long as the scene is running. The last piece of code in the init method doesn't need explanation as it creates a CCButton that will execute onBackClicked once clicked. This method replaces the scene HelloWorldScene with IntroScene in a similar way as we saw before, with only one difference: the transition happens from left to right. Did you try to touch the screen? Try it and you will understand why touchBegan has the following code: -(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {    CGPoint touchLoc = [touch locationInNode:self];    // Move our sprite to touch location    CCActionMoveTo *actionMove = [CCActionMoveToactionWithDuration:1.0f position:touchLoc];    [_sprite runAction:actionMove]; } This is one of the methods you need to implement to manage touch. The others are touchMoved, touchEnded, and touchCancelled. When the user begins touching the screen, the sprite will move to the registered coordinates thanks to a commonly used action: CCActionMoveto. This action just needs to know the position that we want to move our sprite to and the duration of the movement. Now that we have had an overview of the initial project code, it is time to go deeper into some of the classes we have shown. Did you realize that CCNode is the parent class of several classes we have seen? You will understand why if you keep reading. Summary In this article, we had our first contact with a Cocos2d project. We executed a new project and took an overview of it, understanding some of the classes that are part of this framework. Resources for Article: Further resources on this subject: Dragging a CCNode in Cocos2D-Swift [Article] Animations in Cocos2d-x [Article] Why should I make cross-platform games? [Article]
Read more
  • 0
  • 0
  • 1416

article-image-why-should-i-make-cross-platform-games
Packt
12 Jan 2015
10 min read
Save for later

Why should I make cross-platform games?

Packt
12 Jan 2015
10 min read
In this article by Emanuele Feronato, author of the book Learning Cocos2d-JS Game Development, we will see why we need to make cross-platform games and how to do it using Cocos2d-JS. This is a very important question. I asked it to myself a lot of times when HTML5 mobile gaming started to become popular. I was just thinking it was a waste of time to simply care about the different screen resolutions and aspect ratios, so my first HTML5 game was made to perfectly fit my iPad 2 tablet. When I finally showed it to sponsors, most of them said something like "Hey, I like the game, but unfortunately it does not look that good on my iPhone". "Don't worry", I said, "you'll get the game optimized for iPad and iPhone". Unfortunately, it did not look that good on the Galaxy Note. Neither did it on the Samsung S4. You can imagine the rest of this story. I found myself almost rewriting the game with a series of if.. then.. else loops, trying to make it look good on any device. This is why you should make a cross-platform game: To code once and rule them all. Focus on game development and let a framework do the dirty work for you. What Cocos2d-JS is and how it works Cocos2d-JS is a free open source 2D game framework. It can help you to develop cross-platform browser games and native applications. This framework allows you to write games in JavaScript. So, if you have already developed JavaScript applications, you don't have to learn a new language from scratch. Throughout this book, you will learn how to create almost any kind of cross-platform game using a familiar and intuitive language. Requirements to run Cocos2d-JS Before you start, let's see what software you need to install on your computer in order to start developing with Cocos2d-JS: Firstly, you need a text editor. The official IDE for Cocos2d-JS coding is Cocos Code IDE, which you can download for free at http://www.cocos2d-x.org/products/codeide. It features auto completion, code hinting, and some more interesting characteristics to speed up your coding. If you are used to your favorite code editor, that's fine. There are plenty of them, but I personally use PSPad (you can find this at http://www.pspad.com/) on my Windows machine and TextWrangler (you can find this at http://www.barebones.com/products/textwrangler/) on the Mac. They are both free and easy to use, so you can download and have them installed in a matter of minutes. To test your Cocos2d-JS projects, you will need to install a web server on your computer to override security limits when running your project locally. I am using WAMP (http://www.wampserver.com/) on my Windows machine, and MAMP (http://www.mamp.info/) on the Mac. Again, both are free to use as you won't need the PRO version, which is also available for Mac computers. Explaining all the theory behind this is beyond the scope of this book, but you can find all the required information as well as the installation documentation on the official sites. If you prefer, you can test your projects directly online by uploading them on an FTP space you own and call them directly from the web. In this case, you don't need to have a web server installed on your computer, but I highly recommend using WAMP or MAMP instead. I personally use Google Chrome as the default browser to test my projects. As these projects are meant to be cross-platform games, it should run in the same way on every browser, so feel free to use the browser you prefer. The latest information about Cocos2d-JS can be found on the official page http://www.cocos2d-x.org/wiki/Cocos2d-JS, while the latest version can be downloaded at http://www.cocos2d-x.org/download. Cocos2d-JS is updated quite frequently, but at the time of writing, the latest stable release is v3.1. Although new releases always bring some changes, all examples included in this book should work fine with any release marked as 3.x as there aren't huge changes in the roadmap. You will notice the download file is a ZIP file that is greater than 250 MB. Don't worry. Most of the content of the package is made by docs, graphic assets, and examples, while the only required folder, at the moment, is the one called cocos2d-html5. The structure of your Cocos2d-JS project Every HTML5 game is basically a web page with some magic in it; this is what you are going to create with Cocos2d-JS: a web page with some magic in it. To perform this magic, a certain file structure needs to be created, so let's take a look at a screenshot of a folder with a Cocos2d-JS project in it: This is what you are going to build; to tell you the truth, this is a picture of the actual project folder I built for the example to be explained in this article, which is placed in the WAMP localhost folder on my computer. It couldn't be any more real. So, let's take a look at the files to be created: cocos2d-html5: This is the folder you will find in the zip archive. index.html: This is the web page that will contain the game. main.js:This is a file required by Cocos2d-JS with the Cocos2d-JS function calls to make the game start. You will create this within the next few minutes. project.json: This is a JavaScript Object Notation (JSON) with some basic configurations. This is what you need to make your game run. Well, almost, because the actual game will be placed in the src folder. Let's see a few other things first. Hello Cross-World The time has come, the boring theory has ended, and we can now start coding our first project. Let's begin! Firstly, create a page called index.html in the root of the game folder and write this HTML code: <!DOCTYPE html> <head>    <title>      My Awesome game    </title>    <script src="cocos2d-html5/CCBoot.js" type="text/javascript"> </script>    <script src="main.js" type="text/javascript"> </script> </head> <body style="padding:0;margin:0;background-color:#000000;"> </body> </html> There's nothing interesting in it as it is just plain HTML. Let's take a closer look at these lines to see what is going on: <script src=" cocos2d-html5/CCBoot.js "></script> Here, I am including the Cocos2d-JS boot file to make the framework start: <script src="main.js"></script> From the preceding line, this is where we call the script with the actual game we are going to build. Next, we have the following code: <canvas id="gameCanvas"></canvas> This is the canvas we will use to display the game. Notice here that the canvas does not have a width and height, as they will be defined by the game itself. Next is the creation of main.js: the only file we will call from our main index.html page. This is more of a configuration file rather than the game itself, so you won't code anything that is game-related at the moment. However, the file you are going to build will be the blueprint you will be using in all your Cocos2d-JS games. The content of main.js is as follows: cc.game.onStart = function(){   cc.view.setDesignResolutionSize(320, 480, cc.ResolutionPolicy.SHOW_ALL);   cc.director.runScene(new gameScene());};cc.game.run(); Don't worry about the code at the moment; it looks a lot more complicated than it really is. At the moment, the only line we have to worry about is the one that defines the resolution policy. One of the most challenging tasks in cross-platform development is to provide a good gaming experience, no matter what browser or what device the game is running on. However, the problem here is that each device has its own resolution, screen size, and ratio. Cocos2d-JS allows us to handle different resolutions in a similar way web designers do when building responsive design. At the moment, we just want to adapt the game canvas to fit the browser window while targeting the most popular resolution, which is 320x480 (portrait mode). That's what this line does: cc.view.setDesignResolutionSize(320, 480, cc.ResolutionPolicy.SHOW_ALL); Using these settings, you should be pretty sure that your game will run on every device, although you will be working in a low resolution. Also, have a look at this line: cc.director.runScene(new gameScene()); Basically, a Cocos2d-JS game is made by a scene where the game itself runs. There can be more scenes in the same game. Imagine a scene with the title screen, a scene with the game over screen, and a scene with the game itself. At the moment, you only have one scene called gameScene. Remember this name because you are going to use it later. Following this, the next required blueprint file you are going to build is project.json, which has some interesting settings. Let's take a look at the file first: {"debugMode" : 0,"showFPS" : false,"frameRate" : 60,"id" : "gameCanvas","renderMode" : 0,"engineDir":"cocos2d-html5/", "modules" : ["cocos2d"], "jsList" : [   "src/gamescript.js"]} What do these lines mean? Let's see them one by one: debugMode: This is the object key that determines the level of debug warnings. It has a range from 0 to 6. Leave it at 0 at the moment since the project is very simple and we won't make any errors. showFPS: This object can be true or false; it shows or hides the FPS meter on the screen. frameRate: This object sets the frame rate of your game. Set it to 60 to have a smooth game. id: This is the DOM element that is required to run the game. Do you remember you gave your canvas the gameCanvas id? Here you are. engineDir: This is the folder where Cocos2d-JS is installed. modules: This object engines the modules to load. At the moment, we only need the basic Cocos2d library. jsList: This is an array with the files used in the game. This means we are going to create our game in src/gamescript.js. Finally, we arrive at the game script itself. This is the one that will contain the actual game, gamescript.js, which at the moment is just a plain declaration of the game scene: var gameScene = cc.Scene.extend({onEnter:function () {   this._super();   console.log("my awesome game starts here");}}); Here, you want to save everything and call index.html page from your localhost (refer to your WAMP or MAMP docs) in your browser. If you now open the developer console, you should see: my awesome game starts here Congratulations! This means you have successfully managed to create a Cocos2d-JS template file to build your future games. Summary In this article we learned the importance of cross-platform games and how to make them using Cocos2d-JS.
Read more
  • 0
  • 0
  • 1344

article-image-downloading-and-understanding-construct-2
Packt
26 Dec 2014
19 min read
Save for later

Downloading and Understanding Construct 2

Packt
26 Dec 2014
19 min read
In this article by Aryadi Subagio, the author of Learning Construct 2, introduces you to Construct 2, makes you familiar with the interface and terms that Construct 2 uses, as well as gives you a quick overview of the event system. (For more resources related to this topic, see here.) About Construct 2 Construct 2 is an authoring tool that makes the process of game development really easy. It can be used by a variety of people, from complete beginners in game development to experts who want to make a prototype quickly or even use Construct 2 to make games faster than ever. It is created by Scirra Ltd, a company based in London, and right now, it can run on the Windows desktop platform, although you can export your games to multiple platforms. Construct 2 is an HTML5-based game editor with a lot of features enough for people beginning to work with game development to make their first 2D game. Some of them are: Multiple platforms to target: You can publish your game to desktop computers (PC, Mac, or Linux), to many mobile platforms (Android, iOS, Blackberry, Windows Phone 8.0, Tizen, and much more), and also on websites via HTML5. Also, if you have a developer's license, you can publish it on Nintendo's Wii U. No programming language required: Construct 2 doesn't use any programming language that is difficult to understand; instead, it relies on its event system, which is really easy for anyone, even without coding experience, to jump in. Built-in physics: Using Construct 2 means you don't need to worry about complicated physics functions; it's all built in Construct 2 and is easy to use! Can be extended (extensible): Many plugins have been written by third-party developers to add new functionalities to Construct 2. Note that writing plugins is outside the scope of this book. If you have a JavaScript background and want to try your hand at writing plugins, you can access the JavaScript SDK and documentation at https://www.scirra.com/manual/15/sdk. Special effects: There are a lot of built-in effects to make your game prettier! You can use Construct 2 to virtually create all kinds of 2D games, from platformer, endless run, tower defense, casual, top-down shooting, and many more. Downloading Construct 2 Construct 2 can be downloaded from Scirra's website (https://www.scirra.com/), which only requires you to click on the download button in order to get started. At the time of writing this book, the latest stable version is r184, and this tutorial is written using this version. Another great thing about Construct 2 is that it is actively developed, and the developer frequently releases beta features to gather feedback and perform bug testing. There are two different builds of Construct 2: beta build and stable build. Choosing which one to download depends on your preference when using Construct 2. If you like to get your hands on the latest features, then you should choose the beta build; just remember that beta builds often have bugs. If you want a bug-proof version, then choose the stable build, but you won't be the first one to use the new features. The installation process is really straightforward. You're free to skip this section if you like, because all you need to do is open the file and follow the instructions there. If you're installing a newer version of Construct 2, it will uninstall the older version automatically for you! Navigating through Construct 2 Now that we have downloaded and installed Construct 2, we can start getting our hands dirty and make games with it! Not so fast though. As Construct 2's interface is different compared to other game-making tools, we need to know how to use it. When you open Construct 2, you will see a start page as follows:   This start page is basically here to make it easier for you to return to your most recent projects, so if you just opened Construct 2, then this will be empty. What you need to pay attention to is the new project link on the left-hand side; click on it, and we'll start making games. Alternatively, you can click on File in the upper-left corner and then click on New. You'll see a selection of templates to start with, so understandably, this can be confusing if you don't know which one to pick. So, for now, just click on New empty project and then click on Open. Starting an empty project is good when you want to prototype your game. What you see in the screenshot now is an empty layout, which is the place where we'll make our games. This also represents how your game will look. It might be confusing to navigate the first time you see this, but don't worry; I'll explain everything you need to know for now by describing it piece by piece. The white part in the middle is the layout, because Construct 2 is a what you see is what you get kind of tool. This part represents how your game will look in the end. The layout is like your canvas; it's your workspace; it is where you design your levels, add your enemies, and place your floating coins. It is where you make your game. The take-home point here is that the layout size is not the same as the window size! The layout size can be bigger than the window size, but it can't be smaller than the window size. This is because the window size represents the actual game window. The dotted line is the border of the window size, so if you put a game object outside it, it won't be initially visible in the game, unless you scroll towards it. In the preceding screenshot, only the red plane is visible to the player. Players don't see the green spaceship because it's outside the game window. On the right-hand side, we have the Projects bar and the Objects bar. An Objects bar shows you all the objects that are used in the active layout. Note that an active layout is the one you focused on right now; this means that, at this very instance, we only have one layout. The Objects bar is empty because we haven't added any objects. The Projects bar helps in the structuring of your project, and it is structured as follows: All layouts are stored in the Layouts folder. Event sheets are stored in the Event sheets folder. All objects that are used in the project are stored in the Object types folder. All created families are in the Families folder. A family is a feature of Construct 2. The Sounds folder contains sound effects and audio files. The Music folder contains long background music. The difference between the Sounds folder and the Music folder is that the contents in the Music folder are streamed, while the files inside the Sounds folder are downloaded completely before they are played. This means if you put a long music track in the Sounds folder, it will take a few minutes for it to be played, but in the Music folder, it is immediately streamed. However, it doesn't mean that the music will be played immediately; it might need to buffer before playing. The Files folder contains other files that don't fit into the folders mentioned earlier. One example here is Icons. Although you can't rename or delete these folders, you can add subfolders inside them if you want. On the left-hand side, we have a Properties bar. There are three kinds of properties: layout properties, project properties, and object properties. The information showed in the Properties bar depends on what you clicked last. There is a lot of information here, so I think it's best to explain it as we go ahead and make our game, but for now, you can click on any part of the Properties bar and look at the bottom part of it for help. I'll just explain a bit about some basic things in the project properties: Name: This is your project's name; it doesn't have to be the same as the saved file's name. So, you can have the saved file as project_wing.capx and the project's name as Shadow wing. Version: This is your game's version number if you plan on releasing beta versions; make sure to change this first Description: Your game's short description; some application stores require you to fill this out before submitting ID: This is your game's unique identification; this comes in the com.companyname.gamename format, so your ID would be something like com.redstudio.shadowwing. Creating game objects To put it simply, everything in Construct 2 is a game object. This can range from the things that are visible on screen, which, for example, are sprites, text, particles, and sprite font, to the things that are not visible but are still used in the game, such as an array, dictionary, keyboard, mouse, gamepad, and many more. To create a new game object, you can either double-click anywhere on a layout (not on another object already present), or you can right-click on your mouse and select Insert new object. Doing either one of these will open an Insert New Object dialog, where you can select the object to be inserted. You can click on the Insert button or double-click on the object icon to insert it. There are two kinds of objects here: the objects that are inserted into the active layout and the objects that are inserted into the entire project. Objects that are visible on the screen are inserted into the active layout, and objects that are not visible on the screen are inserted into the entire project. If you look closely, each object is separated into a few categories such as Data & Storage, Form controls, General, and so on. I just want to say that you should pay special attention to the objects in the Form controls category. As the technology behind it is HTML5 and a Construct 2 game is basically a game made in JavaScript, objects such as the ones you see on web pages can be inserted into a Construct 2 game. These objects are the objects in the Form controls category. A special rule applies to the objects: we can't alter their layer order. This means that these objects are always on top of any other objects in the game. We also can't export them to platforms other than web platforms. So, if you want to make a cross-platform game, it is advised not to use the Form controls objects. For now, insert a sprite object by following these steps: After clicking on the Insert button, you will notice that your mouse cursor becomes a crosshair, and there's a floating label with the Layer 0 text. This is just a way for Construct 2 to tell you which layer you're adding to your object. Click your mouse to finally insert the object. Even if you add your object to a wrong layer, you can always move it later. When adding any object with a visual representation on screen, such as a sprite or a tiled background, Construct 2 automatically opens up its image-editing window. You can draw an image here or simply load it from a file created using a software. Click on X in the top-right corner of the window to close the window when you have finished drawing. You shouldn't worry here; this won't delete your object or image. Adding layers Layers are a great way to manage your objects' visual hierarchy. You can also add some visual effects to your game using layers. By default, your Layers bar is located at the same place as the Projects bar. You'll see two tabs here: Projects and Layers. Click on the Layers tab to open the Layers bar. From here, you can add new layers and rename, delete, and even reorganize them to your liking. You can do this by clicking on the + icon a few times to add new layers, and after this, you can reorganize them by dragging a layer up or down. Just like with Adobe products, you can also toggle the visibility of all objects in the same layer to make it easier while you're developing games. If you don't want to change or edit all objects in the same layer, which might be on a background layer for instance, you can lock this layer. Take a look at the following screenshot: There are two ways of referring to a layer: using its name (Layer 0, Layer 1, Layer 2, Layer 3) or its index (0, 1, 2, 3). As you can see from the previous screenshot, the index of a layer changes as you move a layer up or down its layer hierarchy (the layer first created isn't always the one with the index number 0). The layer with index 0 will always be at the bottom, and the one with the highest index will always be at the top, so remember this because it will come in handy when you make your games. The eye icon determines the visibility of the layer. Alternatively, you can also check the checkbox beside each layer's name. Objects from the invisible layer won't be visible in Construct 2 but will still visible when you play the game. The lock icon, beside the layer's name at the top, toggles between whether a layer is locked or not, so objects from locked layers can't be edited, moved, and selected. What is an event system? Construct 2 doesn't use a traditional programming language. Instead, it uses a unique style of programming called an event system. However, much like traditional programming languages, it works as follows: It executes commands from top to bottom It executes commands at every tick It has variables (a global and a local variable) It has a feature called function, which works in the same way as other functions in a traditional programming language, without having to go into the code An event system is used to control the objects in a layout. It can also be used to control the layout itself. An event system can be found inside the event sheet; you can access it by clicking on the event sheet tab at the top of the layout. Reading an event system I hope I haven't scared you all with the explanations of an event system. Please don't worry because it's really easy! There are two components to an event system: an event and an action. Events are things that occur in the game, and actions are the things that happen when there is an event. For a clearer understanding, take a look at the following screenshot where the event is taken from one of my game projects: The first event, the one with number 12, is a bullet on collision with an enemy, which means when any bullet collides with any enemy, the actions on its right-hand side will be executed. In this case, it will subtract the enemy's health, destroy the bullet object, and create a new object for a damage effect. The next event, number 13, is what happens when an enemy's health drops below zero; the actions will destroy the enemy and add points to the score variable. This is easy, right? Take a look at how we created the redDamage object; it says on layer "Game". Every time we create a new object through an action, we also need to specify on which layer it is created. As mentioned earlier, we can refer to a layer with its name or with its index number, so either way is fine. However, I usually use a layer's name, just in case I need to rearrange the layer's hierarchy later. If we use the layer's index (for example, index 1) we can rearrange the layer so that index 1 is different; this means that we will end up creating objects in the wrong layer. Earlier, I said that an event system executes commands from top to bottom. This is true except for one kind of event: a trigger. A trigger is an event that, instead of executing at every tick, waits for something to happen before it is executed. Triggers are events with a green arrow beside them (like the bullet on collision with enemy event shown earlier). As a result of this, unlike the usual events, it doesn't matter where the triggers are placed in the event system. Writing events Events are written on event sheets. When you create a new layout, you can choose to add a new event sheet to this new layout. If you choose to add an event sheet, you can rename it to the same name or one that is different from the layout. However, it is advised that you name the event sheets exactly same as the layout to make it clear which event sheet is associated with a layout. We can only link one event sheet to a layout from its properties, so if we want to add more event sheets to a layout, we must include them in that event sheet. To write an event, just perform the following steps: Click on the event sheet tab above the layout. You'll see an empty event sheet; to add events, simply click on the Add event link or right-click and select Add event. Note that from now, on I will refer to the action of adding a new step with words such as add event, add new event, or something similar. You'll see a new window with objects to create an event from; every time you add an event (or action), Construct 2 always gives you objects you can add an event (or action) from. This prevents you from doing something impossible, for example, trying to modify the value of a local variable outside of its scope. I will explain local variables shortly. Whether or not you have added an object, there will always be a system object to create an event from. This contains a list of events that you create directly from the game instead of from an object. Double-click on it, and you'll see a list of events you can create with a system object. There are a lot of events, and explaining them can take a long time. For now, if you're curious, there is an explanation of each event in the upper part of the window. Next, scroll down and look for an Every x seconds event. Double-click on it, enter 1.0 second, and click on Done. You should have the following event: To add an action to an event, just perform the following steps: Click on the Add action link beside an event. Click on an object you want to create an action from; for now, double-click on the systems object. Double-click on the Set layer background color action under the Layers & Layout category. Change the three numbers inside the bracket to 100, 200, and 50. Click on the Done button. You should have the following event: This action will change the background color of layer 0 to the one we set in the parameter, which is green. Also, because adding a screenshot every time gives you a code example, which would be troublesome, I will write my code example as follows: System every 1.0 seconds | System Restart layout The left-hand side of the code is the event, and the right-hand side of the code is the action. I think this is pretty clear. Creating a variable I said that I'm going to explain variables, and you might have noticed a global and local variables category when you added an action. A variable is like a glass or cup, but instead of water, it holds values. These values can be one of three types: Text, Number, or Boolean. Text: This type holds a value of letters, words, or a sentence. This can include numbers as well, but the numbers will be treated like a part of the word. Number: This type holds numerical values and can't store any alphabetical value. The numbers are treated like numbers, which means that mathematical operations can be performed on them. Boolean: This type only holds one of the two values: True or False. This is used to check whether a certain state of an object is true or false. To create a global variable, just right-click in an event sheet and select Add global variable. After that, you'll see a new window to add a global variable. Here's how to fill each field: Name: This is the name of the variable; no two variables can have the same name, and this name is case sensitive, which means exampleText is different from ExampleText. Type: This tells whether the variable is Text, Number, or Boolean. Only instance variables can have a Boolean type. Initial value: This is the variable's value when first created. A text type's value must be surrounded with a quote (" "). Description: This is an optional field; just in case the name isn't descriptive enough, additional explanation can be written here. After clicking on the OK button, you have created your new variable! This variable has a global scope; this means that it can be accessed from anywhere within the project, while a local variable only has a limited scope and can be accessed from a certain place in the event sheet. I will cover local variables in depth later in the book. You might have noticed that in the previous screenshot, the Static checkbox cannot be checked. This is because only local variables can be marked as static. One difference between global and local variables is that the local variable's value reverts to its initial value the next time the code is executed, while the global variable's value doesn't change until there's a code that changes it. A static local variable retains its value just like a global variable. All variables' values can be changed from events, both global and local, except the ones that are constant. Constant variables will always retain their initial value; they can never be changed. A constant variable can be used for a variable that has a value you don't want to accidentally rewrite later. Summary In this article, we learned about the features of Construct 2, its ease of use, and why it's perfect for people with no programming background. We learned about Construct 2's interface and how to create new layers in it. We know what objects are and how to create them. This article also introduced you to the event system and showed you how to write code in it. Now, you are ready to start making games with Construct 2! Resources for Article:  Further resources on this subject: Building Mobile Apps [article] Introducing variables [article] HTML5 Game Development – A Ball-shooting Machine with Physics Engine [article]
Read more
  • 0
  • 0
  • 2232
article-image-animations-cocos2d-x
Packt
23 Sep 2014
24 min read
Save for later

Animations in Cocos2d-x

Packt
23 Sep 2014
24 min read
In this article, created by Siddharth Shekhar, the author of Learning Cocos2d-x Game Development, we will learn different tools that can be used to animate the character. Then, using these animations, we will create a simple state machine that will automatically check whether the hero is falling or is being boosted up into the air, and depending on the state, the character will be animated accordingly. We will cover the following in this article: Animation basics TexturePacker Creating spritesheet for the player Creating and coding the enemy animation Creating the skeletal animation Coding the player walk cycle (For more resources related to this topic, see here.) Animation basics First of all, let's understand what animation is. An animation is made up of different images that are played in a certain order and at a certain speed, for example, movies that run images at 30 fps or 24 fps, depending on which format it is in, NTSC or PAL. When you pause a movie, you are actually seeing an individual image of that movie, and if you play the movie in slow motion, you will see the frames or images that make up to create the full movie. In games while making animations, we will do the same thing: adding frames and running them at a certain speed. We will control the images to play in a particular sequence and interval by code. For an animation to be "smooth", you should have at least 24 images or frames being played in a second, which is known as frames per second (FPS). Each of the images in the animation is called a frame. Let's take the example of a simple walk cycle. Each walk cycle should be of 24 frames. You might say that it is a lot of work, and for sure it is, but the good news is that these 24 frames can be broken down into keyframes, which are important images that give the illusion of the character walking. The more frames you add between these keyframes, the smoother the animation will be. The keyframes for a walk cycle are Contact, Down, Pass, and Up positions. For mobile games, as we would like to get away with as minimal work as possible, instead of having all the 24 frames, some games use just the 4 keyframes to create a walk animation and then speed up the animation so that player is not able to see the missing frames. So overall, if you are making a walk cycle for your character, you will create eight images or four frames for each side. For a stylized walk cycle, you can even get away with a lesser number of frames. For the animation in the game, we will create images that we will cycle through to create two sets of animation: an idle animation, which will be played when the player is moving down, and a boost animation, which will get played when the player is boosted up into the air. Creating animation in games is done using two methods. The most popular form of animation is called spritesheet animation and the other is called skeletal animation. Spritesheet animation Spritesheet animation is when you keep all the frames of the animation in a single file accompanied by a data file that will have the name and location of each of the frames. This is very similar to the BitmapFont. The following is the spritesheet we will be using in the game. For the boost and idle animations, each of the frames for the corresponding animation will be stored in an array and made to loop at a particular predefined speed. The top four images are the frames for the boost animation. Whenever the player taps on the screen, the animation will cycle through these four images appearing as if the player is boosted up because of the jetpack. The bottom four images are for the idle animation when the player is dropping down due to gravity. In this animation, the character will look as if she is blinking and the flames from the jetpack are reduced and made to look as if they are swaying in the wind. Skeletal animation Skeletal animation is relatively new and is used in games such as Rayman Origins that have loads and loads of animations. This is a more powerful way of making animations for 2D games as it gives a lot of flexibility to the developer to create animations that are fast to produce and test. In the case of spritesheet animations, if you had to change a single frame of the animation, the whole spritesheet would have to be recreated causing delay; imagine having to rework 3000 frames of animations in your game. If each frame was hand painted, it would take a lot of time to produce the individual images causing delay in production time, not to mention the effort and time in redrawing images. The other problem is device memory. If you are making a game for the PC, it would be fine, but in the case of mobiles where memory is limited, spritesheet animation is not a viable option unless cuts are made to the design of the game. So, how does skeletal animation work? In the case of skeletal animation, each item to be animated is stored in a separate spritesheet along with the data file for the locations of the individual images for each body part and object to be animated, and another data file is generated that positions and rotates the individual items for each of the frames of the animation. To make this clearer, look at the spritesheet for the same character created with skeletal animation: Here, each part of the body and object to be animated is a separate image, unlike the method used in spritesheet animation where, for each frame of animation, the whole character is redrawn. TexturePacker To create a spritesheet animation, you will have to initially create individual frames in Photoshop, Illustrator, GIMP or any other image editing software. I have already made it and have each of the images for the individual frames ready. Next, you will have to use a software to create spritesheets from images. TexturePacker is a very popular software that is used by industry professionals to create spritesheets. You can download it from https://www.codeandweb.com/. These are the same guys who made PhysicsEditor, which we used to make shapes for Box2D. You can use the trial version of this software. While downloading, choose the version that is compatible with your operating system. Fortunately, TexturePacker is available for all the major operating systems, including Linux. Refer to the following screenshot to check out the steps to use TexturePacker: Once you have downloaded TexturePacker, you have three options: you can click to try the full version for a week, or you can purchase the license, or click on the essential version to use in the trial version. In the trial version, some of the professional features are disabled, so I recommend trying the professional features for a week. Once you click the option, you should see the following interface: Texture packer has three panels; let's start from the right. The right-hand side panel will display the names of all the images that you select to create the spritesheet. The center panel is a preview window that shows how the images are packed. The left-hand side panel gives you options to store the packed texture and data file to be published to and decide the maximum size of the packed image. The Layout section gives a lot of flexibility to set up the individual images in TexturePacker, and then you have the advanced section. Let's look at some of the key items on the panel on the left. The display section The display section consists of the following options: Data Format: As we saw earlier, each exported file creates a spritesheet that has a collection of images and a data file that keeps track of the positions on the spritesheet. The data format usually changes depending upon the framework or engine. In TexturePacker, you can select the framework that you are using to develop the game, and TexturePacker will create a data file format that is compatible with the framework. If you look at the drop-down menu, you can see a lot of popular frameworks and engines in the list such as 2DToolkit, OGRE, Cocos2d, Corona SDK, LibGDX, Moai, Sparrow/Starling, SpriteKit, and Unity. You can also create a regular JSON file too if you wish. Java Script Object Notification (JSON) is similar to an XML file that is used to store and retrieve data. It is a collection of names and value pairs used for data interchanging. Data file: This is the location where you want the exported file to be placed. Texture format: Usually, this is set to .png, but you can select the one that is most convenient. Apart from PNG, you also have PVR, which is used so that people cannot view the image readily and also provides image compression. Png OPT file: This is used to set the quality of PNG images. Image format: This sets the RGB format to be used; usually, you would want this to be set at the default value. AutoSD: If you are going to create images for different resolutions, this option allows you to create resources depending on the different resolutions you are developing the game for, without the need for going into the graphics software, shrinking the images and packing them again for all the resolutions. Content protection: This protects the image and data file with an encryption key so that people can't steal spritesheets from the game file. The Geometry section The Geometry section consists of the following options: Max size: You can specify the maximum width and height of the spritesheet depending upon the framework. Usually, all frameworks allow up to 4092 x 4092, but it mostly depends on the device. Fixed size: Apparently, if you want a fixed size, you will go with this option. Size constraint: Some frameworks prefer the spritesheets to be in the power of 2 (POT), for example, 32x32, 64x64, 256x256, and so on. If this is the case, you need to select the size accordingly. For Cocos2d, you can choose any size. Scale: This is used to scale up or scale down the image. The Layout section The Layout section consists of the following options: Algorithm: This is the algorithm that will be used to make sure that the images you select to create the spritesheet are packed in the most efficient way. If you are using the pro version, choose MaxRects, but if you are using the essential version, you will have to choose Basic. Border Padding / Shape Padding: Border padding packs the gap between the border of the spritesheet and the image that it is surrounding. Shape padding is the padding between the individual images of the spritesheets. If you find that the images are getting overlapped while playing the animation in the game, you might want to increase the values to avoid overlapping. Trim: This removes the extra alpha that is surrounding the image, which would unnecessarily increase the image size of the spritesheet. Advanced features The following are some miscellaneous options in TexturePacker: Texture path: This appends the path of the texture file at the beginning of the texture name Clean transparent pixels: This sets the transparent pixels color to #000 Trim sprite names: This will remove the extension from the names of the sprites (.png and .jpg), so while calling for the name of the frame, you will not have to use extensions Creating a spritesheet for the player Now that we understand the different items in the TextureSettings panel of TexturePacker, let's create our spritesheet for the player animation from individual frames provided in the Resources folder. Open up the folder in the system and select all the images for the player that contains the idle and boost frames. There will be four images for each of the animation. Select all eight images and click-and-drag all the images to the Sprites panel, which is the right-most panel of TexturePacker. Once you have all the images on the Sprites panel, the preview panel at the center will show a preview of the spritesheet that will be created: Now on the TextureSettings panel, for the Data format option, select cocos2d. Then, in the Data file option, click on the folder icon on the right and select the location where you would like to place the data file and give the name as player_anim. Once selected, you will see that the Texture file location also auto populates with the same location. The data file will have a format of .plist and the texture file will have an extension of .png. The .plist format creates data in a markup language similar to XML. Although it is more common on Mac, you can use this data type independent of the platform you use while developing the game using Cocos2d-x. Keep the rest of the settings the same. Save the file by clicking on the save icon on the top to a location where the data and spritesheet files are saved. This way, you can access them easily the next time if you want to make the same modifications to the spritesheet. Now, click on the Publish button and you will see two files, player_anim.plist and player_anim.png, in the location you specified in the Data file and Location file options. Copy and paste these two files in the Resources folder of the project so that we can use these files to create the player states. Creating and coding enemy animation Now, let's create a similar spritesheet and data file for the enemy also. All the required files for the enemy frames are provided in the Resources folder. So, once you create the spritesheet for the enemy, it should look something like the following screenshot. Don't worry if the images are shown in the wrong sequence, just make sure that the files are numbered correctly from 1 to 4 and it is in the sequence the animations needs to be played in. Now, place the enemy_anim.png spritesheet and data file in the Resources folder in the directory and add the following lines of code in the Enemy.cpp file to animate the enemy:   //enemy animation       CCSpriteBatchNode* spritebatch = CCSpriteBatchNode::create("enemy_anim.png");    CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();    cache->addSpriteFramesWithFile("enemy_anim.plist");       this->createWithSpriteFrameName("enemy_idle_1.png");    this->addChild(spritebatch);             //idle animation    CCArray* animFrames = CCArray::createWithCapacity(4);      char str1[100] = {0};    for(int i = 1; i <= 4; i++)    {        sprintf(str1, "enemy_idle_%d.png", i);        CCSpriteFrame* frame = cache->spriteFrameByName( str1 );        animFrames->addObject(frame);    }           CCAnimation* idleanimation = CCAnimation::createWithSpriteFrames(animFrames, 0.25f);    this->runAction (CCRepeatForever::create(CCAnimate::create(idleanimation))) ; This is very similar to the code for the player. The only difference is that for the enemy, instead of calling the function on the hero, we call it to the same class. So, now if you build and run the game, you should see the enemy being animated. The following is the screenshot from the updated code. You can now see the flames from the booster engine of the enemy. Sadly, he doesn't have a boost animation but his feet swing in the air. Now that we have mastered the spritesheet animation technique, let's see how to create a simple animation using the skeletal animation technique. Creating the skeletal animation Using this technique, we will create a very simple player walk cycle. For this, there is a software called Spine by Esoteric Software, which is a very widely used professional software to create skeletal animations for 2D games. The software can be downloaded from the company's website at http://esotericsoftware.com/spine-purchase: There are three versions of the software available: the trial, essential, and professional versions. Although majority of the features of the professional version are available in the essential version, it doesn't have ghosting, meshes, free-form deformation, skinning, and IK pinning, which is in beta stage. The inclusion of these features does speed up the animation process and certainly takes out a lot of manual work for the animator or illustrator. To learn more about these features, visit the website and hover the mouse over these features to have a better understanding of what they do. You can follow along by downloading the trial version, which can be done by clicking the Download trial link on the website. Spine is available for all platforms including Windows, Mac, and Linux. So download it for the OS of your choice. On Mac, after downloading and running the software, it will ask to install X11, or you can download and install it from http://xquartz.macosforge.org/landing/. After downloading and installing the plugin, you can open Spine. Once the software is up and running, you should see the following window: Now, create a new project by clicking on the spine icon on the top left. As we can see in the screenshot, we are now in the SETUP mode where we set up the character. On the Tree panel on the right-hand side, in the Hierarchy pane, select the Images folder. After selecting the folder, you will be able to select the path where the individual files are located for the player. Navigate to the player_skeletal_anim folder where all the images are present. Once selected, you will see the panel populate with the images that are present in the folder, namely the following: bookGame_player_Lleg bookGame_player_Rleg bookGame_player_bazooka bookGame_player_body bookGame_player_hand bookGame_player_head Now drag-and-drop all the files from the Images folder onto the scene. Don't worry if the images are not in the right order. In the Draw Order dropdown in the Hierarchy panel, you can move around the different items by drag-and-drop to make them draw in the order that you want them to be displayed. Once reordered, move the individual images on the screen to the appropriate positions: You can move around the images by clicking on the translate button on the bottom of the screen. If you hover over the buttons, you can see the names of the buttons. We will now start creating the bones that we will use to animate the character. In the panel on the bottom of the Tools section, click on the Create button. You should now see the cursor change to the bone creation icon. Before you create a bone, you have to always select the bone that will be the parent. In this case, we select the root bone that is in the center of the character. Click on it and drag downwards and hold the Shift key at the same time. Click-and-drag downwards up to the end of the blue dress of the character; make sure that the blue dress is highlighted. Now release the mouse button. The end point of this bone will be used as the hip joint from where the leg bones will be created for the character. Now select the end of the newly created bone, which you made in the last step, and click-and-drag downwards again holding Shift at the same time to make a bone that goes all the way to the end of the leg. With the leg still getting highlighted, release the mouse button. To create the bone for the other leg, create a new bone again starting from end of the first bone and the hip joint, and while the other leg is selected, release the mouse button to create a bone for the leg. Now, we will create a bone for the hand. Select the root node, the node in the middle of the character while holding Shift again, and draw a bone to the hand while the hand is highlighted. Create a bone for the head by again selecting the root node selected earlier. Draw a bone from the root node to the head while holding Shift and release the mouse button once you are near the ear of the character and the head is highlighted. You will notice that we never created a bone for the bazooka. For the bazooka, we will make the hand as the parent bone so that when the hand gets rotated, the bazooka also rotates along. Click on the bazooka node on the Hierarchy panel (not the image) and drag it to the hand node in the skeleton list. You can rotate each of the bones to check whether it is rotating properly. If not, you can move either the bones or images around by locking either one of them in its place so that you can move or rotate the other freely by clicking either the bones or the images button in the compensate button at the bottom of the screen. The following is the screenshot that shows my setup. You can use it to follow and create the bones to get a more satisfying animation. To animate the character, click on the SETUP button on the top and the layout will change to ANIMATE. You will see that a new timeline has appeared at the bottom. Click on the Animations tab in Hierarchy and rename the animation name from animation to runCycle by double-clicking on it. We will use the timeline to animate the character. Click on the Dopesheet icon at the bottom. This will show all the keyframes that we have made for the animation. As we have not created any, the dopesheet is empty. To create our first keyframe, we will click on the legs and rotate both the bones so that it reflects the contact pose of the walk cycle. Now to set a keyframe, click on the orange-colored key icon next to Rotate in the Transform panel at the bottom of the screen. Click on the translate key, as we will be changing the translation as well later. Once you click on it, the dopesheet will show the bones that you just rotated and also show what changes you made to the bone. Here, we rotated the bone, so you will see Rotation under the bones, and as we clicked on the translate key, it will show the Translate also. Now, frame 24 is the same as frame 0. So, to create the keyframe at frame 24, drag the timeline scrubber to frame 24 and click on the rotate and translate keys again. To set the keyframe at the middle where the contact pose happens but with opposite legs, rotate the legs to where the opposite leg was and select the keys to create a keyframe. For frames 6 and 18, we will keep the walk cycle very simple, so just raise the character above by selecting the root node, move it up in the y direction and click the orange key next to the translate button in the Transform panel at the bottom. Remember that you have to click it once in frame 6 and then move the timeline scrubber to frame 18, move the character up again, and click on the key again to create keyframes for both frames 6 and 18. Now the dopesheet should look as follow: Now to play the animation in a loop, click on the Repeat Animation button to the right of the Play button and then on the Play button. You will see the simple walk animation we created for the character. Next, we will export the data required to create the animation in Cocos2d-x. First, we will export the data for the animation. Click on the Spine button on top and select Export. The following window should pop up. Select JSON and choose the directory in which you would want to save the file to and click on Export: That is not all; we have to create a spritesheet and data file just as we created one in texture packer. There is an inbuilt tool in Spine to create a packed spritesheet. Again, click on the Spine icon and this time select Texture Packer. Here, in the input directory, select the Images folder from where we imported all the images initially. For the output directory, select the location to where the PNG and data files should be saved to. If you click on the settings button, you will see that it looks very similar to what we saw in TexturePacker. Keep the default values as they are. Click on Pack and give the name as player. This will create the .png and .atlas files, which are the spritesheet and data file, respectively: You have three files instead of the two in TexturePacker. There are two data files and an image file. While exporting the JSON file, if you didn't give it a name, you can rename the file manually to player.json just for consistency. Drag the player.atlas, player.json, and player.png files into the project folder. Finally, we come to the fun part where we actually use the data files to animate the character. For testing, we will add the animations to the HelloWorldScene.cpp file and check the result. Later, when we add the main menu, we will move it there so that it shows as soon as the game is launched. Coding the player walk cycle If you want to test the animations in the current project itself, add the following to the HelloWorldScene.h file first: #include <spine/spine-cocos2dx.h> Include the spine header file and create a variable named skeletonNode of the CCSkeletalAnimation type: extension::CCSkeletonAnimation* skeletonNode; Next, we initialize the skeletonNode variable in the HelloWorldScene.cpp file:    skeletonNode = extension::CCSkeletonAnimation::createWithFile("player.json", "player.atlas", 1.0f);    skeletonNode->addAnimation("runCycle",true,0,0);    skeletonNode->setPosition(ccp(visibleSize.width/2 , skeletonNode->getContentSize().height/2));    addChild(skeletonNode); Here, we give the two data files into the createWithFile() function of CCSkeletonAnimation. Then, we initiate it with addAnimation and give it the animation name we gave when we created the animation in Spine, which is runCycle. We next set the position of the skeletonNode; we set it right above the bottom of the screen. Next, we add the skeletonNode to the display list. Now, if you build and run the project, you will see the player getting animated forever in a loop at the bottom of the screen: On the left, we have the animation we created using TexturePacker from CodeAndWeb, and in the middle, we have the animation that was created using Spine from Esoteric Software. Both techniques have their set of advantages, and it also depends upon the type and scale of the game that you are making. Depending on this, you can choose the tool that is more tuned to your needs. If you have a smaller number of animations in your game and if you have good artists, you could use regular spritesheet animations. If you have a lot of animations or don't have good animators in your team, Spine makes the animation process a lot less cumbersome. Either way, both tools in professional hands can create very good animations that will give life to the characters in the game and therefore give a lot of character to the game itself. Summary This article took a very brief look at animations and how to create an animated character in the game using the two of the most popular animation techniques used in games. We also looked at FSM and at how we can create a simple state machine between two states and make the animation change according to the state of the player at that moment. Resources for Article: Further resources on this subject: Moving the Space Pod Using Touch [Article] Sprites [Article] Cocos2d-x: Installation [Article]
Read more
  • 0
  • 0
  • 5088

article-image-components-unity
Packt
26 Aug 2014
13 min read
Save for later

Components in Unity

Packt
26 Aug 2014
13 min read
In this article by Simon Jackson, author of Mastering Unity 2D Game Development, we will have a walkthrough of the new 2D system and other new features. We will then understand some of the Unity components deeply. We will then dig into animation and its components. (For more resources related to this topic, see here.) Unity 4.3 improvements Unity 4.3 was not just about the new 2D system; there are also a host of other improvements and features with this release. The major highlights of Unity 4.3 are covered in the following sections. Improved Mecanim performance Mecanim is a powerful tool for both 2D and 3D animations. In Unity 4.3, there have been many improvements and enhancements, including a new game object optimizer that ensures objects are more tightly bound to their skeletal systems and removes unnecessary transform holders. Thus making Mecanim animations lighter and smoother. Refer to the following screenshot: In Unity 4.3, Mecanim also adds greater control to blend animations together, allowing the addition of curves to have smooth transitions, and now it also includes events that can be hooked into at every step. The Windows Phone API improvements and Windows 8.1 support Unity 4.2 introduced Windows Phone and Windows 8 support, since then things have been going wild, especially since Microsoft has thrown its support behind the movement and offered free licensing for the existing Pro owners. Refer to the following screenshot: Unity 4.3 builds solidly on the v4 foundations by bringing additional platform support, and it closes some more gaps between the existing platforms. Some of the advantages are as follows: The emulator is now fully supported with Windows Phone (new x86 phone build) It has more orientation support, which allows even the splash screens to rotate properly and enabling pixel perfect display It has trial application APIs for both Phone and Windows 8 It has improved sensors and location support On top of this, with the recent release of Windows 8.1, Unity 4.3 now also supports Windows 8.1 fully; additionally, Unity 4.5.3 will introduce support Windows Phone 8.1 and universal projects. Dynamic Nav Mesh (Pro version only) If you have only been using the free version of Unity till now, you will not be aware of what a Nav Mesh agent is. Nav Meshes are invisible meshes that are created for your 3D environment at the build time to simplify path finding and navigation for movable entities. Refer to the following screenshot: You can, of course, create the simplified models for your environment and use them in your scenes; however, every time you change your scene, you need to update your navigation model. Nav Meshes simply remove this overhead. Nav Meshes are crucial, especially in larger environments where collision and navigation calculations can make the difference between your game running well or not. Unity 4.3 has improved this by allowing more runtime changes to the dynamic Nav Mesh, allowing you to destroy parts of your scene that alter the walkable parts of your terrain. Nav Mesh calculations are also now multithreaded to give even an even better speed boost to your game. Also, there have been many other under-the-hood fixes and tweaks. Editor updates The Unity editor received a host of updates in Unity 4.3 to improve the performance and usability of the editor, as you can see in the following demo screenshot. Granted most of the improvements are behind the scenes. The improved Unity Editor GUI with huge improvements The editor refactored a lot of the scripting features on the platform, primarily to reduce the code complexity required for a lot of scripting components, such as unifying parts of the API into single components. For example, the LookLikeControls and LookLikeInspector options have been unified into a single LookLike function, which allows easier creation of the editor GUI components. Further simplification of the programmable editor interface is an ongoing task and a lot of headway is being made in each release. Additionally, the keyboard controls have been tweaked to ensure that the navigation works in a uniform way and the sliders/fields work more consistently. MonoDevelop 4.01 Besides the editor features, one of the biggest enhancements has to be the upgrade of the MonoDevelop editor (http://monodevelop.com/), which Unity supports and is shipped with. This has been a long running complaint for most developers simply due to the brand new features in the later editions. Refer to the following screenshot: MonoDevelop isn't made by Unity; it's an open source initiative run by Xamarin hosted on GitHub (https://github.com/mono/monodevelop) for all the willing developers to contribute and submit fixes to. Although the current stable release is 4.2.1, Unity is not fully up to date. Hopefully, this recent upgrade will mean that Unity can keep more in line with the future versions of this free tool. Sadly, this doesn't mean that Unity has yet been upgraded from the modified V2 version of the Mono compiler (http://www.mono-project.com/Main_Page) it uses to the current V3 branch, most likely, due to the reduced platform and the later versions of the Mono support. Movie textures Movie textures is not exactly a new feature in Unity as it has been available for some time for platforms such as Android and iOS. However, in Unity 4.3, it was made available for both the new Windows 8 and Windows Phone platforms. This adds even more functionality to these platforms that were missing in the initial Unity 4.2 release where this feature was introduced. Refer to the following screenshot: With movie textures now added to the platform, other streaming features are also available, for example, webcam (or a built-in camera in this case) and microphone support were also added. Understanding components Components in Unity are the building blocks of any game; almost everything you will use or apply will end up as a component on a GameObject inspector in a scene. Until you build your project, Unity doesn't know which components will be in the final game when your code actually runs (there is some magic applied in the editor). So, these components are not actually attached to your GameObject inspector but rather linked to them. Accessing components using a shortcut Now, in the previous Unity example, we added some behind-the-scenes trickery to enable you to reference a component without first discovering it. We did this by adding shortcuts to the MonoBehavior class that the game object inherits from. You can access the components with the help of the following code: this.renderer.collider.attachedRigidbody.angularDrag = 0.2f; What Unity then does behind the scenes for you is that it converts the preceding code to the following code: var renderer = this.GetComponent<Renderer>(); var collider = renderer.GetComponent<Collider>(); var ridgedBody = collider.GetComponent<Rigidbody>(); ridgedBody.angularDrag = 0.2f; The preceding code will also be the same as executing the following code: GetComponent<Renderer>().GetComponent<Collider>().GetComponent<Rigidbody>().angularDrag = 0.2f; Now, while this is functional and working, it isn't very performant or even a best practice as it creates variables and destroys them each time you use them; it also calls GetComponent for each component every time you access them. Using GetComponent in the Start or Awake methods isn't too bad as they are only called once when the script is loaded; however, if you do this on every frame in the update method, or even worse, in FixedUpdate methods, the problem multiplies; not to say you can't, you just need to be aware of the potential cost of doing so. A better way to use components – referencing Now, every programmer knows that they have to worry about garbage and exactly how much memory they should allocate to objects for the entire lifetime of the game. To improve things based on the preceding shortcut code, we simply need to manually maintain the references to the components we want to change or affect on a particular object. So, instead of the preceding code, we could simply use the following code: Rigidbody myScriptRigidBody; void Awake() { var renderer = this.GetComponent<Renderer>(); var collider = renderer.GetComponent<Collider>(); myScriptRigidBody = collider.GetComponent<Rigidbody>(); } void Update() { myScriptRigidBody.angularDrag = 0.2f * Time.deltaTime; } This way the RigidBody object that we want to affect can simply be discovered once (when the scripts awakes); then, we can just update the reference each time a value needs to be changed instead of discovering it every time. An even better way Now, it has been pointed out (by those who like to test such things) that even the GetComponent call isn't as fast as it should be because it uses C# generics to determine what type of component you are asking for (it's a two-step process: first, you determine the type and then get the component). However, there is another overload of the GetComponent function in which instead of using generics, you just need to supply the type (therefore removing the need to discover it). To do this, we will simply use the following code instead of the preceding GetComponent<>: myScriptRigidBody =(Rigidbody2D)GetComponent(typeof(Rigidbody2D)); The code is slightly longer and arguably only gives you a marginal increase, but if you need to use every byte of the processing power, it is worth keeping in mind. If you are using the "." shortcut to access components, I recommend that you change that practice now. In Unity 5, they are being removed. There will, however, be a tool built in the project's importer to upgrade any scripts you have using the shortcuts that are available for you. This is not a huge task, just something to be aware of; act now if you can! Animation components All of the animation in the new 2D system in Unity uses the new Mecanim system (introduced in Version 4) for design and control, which once you get used to is very simple and easy to use. It is broken up into three main parts: animation controllers, animation clips, and animator components. Animation controllers Animation controllers are simply state machines that are used to control when an animation should be played and how often, including what conditions control the transition between each state. In the new 2D system, there must be at least one controller per animation for it to play, and controllers can contain many animations as you can see here with three states and transition lines between them: Animation clips Animation clips are the heart of the animation system and have come very far from their previous implementation in Unity. Clips were used just to hold the crafted animations of the 3D models with a limited ability to tweak them for use on a complete 3D model: The new animation dope sheet system (as shown in the preceding screenshot) is very advanced; in fact, now it tracks almost every change in the inspector for sprites, allowing you to animate just about everything. You can even control which sprite from a spritesheet is used for each frame of the animation. The preceding screenshot shows a three-frame sprite animation and a modified x position modifier for the middle image, giving a hopping effect to the sprite as it runs. This ability of the dope sheet system implies there is less burden on the shoulders of art designers to craft complex animations as the animation system itself can be used to produce a great effect. Sprites don't have to be picked from the same spritesheet to be animated. They can come from individual textures or picked from any spritesheet you have imported. The Animator component To use the new animation prepared in a controller, you need to apply it to a game object in the scene. This is done through the Animator component, as shown here: The only property we actually care about in 2D is the Controller property. This is where we attach the controller we just created. Other properties only apply to the 3D humanoid models, so we can ignore them for 2D. For more information about the complete 3D Mecanim system, refer to the Unity Learn guide at http://unity3d.com/learn/tutorials/modules/beginner/animation. Animation is just one of the uses of the Mecanim system. Setting up animation controllers So, to start creating animations, you first need an animation controller in order to define your animation clips. As stated before, this is just a state machine that controls the execution of animations even if there is only one animation. In this case, the controller runs the selected animation for as long as it's told to. If you are browsing around the components that can be added to the game object, you will come across the Animator component, which takes a single animation clip as a parameter. This is the legacy animation system for backward compatibility only. Any new animation clip created and set to this component will not work; it will simply generate a console log item stating The AnimationClip used by the Animation component must be marked as Legacy. So, in Unity 4.3 onwards, just avoid this. Creating an animation controller is just as easy as any other game object. In the Project view, simply right-click on the view and select Create | Animator Controller. Opening the new animation will show you the blank animator controller in the Mecanim state manager window, as shown in the following screenshot: There is a lot of functionality in the Mecanim state engine, which is largely outside the scope of this article. Check out for more dedicated books on this, such as Unity 4 Character Animation with Mecanim, Jamie Dean, Packt Publishing. If you have any existing clips, you can just drag them to the Mecanim controller's Edit window; alternatively, you can just select them in the Project view, right-click on them, and select From selected clip under Create. However, we will cover more of this later in practice. Once you have a controller, you can add it to any game object in your project by clicking on Add Component in the inspector or by navigating to Component | Create and Miscellaneous | Animator and selecting it. Then, you can select your new controller as the Controller property of the animator. Alternatively, you can just drag your new controller to the game object you wish to add it to. Clips in a controller are bound to the spritesheet texture of the object the controller is attached to. Changing or removing this texture will prevent the animation from being displayed correctly. However, it will appear as it's still running. So with a controller in place, let's add some animation to it. Summary In this article, we did a detailed analysis of the new 2D features added in Unity 4.3. Then we overviewed all the main Unity components. Resources for Article: Further resources on this subject: Parallax scrolling [article] What's Your Input? [article] Unity 3-0 Enter the Third Dimension [article]
Read more
  • 0
  • 0
  • 3380