Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - 2D Game Development

64 Articles
article-image-adding-animations
Packt
19 Mar 2014
10 min read
Save for later

Adding Animations

Packt
19 Mar 2014
10 min read
(For more resources related to this topic, see here.) Exploring 3D hierarchies The ability to parent objects among one another is a versatile feature in Unity3D. So far, in this article, we have seen how hierarchies can be used as a means of associating objects with one another and organizing them into hierarchies. One example of this is the character Prefabs with their child hats we developed for the player Prefab and the racers. In this example, by dragging-and-dropping the hat object onto the player's body, we associated the hat with the object body by putting the child into the parent's coordinate system. After doing this, we saw that the rotations, translations, and scales of the body's transform component were propagated to the hat. In practice, this is how we attach objects to one another in 3D games, that is, by parenting them to different parents and thereby changing their coordinate systems or frames of reference. Another example of using hierarchies as a data structure was for collections. Examples of this are the splineData collections we developed for the NPCs. These objects had a single game object at the head and a collection of data as child objects. We could then perform operations on the head, which lets us process all of the child objects in the collection (in our case, installing the way points). A third use of hierarchies was for animation. Since rotations, translations, and scales that are applied to a parent transform are propagated to all of the children, we have a way of developing fine-tuned motion for a hierarchy of objects. It turns out that characters in games and animated objects alike use this hierarchy of transforms technique to implement their motion. Skinned meshes in Unity3D A skinned mesh is a set of polygons whose positions and rotations are computed based on the positions of the various transforms in a hierarchy. Instead of each GameObject in the hierarchy have its own mesh, a single set of polygons is shared across a number of transforms. This results in a single mesh that we call a skin because it envelops the transforms in a skin-like structure. It turns out that this type of mesh is great for in-game characters because it moves like skin. We will now use www.mixamo.com to download some free models and animations for our game. Acquiring and importing models Let's download a character model for the hero of our game. Open your favorite Internet browser and go to www.mixamo.com. Click on Characters and scroll through the list of skinned models. From the models that are listed free, choose the one that you want to use. At the time of writing this article, we chose Justin from the free models available, as shown in the following screenshot: Click on Justin and you will be presented with a preview window of the character on the next page. Click on Download to prepare the model for download. Note, you may have to sign up for an account on the website to continue. Once you have clicked on Download, the small downloads pop-up window at the bottom-right corner of the screen will contain your model. Open the tab and select the model. Make sure to set the download format to FBX for Unity (.fbx) and then click on Download again. FBX (Filmbox)—originally created by a company of the same name and now owned by AutoDesk—is an industry standard format for models and animations. Congratulations! You have downloaded the model we will use for the main player character. While this model will be downloaded and saved to the Downloads folder of your browser, go back to the character select page and choose two more models to use for other NPCs in the game. At the time of writing this article, we chose Alexis and Zombie from the selection of free models, as shown in the following screenshot: Go to Unity, create a folder in your Project tab named Chapter8, and inside this folder, create a folder named models. Right-click on this folder and select Open In Explorer. Once you have the folder opened, drag-and-drop the two character models from your Download folder into the models folder. This will trigger the process of importing a model into Unity, and you should then see your models in your Project view as shown in the following screenshot: Click on each model in the models folder, and note that the Preview tab shows a t-pose of the model as well as some import options. In the Inspector pane, under the Rig tab, ensure that Animation Type is set to Humanoid instead of Generic. The various rig options tell Unity how to animate the skinned mesh on the model. While Generic would work, choosing Humanoid will give us more options when animating under Mechanim. Let Unity create the avatar definition file for you and you can simply click on Apply to change the Rig settings, as shown in the following screenshot: We can now drag-and-drop your characters to the game from the Project tab into the Scene view of the level, as shown in the following screenshot: Congratulations! You have successfully downloaded, imported, and instantiated skinned mesh models. Now, let's learn how to animate them, and we will do this starting with the main player's character. Exploring the Mechanim animation system The Mechanim animation system allows the Unity3D user to integrate animations into complex state machines in an intuitive and visual way! It also has advanced features that allow the user to fine-tune the motion of their characters, right from the use of blend trees to mix, combine, and switch between animations as well as Inverse Kinematics (IK) to adjust the hands and feet of the character after animating. To develop an FSM for our main player, we need to consider the gameplay, moves, and features that the player represents in the game. Choosing appropriate animations In level one, our character needs to be able to walk around the world collecting flags and returning them to the flag monument. Let's go back to www.mixamo.com, click on Animations, and download the free Idle, Gangnam Style, and Zombie Running animations, as shown in the following screenshot: Building a simple character animation FSM Let's build an FSM that the main character will use. To start, let's develop the locomotion system. Import the downloaded animations to a new folder named anims, in Chapter8. If you downloaded the animations attached to a skin, don't worry. We can remove it from the model, when it is imported, and apply it to the animation FSM that you will build. Open the scene file from the first gameplay level TESTBED1. Drag-and-drop the Justin model from the Projects tab into the Scene view and rename it Justin. Click on Justin and add an Animator component. This is the component that drives a character with the Mechanim system. Once you have added this component, you will be able to animate the character with the Mechanim system. Create a new animator controller and call it JustinController. Drag-and-drop JustinController into the controller reference on the Animator component of the Justin instance. The animator controller is the file that will store the specific Mechanim FSM that the Animator component will use to drive the character. Think of it as a container for the FSM. Click on the Justin@t-pose model from the Project tab, and drag-and-drop the avatar definition file from Model to the Avatar reference on the Animator component on the Justin instance. Go to the Window drop-down menu and select Animator. You will see a new tab open up beside the Scene view. With your Justin model selected, you should see an empty Animator panel inside the tab, as shown in the following screenshot: Right now our Justin model has no animations in his FSM. Let's add the idle animation (named Idle_1) from the Adam model we downloaded. You can drag-and-drop it from the Project view to any location inside this panel. That's all there is to it! Now, we have a single anim FSM attached to our character. When you play the game now, it should show Justin playing the Idle animation. You may notice that the loop doesn't repeat or cycle repeatedly. To fix this, you need to duplicate the animation and then select the Loop Pose checkbox. Highlight the animation child object idle_1 and press the Ctrl + D shortcut to duplicate it. The duplicate will appear outside of the hierarchy. You can then rename it to a name of your choice. Let's choose Idle as shown in the following screenshot: Now, click on Idle, and in the Inspector window, make sure that Loop Pose is selected. Congratulations! Using this Idle animation now results in a character who idles in a loop. Let's take a look at adding the walk animation. Click on the Zombie Running animation, which is a child asset of the Zombie model, and duplicate it such that a new copy appears in the Project window. Rename this copy Run. Click on this animation and make sure to check the Loop Pose checkbox so that the animation runs in cycles. Drag-and-drop the Run animation into the Animator tab. You should now have two animations in your FSM, with the default animation still as Idle; if you run the game, Justin should still just be idle. To make him switch animations, we need to do the following: Add some transitions to the Run animation from the Idle animation and vice versa. Trigger the transitions from a script. You will want to switch from the Idle to the Run animation when the player's speed (as determined from the script) is greater than a small number (let's say 0.1 f). Since the variable for speed only lives in the script, we will need a way for the script to communicate with the animation, and we will do this with parameters. In your Animator tab, note that the FSM we are developing lives in the Base Layer screen. While it is possible to add multiple animation layers by clicking on the + sign under Base Layer, this would allow the programmer to design multiple concurrent animation FSMs that could be used to develop varying degrees/levels of complex animation. Add a new parameter by clicking on the + sign beside the Parameters panel. Select float from the list of datatypes. You should now see a new parameter. Name this speed as shown in the following screenshot: Right-click on the Idle Animation and select Make Transition. You will now see that a white arrow is visible, which extends from Idle, that tracks the position of your mouse pointer. If you now click on the Run animation, the transition from Idle to Run will lock into place. Left-click on the little white triangle of the transition and observe the inspector for the transition itself. Scroll down in the Inspector window to the very bottom and set the condition for the transition to speed greater than 0.1, as shown in the following screenshot: Make the transition from the Run animation cycle back to the Idle cycle by following the same procedure. Right-click on the Run animation to start the transition. Then, left-click on Idle. After this, left-click on the transition once it is locked into place. Then, when the transition is activated in Conditions, set its speed to less than 0.09. Congratulations! Our character will now transition from Idle to Run when the speed crosses the 0.1 threshold. The transition is a nice blend from the first animation to the second over a brief period of time, and this is indicated in the transition graph.
Read more
  • 0
  • 0
  • 4554

article-image-getting-started-cocos2d-x
Packt
19 Oct 2015
11 min read
Save for later

Getting started with Cocos2d-x

Packt
19 Oct 2015
11 min read
 In this article written by Akihiro Matsuura, author of the book Cocos2d-x Cookbook, we're going to install Cocos2d-x and set up the development environment. The following topics will be covered in this article: Installing Cocos2d-x Using Cocos command Building the project by Xcode Building the project by Eclipse Cocos2d-x is written in C++, so it can build on any platform. Cocos2d-x is open source written in C++, so we can feel free to read the game framework. Cocos2d-x is not a black box, and this proves to be a big advantage for us when we use it. Cocos2d-x version 3, which supports C++11, was only recently released. It also supports 3D and has an improved rendering performance. (For more resources related to this topic, see here.) Installing Cocos2d-x Getting ready To follow this recipe, you need to download the zip file from the official site of Cocos2d-x (http://www.cocos2d-x.org/download). In this article we've used version 3.4 which was the latest stable version that was available. How to do it... Unzip your file to any folder. This time, we will install the user's home directory. For example, if the user name is syuhari, then the install path is /Users/syuhari/cocos2d-x-3.4. We call it COCOS_ROOT. The following steps will guide you through the process of setting up Cocos2d-x: Open the terminal Change the directory in terminal to COCOS_ROOT, using the following comand: $ cd ~/cocos2d-x-v3.4 Run setup.py, using the following command: $ ./setup.py The terminal will ask you for NDK_ROOT. Enter into NDK_ROOT path. The terminal will will then ask you for ANDROID_SDK_ROOT. Enter the ANDROID_SDK_ROOT path. Finally, the terminal will ask you for ANT_ROOT. Enter the ANT_ROOT path. After the execution of the setup.py command, you need to execute the following command to add the system variables: $ source ~/.bash_profile Open the .bash_profile file, and you will find that setup.py shows how to set each path in your system. You can view the .bash_profile file using the cat command: $ cat ~/.bash_profile We now verify whether Cocos2d-x can be installed: Open the terminal and run the cocos command without parameters. $ cocos If you can see a window like the following screenshot, you have successfully completed the Cocos2d-x install process. How it works... Let's take a look at what we did throughout the above recipe. You can install Cocos2d-x by just unzipping it. You know setup.py is only setting up the cocos command and the path for Android build in the environment. Installing Cocos2d-x is very easy and simple. If you want to install a different version of Cocos2d-x, you can do that too. To do so, you need to follow the same steps that are given in this recipe, but which will be for a different version. There's more... Setting up the Android environment  is a bit tough. If you started to develop at Cocos2d-x soon, you can turn after the settings part of Android. And you would do it when you run on Android. In this case, you don't have to install Android SDK, NDK, and Apache. Also, when you run setup.py, you only press Enter without entering a path for each question. Using the cocos command The next step is using the cocos command. It is a cross-platform tool with which you can create a new project, build it, run it, and deploy it. The cocos command works for all Cocos2d-x supported platforms. And you don't need to use an IDE if you don't want to. In this recipe, we take a look at this command and explain how to use it. How to do it... You can use the cocos command help by executing it with the --help parameter, as follows: $ cocos --help We then move on to generating our new project: Firstly, we create a new Cocos2d-x project with the cocos new command, as shown here: $ cocos new MyGame -p com.example.mygame -l cpp -d ~/Documents/ The result of this command is shown the following screenshot: Behind the new parameter is the project name. The other parameters that are mentioned denote the following: MyGame is the name of your project. -p is the package name for Android. This is the application id in Google Play store. So, you should use the reverse domain name to the unique name. -l is the programming language used for the project. You should use "cpp" because we will use C++. -d is the location in which to generate the new project. This time, we generate it in the user's documents directory. You can look up these variables using the following command: $ cocos new —help Congratulations, you can generate your new project. The next step is to build and run using the cocos command. Compiling the project If you want to build and run for iOS, you need to execute the following command: $ cocos run -s ~/Documents/MyGame -p ios The parameters that are mentioned are explained as follows: -s is the directory of the project. This could be an absolute path or a relative path. -p denotes which platform to run on.If you want to run on Android you use -p android. The available options are IOS, android, win32, mac, and linux. You can run cocos run –help for more detailed information. The result of this command is shown in the following screenshot: You can now build and run iOS applications of cocos2d-x. However, you have to wait for a long time if this is your first time building an iOS application. That's why it takes a long time to build cocos2d-x library, depending on if it was clean build or first build. How it works... The cocos command can create a new project and build it. You should use the cocos command if you want to create a new project. Of course, you can build by using Xcode or Eclipse. You can easier of there when you develop and debug. There's more... The cocos run command has other parameters. They are the following: --portrait will set the project as a portrait. This command has no argument. --ios-bundleid will set the bundle ID for the iOS project. However, it is not difficult to set it later. The cocos command also includes some other commands, which are as follows: The compile command: This command is used to build a project. The following patterns are useful parameters. You can see all parameters and options if you execute cocos compile [–h] command. cocos compile [-h] [-s SRC_DIR] [-q] [-p PLATFORM] [-m MODE] The deploy command: This command only takes effect when the target platform is android. It will re-install the specified project to the android device or simulator. cocos deploy [-h] [-s SRC_DIR] [-q] [-p PLATFORM] [-m MODE] The run command continues to compile and deploy commands. Building the project by Xcode Getting ready Before building the project by Xcode, you require Xcode with an iOS developer account to test it on a physical device. However, you can also test it on an iOS simulator. If you did not install Xcode, you can get it from Mac App Store. Once you have installed it, get it activated. How to do it... Open your project from Xcode. You can open your project by double-clicking on the file placed at: ~/Documents/MyGame/proj.ios_mac/MyGame.xcodeproj. Build and Run by Xcode You should select an iOS simulator or real device on which you want to run your project. How it works... If this is your first time building, it will take a long time. But continue to build with confidence as it's the first time. You can develop your game faster if you develop and debug it using Xcode rather than Eclipse. Building the project by Eclipse Getting ready You must finish the first recipe before you begin this step. If you have not finished it yet, you will need to install Eclipse. How to do it... Setting up NDK_ROOT: Open the preference of Eclipse Open C++ | Build | Environment Click on Add and set the new variable, name is NDK_ROOT, value is NDK_ROOT path. Importing your project into Eclipse: Open the file and click on Import Go to Android | Existing Android Code into Workspace Click on Next Import the project to Eclipse at ~/Documents/MyGame/proj.android. Importing Cocos2d-x library into Eclipse Perform the same steps from Step 3 to Step 4. Import the project cocos2d lib at ~/Documents/MyGame/cocos2d/cocos/platform/android/java, using the folowing command: importing cocos2d lib Build and Run Click on Run icon The first time, Eclipse asks you to select a way to run your application. You select Android Application and click on OK, as shown in the following screenshot: If you connected the Android device on your Mac, you can run your game on your real device or an emulator. The following screenshot shows that it is running it on Nexus5. If you added cpp files into your project, you have to modify the Android.mk file at ~/Documenst/MyGame/proj.android/jni/Android.mk. This file is needed to build NDK. This fix is required to add files. The original Android.mk would look as follows: LOCAL_SRC_FILES := hellocpp/main.cpp ../../Classes/AppDelegate.cpp ../../Classes/HelloWorldScene.cpp If you added the TitleScene.cpp file, you have to modify it as shown in the following code: LOCAL_SRC_FILES := hellocpp/main.cpp ../../Classes/AppDelegate.cpp ../../Classes/HelloWorldScene.cpp ../../Classes/TitleScene.cpp The preceding example shows an instance of when you add the TitleScene.cpp file. However, if you are also adding other files, you need to add all the added files. How it works... You get lots of errors when importing your project into Eclipse, but don't panic. After importing cocos2d-x library, errors soon disappear. This allows us to set the path of NDK, Eclipse could compile C++. After you modified C++ codes, run your project in Eclipse. Eclipse automatically compiles C++ codes, Java codes, and then runs. It is a tedious task to fix Android.mk again to add the C++ files. The following code is original Android.mk: LOCAL_SRC_FILES := hellocpp/main.cpp ../../Classes/AppDelegate.cpp ../../Classes/HelloWorldScene.cpp LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes The following code is customized Android.mk that adds C++ files automatically. CPP_FILES := $(shell find $(LOCAL_PATH)/../../Classes -name *.cpp) LOCAL_SRC_FILES := hellocpp/main.cpp LOCAL_SRC_FILES += $(CPP_FILES:$(LOCAL_PATH)/%=%) LOCAL_C_INCLUDES := $(shell find $(LOCAL_PATH)/../../Classes -type d) The first line of the code gets C++ files to the Classes directory into CPP_FILES variable. The second and third lines add C++ files into LOCAL_C_INCLUDES variable. By doing so, C++ files will be automatically compiled in NDK. If you need to compile a file other than the extension .cpp file, you will need to add it manually. There's more... If you want to manually build C++ in NDK, you can use the following command: $ ./build_native.py This script is located at the ~/Documenst/MyGame/proj.android . It uses ANDROID_SDK_ROOT and NDK_ROOT in it. If you want to see its options, run ./build_native.py –help. Summary Cocos2d-x is an open source, cross-platform game engine, which is free and mature. It can publish games for mobile devices and desktops, including iPhone, iPad, Android, Kindle, Windows, and Mac. The book Cocos2d-x Cookbook focuses on using version 3.4, which is the latest version of Cocos2d-x that was available at the time of writing. We focus on iOS and Android development, and we'll be using Mac because we need it to develop iOS applications. Resources for Article: Further resources on this subject: CREATING GAMES WITH COCOS2D-X IS EASY AND 100 PERCENT FREE [Article] Dragging a CCNode in Cocos2D-Swift [Article] COCOS2D-X: INSTALLATION [Article]
Read more
  • 0
  • 0
  • 4544

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
  • 4249
Visually different images

article-image-article-making-money-your-game
Packt
17 May 2013
23 min read
Save for later

Making Money with Your Game

Packt
17 May 2013
23 min read
(For more resources related to this topic, see here.) Your game development strategy If you want to build a game to make some money, it is imperative you take a few things into consideration before starting off building one. The first question you will need to ask yourself is probably this: who am I going to build a game for? Are you aiming at everyone capable of playing games or do you want to target a very specific segment of people and meet their gaming needs? This is the difference between broad and niche targeting. An example of very broadly targeted games are most tower defense games, in which you need to build towers with diverse properties to repel an army. Games such as Tetris, Bejeweled, Minesweeper, and most light puzzle games in general. Angry Birds is another example of a game that is popular with a broad audience because of its simplicity, likeable graphics, and incredible amount of clever marketing. Casual games in general seem to appeal to the masses because of the following few factors: Simplicity prevails: most gamers get used to the game in mere minutes. There are little or no knowledge prerequisites: you are not expected to already know some of the background story or have experience in these types of games. Casual gamers tend to do well even though they put in less time to practice. Even if you do well from the start, you can still become better at it. A game at which you cannot become better by replaying doesn't hold up for long. Notable exceptions are games of chance like roulette and the slots, which do prove to be addictive; but that is for other reasons, such as the chance to win money. The main advantage to building casual games is that practically everyone is a potential user of your game. As such, the achievable success can be enormous. World of Warcraft is a game which has moved from rather hardcore and niche to more casual over the years. They did this because they had already reached most regular gamers out there, and decided to convince the masses that you can play World of Warcraft even if you don't play a lot in general. The downside of trying to please everyone is the amount of competition. Sticking out as a unique game among numerous games out there is tremendously difficult. This is especially true if you don't have an impressive marketing machine to back it up. A good example of a niche game is any game built after a movie. Games on Star Trek, Star Wars, Lord of the Rings, and so on, are mostly aimed at people who have seen and liked those movies. Niche games can also be niche because they are targeted solely at a specific group of gamers. For example, people who prefer playing FPS (First Person Shooters) games, and do so every single day. In essence, niche games have the following properties (note that they oppose casual or broadly targeted games): Steep learning curve: mastery requires many hours of dedicated gaming. Some knowledge or experience with games is required. An online shooter game such as PlanetSide 2 requires you to have at least some experience with shooter games in the past, since you are pitted against people who know what they are doing. The more you play the game, the more are the useful rewards you get. Playing a lot is often rewarded with items that make you even stronger in the game, thus reinforcing the fact that you already became better by playing more. StarCraft is a game released by Blizzard in 1998 and is still being played in tournaments today, even though there is a follow up: StarCraft 2. Games such as the original StarCraft are perfectly feasible to be built in HTML5 and run on a browser or smartphone. When StarCraft was released, the average desktop computer had less power than many smartphones have today. Technically, the road is open; replicating the same level of success is another matter though. The advantage of aiming at a niche of gamers is the unique position you can take in their lives. Maybe there are not many people in your target group, but it will be easier to get and keep their attention with your game since it is specifically built for them. In addition, it doesn't mean that because you have a well-defined aim, gamers can't drop in from unexpected corners. People you would have never thought would play your game can still like what you did. It is for this reason that knowing your gamers is so important, and why tools such as Playtomic exist. The disadvantage of the niche marketing is obvious: your game is very unlikely to grow beyond a certain point; it will probably never rank among the most played games in the world. The type of games you are going to develop is one choice, the amount of detail you put in each one of them is another. You can put as much effort into building a single game as you want. In essence, a game is never finished. A game can always have an extra level, Easter egg, or another nice little detail. When scoping your game, you must have decided whether you will use a shotgun or a sniper development strategy. In a shotgun strategy, you develop and release games quickly. Each game still has a unique element that should distinguish it from other games: the UGP (Unique Gaming Proposition). But games released under a shotgun strategy don't deliver a lot of details; they are not polished. The advantages of adopting a shotgun strategy are plenty: Low development cost; thus every game represents a low risk The short time to market allows for using world events as game settings You have several games on the market at once, but often you only need a single one to succeed in order to cover the expenses incurred for the others Short games can be given to the public for free but monetized by selling add-ons, such as levels However, it's not just rainbows and sunshine when you adopt this strategy. There are several reasons why you wouldn't choose shotgun strategy: A game that doesn't feel finished has less chance of being successful than a perfected one. Throwing your game on the market not only tests whether a certain concept works, but also exposes it to the competitors, who can now start building a copy. Of course, you have the first mover advantage, but it's not as big as it could have been. You must always be careful not to throw garbage on the market either, or you might ruin your name as a developer. However, don't get confused. The shotgun strategy is not an excuse for building mediocre games. Every game you release should have an original touch to it— something that no other game has. If a game doesn't have that new flavor, why would anyone prefer it over all the others? Then, of course, there is the sniper strategy, which involves building a decent and well-thought-out game and releasing it to the market with the utmost care and support. This is what distributors such as Apple urge developers to do, and for good reason—you wouldn't want your app store full of crappy games, would you? Some other game distributers, such as Steam, are even pickier in the games they allow to be distributed, making the shotgun strategy nearly impossible. But it is also the strategy most successful game developers use. Take a look at developers such as Rockstar (developer of the GTA series), Besthesda (developer of the Elder Scroll series), Bioware (developer of the Mass Effect series), Blizzard (developer of the Warcraft series), and many others. These are no small fries, yet they don't have that many games on the market. This tactic of developing high quality games and hoping they will pay off is obviously not without risk. In order to develop a truly amazing game, you also need the time and money to do so. If your game fails to sell, this can be a real problem for you or your company. Even for HTML5 games, this can be the case, especially since devices and browsers keep getting more and more powerful. When the machines running the games get more powerful, the games themselves often become more complex and take longer to develop. We have taken a look at two important choices one needs to make when going into the game-developing business. Let's now look at the distribution channels that allow you to make money with your game, but before that let's summarize the topic we have just covered: Deciding who you want to target is extremely important even before you start developing your game. Broad targeting is barely targeting at all. It is about making a game accessible and likeable by as many people as possible. Niche targeting is taking a deeper look and interest in a certain group of people and building a game to suit their specific gaming needs. In developing and releasing games, there are two big strategies: shotgun and sniper. In the shotgun strategy you release games rapidly. Each game still has unique elements that no other games possess, but they are not as elaborate or polished as they could be. With the sniper strategy you build only a few games, but each one is already perfected at the time of release and only needs slight polishing when patches are released for it. Making money with game apps If you built your game into an app, you have several distribution channels you can turn to, such as Firefox marketplace, the IntelAppUp center, Windows Phone Store, Amazon Appstore, SlideMe, Mobango, Getjar, and Apple Appsfire. But the most popular players on the market currently are Google Play and the iOS App Store. The iOS App Store is not to be confused with the Mac app store. iPad and Mac have two different operating systems, iOS and Mac OS, so they have separate stores. Games can be released both in the iOS and the Mac store. There could also be some confusion between Google Play and Chrome Web Store. Google Play contains all the apps available for smartphones that have Google's Android operating system. The Chrome Web Store lets you add apps to your Google Chrome browser. So there are quite a few distribution channels to pick from and here we will have a quick look at Google Play, the iOS App store, and the Chrome Web Store. Google Play Google Play is the Android's default app shop and the biggest competitor of the iOS App Store. If you wish to become an Android app developer, there is a $25 fee and a developer distribution agreement that you must read. In return for the entrance fee and signing this agreement, they allow you to make use of their virtual shelves and have all its benefits. You can set your price as you please, but for every game you sell, Google will cash in about 30 percent. It is possible to do some geological price discrimination. So you could set your price at, let's say €1 in Belgium, while charging €2 in Germany. You can change your price at any time; however, if you release a game for free, there is no turning back. The only way to monetize this app afterwards is by allowing in game advertising, selling add-ons, or creating an in-game currency that can be bought with real money. Introducing an in-game currency that is bought with real money can be a very appealing format. A really successful example of this monetizing scheme can be found in The Smurfs games. In this game you build your own Smurf village, complete with Big Smurf, Smurfette, and a whole lot of mushrooms. Your city gets bigger as you plant more crops and build new houses, but it is a slow process. To speed it up a bit you can buy special berries in exchange for real money, which in turn allows you to build exclusive mushrooms and other things. This monetization scheme becomes very popular as has been shown in games such as League Of Legends, PlanetSide 2, World of Tanks, and many others. For Google Play apps, this in-app payment system is supported by Android's Google Checkout. In addition, Google allows you access to some basic statistics for your game, such as the number of players and the devices on which they play, as shown in the following diagram: Image Information such as this allows you to redesign your game to boost your success. For example, you could notice if a certain device doesn't have that many unique users, even though it is a very popular device and is bought by many people. If this is the case, maybe your game doesn't looks nice on this particular smartphone or tablet and you should optimize for it. The biggest competitor and initiator of all apps is the iOS App Store, so let's have a look at this. iOS App Store The iOS App Store was the first of its kind and at the time of writing this book, it still has the biggest revenue. In order to publish apps in the iOS App Store, you need to subscribe to the iOS Developer Program, which costs $99 annually—almost four times the subscription fee of Google Play. In effect, they offer about the same thing as Google Play does; as you can see in this short list: You pick your own prices and get 70 percent of sales revenue You receive monthly payments without credit card, hosting, or marketing fees There is support and adequate documentation to get you started More importantly, here are the following differences between Google Play and the iOS App Store: As mentioned earlier, signing up for Google Play is cheaper. The screening process of Apple seems to be more strict than that of Google Play, which results in a longer time to reach the market and a higher chance of never even reaching the market. Google Play incorporates a refund option that allows the buyer of your app to be refunded if he or she uninstalls the app or game within 24 hours. If you want your game to make use of some Android core functionalities, this is possible since the platform is open source. Apple, on the other hand, is very protective of its iOS platform and doesn't allow the same level of flexibility for apps. This element might not seem that important for games just yet, but it might be for very innovative games that do want to make use of this freedom. iOS reaches more people than Android does, though the current trend indicates that this might change in the near future. There seem to be significant differences in the kind of people buying Apple devices and the users of smartphones or tablets with Android OS. Apple fans tend to have a lower barrier towards spending money on their apps than Android users do. In general, iPads and iPhones are more expensive than other tablets and smartphones, attracting people who have no problem with spending even more money on the device. This difference in target group seems to make it more difficult for Android game developers to make money from their games. The last option for selling apps that we will discuss here is the Chrome Web Store. The Chrome Web Store The Chrome Web Store differs from Google Play and the iOS App Store in that it provides apps specifically for the Chrome browser and not for mobile devices. The Chrome Store offers web apps. Web apps are like applications you would install on your PC, except web apps are installed in your browser and are mostly written using HTML, CSS, and JavaScript, like our ImpactJS games. The first thing worth noticing about the Chrome Store is the one-time $5 entrance fee for posting apps. If this in itself is not good enough, the transaction fee for selling an app is only 5 percent. This is a remarkable difference to both Google Play and the iOS App Store. If you are already developing a game for your own website and packaged it as an app for Android and/or Apple, you can just as well launch it on the Chrome Web Store. Turning your ImpactJS game into a web app for the Chrome Store can be done using AppMobi, yet Google itself provides detailed documentation on how to do this manually. One of the biggest benefits of the web app is the facilitation of the permission process. Let's say your web app needs the location of the user in order to work. While iPad apps ask for permission every time they need location data, the web app asks permission only once: at installation time. Furthermore, you have the same functionalities and payment modalities as in Google Play, give or take. For instance, there is also the option to incorporate a free trial version, also known as a freemium. A freemium model is when you allow downloading a demo version for free with the option of upgrading it to a full version for a price. The Smurfs game also uses the freemium model, albeit with a difference. The entire game is free, but players can opt to pay real money to buy things that would otherwise cost them a lot of time to acquire. In this freemium model, you pay for convenience and unique items. For instance, in PlanetSide 2, acquiring a certain sniper rifle might take you several days or $10, depending on how you choose to play the freemium game. If you plan on releasing an ImpactJS game for Android, there is no real reason why you wouldn't do so for the Chrome Web Store. That being said, let's have a quick recap: The time when the iOS app store was the only app store out there is long gone; there is an impressive repertoire of app stores to choose from, which includes Firefox Marketplace, Intel AppUp Center, Windows Phone Store, Amazon Appstore, SlideMe, Mobango, Getjar, Appsfire, Google Play, among others. The biggest app stores are currently Google Play and the iOS App Store. They differ greatly on several fronts, of which the most important ones are: Subscription fee Screening process Type of audience they attract The Chrome Web Store sells web apps that act like normal apps but are available in the Chrome browser. The Chrome Web Store is cheap and easy to subscribe to. You must definitely have a go at releasing your game on this platform. In-game advertising In-game advertising is another way to make money with your game. In-game advertising is a growing market and is currently already being used by major companies; it was also used by Barack Obama in both his 2008 and 2012 campaigns, as shown in the following in-game screenshot: Image There is a trend towards more dynamic in-game advertising. The game manufacturers make sure there is space for advertising in the game, but the actual ads themselves are decided upon later. Depending on what is known about you, these can then change to become relevant to you as a player and a real-life consumer. When just starting out to build games, in-game adverting doesn't get that spectacular though. Most of the best known in-game advertisers for online games don't even want their ads in startup games. The requirements for Google AdSense are the following: Game plays: Minimum 500,000 per day Game types: Web-based Flash only Integration: Must be technically capable of SDK integration Traffic source: Eighty percent of traffic must be from the US and the UK Content: Family-safe and targeted at users aged 13 and over Distribution: Must be able to report embedded destinations and have control over where the games are distributed The requirements for another big competitor, Ad4Game, aren't mild either: At least 10,000 daily unique visitors Sub-domains and blogs are not accepted The Alexa rank should be less than 400,000 Adult/violent/racist content is not allowed If you are just starting out, these prerequisites are not good news. Not only because you need such a large number of players before even starting the advertising, but also because currently all support goes to Flash games. HTML5 games are not fully supported yet, though that will probably change. Luckily, there are companies out there that do allow you to start using advertising even though you don't have 10,000 visitors a day. Tictacti is one of those companies. Once again, almost all support goes to the Flash games, but they do have one option available for an HTML5 game:pre-roll. Pre-roll simply means that a screen with an ad appears before you can start the game. Integration of the pre-roll advertising is rather straightforward and does not require a change to your game, but to your index.html file, as in the following example from Tictacti: code While adding this to your game's index.html file, you fill out your own publisher ID and you are basically ready to go. Tictacti is similar to Google Analytics, and it also provides you with some relevant information about the ads on your game's website, as shown in the following diagram: Image Be careful, however, pre-roll advertising is one of the most intruding and annoying kinds of advertising. Technically, it is not even in-game advertising at all, since it runs before you play the game. If your game is not yet well established enough to convince the player to endure the advertising before being able to play, don't choose this option. Give your game some time to build a reputation before putting your gamers through this. As the last option, we will have a look at selling your actual distribution rights with MarketJS. But let's first briefly recap on in-game advertising: In-game advertising is a growing market. Even Barack Obama made use of in-game billboards to support his campaign. There is a trend towards more dynamic in-game advertising—using your location and demographic information to adapt the ads in a game. Currently, even the most accessible companies that offer online in-game advertising are focused on Flash games and require many unique visitors before even allowing you to show their ads. Tictacti is a notable exception, as it has low prerequisites and an easy implementation; though advertising is currently limited to pre-roll ads. Always take care to first build a positive reputation for your game and allow advertisements later. Selling distribution rights with MarketJS The last option we will investigate in this chapter is selling your game's distribution rights. You can still make money by just posting your game to all the app stores and on your own website, but it becomes increasingly difficult to be noticed. Quality can only prevail if people know it is out there, thus making a good game is sometimes not enough—you need marketing. If you are a beginner game builder with great game ideas and the skills to back it up, that's great, but marketing may not be your cup of tea. This is where MarketJS comes into play. MarketJS acts as an intermediate between you as the game developer and the game publishers. The procedure is simple once you have a game: You sign up on their website, http://www.marketjs.com. Upload the game on your own website or directly to the MarketJS server. Post your game for publishers to see. You set several options such as the price and contract type that would suit you the best. You have five contract options: Complete distribtution contract: Sell all your distribution rights to the game. Exclusive distribution partner contract: Here you restrict yourself to work with one distributor but still retain the rights to the game. Non-exclusive contract: Here, any distributor can buy the rights to use your game, but you can go on selling rights as long as you want. Revenue share: Here you negotiate on how to split the revenues derived from the game. Customized contract: This can basically have any terms. You can choose this option if you are not sure yet what you want out of your game. A part of the webpage on which you fill out your contracting preferences is shown in the following screenshot: Image After you have posted a demo, it is a matter of waiting for a publisher to spot it, get stunned by its magnificence, and offer to work with you. The big contribution of MarketJS to the gaming field is this ability to let the game developer focus on developing games. Someone else takes care of the marketing aspect, which is a totally different ballgame. MarketJS also offers a few interesting statistics such as the average price of a game on their website, as shown in the following diagram. It grants you some insight on whether you should take up game developing as a living or keep doing it as a hobby. Image According to MarketJS, prices for non-exclusive rights average between $500 and $1000, while selling exclusive rights to a game range somewhere between $1500 and $2000. If you can build a decent game within this price range, you are more than ready to go: MarketJS is a company that brings game distributers and developers closer together. Their focus is on HTML5 games, so they are great if you are a startup ImpactJS game developer. They require no subscription fee and have a straightforward process to turn your game into a showcase with a price tag. Summary In this article we have taken a look at some important elements while considering your game development strategy. Do you wish to adapt a shotgun approach and develop a lot of games in a short time span? Or will you use the sniper strategy and only build a few, but very polished games? You also need to decide upon the audience you wish to reach out to with your game. You have the option of building a game that is liked by everyone, but the competition is steep. Making money on the application stores is possible, but for Android and Apple there are registration fees. If you decide to develop apps, it is worth giving the Chrome Web Store a try (it runs web apps). In-game advertising is another way to fund your efforts, though most companies offering this service for online games have high prerequisites and support Flash games more than they do the newer HTML5 games. One of the most promising of monetization schemes is the freemium model. Players are allowed to freely play your game but they pay real money for extras. This is an easily tolerated model since the game is essentially free to play for anyone not willing to spend money, and no annoying advertising is present either. A combination of in-game advertising and freemium is possible as well: people annoyed by the advertising pay a fee and in return they are not bothered by it anymore. A final option is leaving the marketing aspect to someone else by selling your distribution rights with the help of MarketJS. They aim for HTML5 games and this option is especially useful for the beginner game developer who has difficulty in marketing his or her game. Resources for Article : Further resources on this subject: HTML5: Developing Rich Media Applications using Canvas [Article] Flash 10 Multiplayer Game: The Lobby and New Game Screen Implementation [Article] Building HTML5 Pages from Scratch [Article]
Read more
  • 0
  • 0
  • 4079

article-image-unity-2018-2-unity-release-for-this-year-2nd-time-in-a-row
Sugandha Lahoti
12 Jul 2018
4 min read
Save for later

Unity 2018.2: Unity release for this year 2nd time in a row!

Sugandha Lahoti
12 Jul 2018
4 min read
It has only been two months since the release of Unity 2018.1 and Unity is back again with their next release for this year. Unity 2018.2 builds on the features of Unity 2018.1 such as Scriptable Render Pipeline (SRP), Shader Graph, and Entity component system. It also adds support for managed code debugging on iOS and Android, along with the final release of 64-bit (ARM64) support for Android devices. Let us look at the features in detail. Scriptable Render Pipeline improvements As mentioned above, Unity 2018.2 builds on Scriptable Render Pipeline introduced in 2018.1. The version 2 comes with two additional features: The SRP batcher: It is a new Unity engine inner loop for speeding up CPU rendering without compromising GPU performance. It works with the High Definition Render Pipeline (HDRP) and Lightweight Render Pipeline (LWRP), with PC DirectX-11, Metal and PlayStation 4 currently supported. A Scriptable shader variants stripping: It can manage the number of shader variants generated, without affecting iteration time or maintenance complexity. This leads to a dramatic reduction in player build time and data size. Performance optimizations in Lightweight Render Pipeline and High Definition Render Pipeline Unity 2018.2 improves performance and optimization of Lightweight Render Pipeline (LWRP) with an Optimized Tile utilization. This feature adjusts the number of load-and-store to tiles in order to optimize the memory of mobile GPUs. It also shades light in batches, which reduces overdraw and draw calls. Unity 2018.2 comes with better high-end visual quality in High Definition Render Pipeline (HDRP). Improvements include volumetrics, glossy planar reflection, Geometric specular AA, and Proxy Screen Space Reflection & Refraction, Mesh decals, and Shadow Mask. Improvements in C# Job System, Entity Component System and Burst Compiler Unity 2018.2 introduces new Reactive system samples in the Entity Component system (ECS) to let developers respond when there are changes to component state and emulate event-driven behavior. Burst compiling for ECS is now available on all editor platforms (Windows, Mac, Linux), and game developers will be able to build AOT for standalone players (Desktop, PS4, Xbox, iOS and Android). The C# Job system, allows developers to take full advantage of the multicore processors currently available and write parallel code without worrying about programming. Updates to Shader Graph Shader Graph, announced as a preview package in Unity 2018.2 will allow developers to build shaders visually. It has further added additional improvements like: High Definition Render Pipeline (HDRP) support, Manual modification of vertex position, Editing of the Reference name for a property, Editable paths for graphs, Texture 2D and 3D array, and more. Texture Mipmap Streaming Game developers can now stream texture mipmaps into memory on demand to reduce the texture memory requirements of a Unity application. This feature speeds up initial load time, gives developers more control, and is simple to enable and manage. Particle System improvements Unity 2018.2 has 7 major improvements to Particle system which are: Support for eight UVs, to use more custom data. MinMaxCurve and MinMaxGradient types in custom scripts to match the style used by the Particle System UI. Particle Systems now converts colors into linear space, when appropriate, before uploading them to the GPU. Two new modes to the Shape module to emit from a sprite or SpriteRenderer component. Two new APIs for baking the geometry of a Particle System into a mesh. Show Only Selected (aka Solo Mode) with the Play/Restart/Stop, etc; controls. Shaders that use separate alpha textures can now be used with particles, while using sprites in the Texture Sheet Animation module. Unity Hub Unity Hub (v1.0) is a new tool, to be released soon, designed to streamline onboarding and setup processes for all users. It is a centralized location to manage all Unity Projects, simpliflying how developers find, download, and manage Unity Editor licenses and add-on components. The Hub 1.0 will be shipped with: Project templates Custom install location Added Asset Store packages to new projects Modified project build target Editor: Added components post-installation There are additional features like Vulkan support for Editor on Windows and Linux and improvements to Progressive Lightmapper, 2D games, SVG importer, etc. It will also support .java and .cpp source files as plugins in a Unity project along with updates to Cinematics and Unity core engine. In total, there are 183 improvements and 1426 fixes in Unity 2018.2 release. Refer to the release notes to view the full list of new features, improvements and fixes. Put your game face on! Unity 2018.1 is now available Unity plugins for augmented reality application development Unity 2D & 3D game kits simplify Unity game development for beginner
Read more
  • 0
  • 0
  • 3790

article-image-game-world
Packt
23 Feb 2016
39 min read
Save for later

The Game World

Packt
23 Feb 2016
39 min read
In this article, we will cover the basics of creating immersive areas where players can walk around and interact, as well as some of the techniques used to manage those areas. This article will give you some practical tips and tricks of the spritesheet system introduced with Unity 4.3 and how to get it to work for you. Lastly, we will also have a cursory look at how shaders work in the 2D world and the considerations you need to keep in mind when using them. However, we won't be implementing shaders as that could be another book in itself. The following is the list of topics that will be covered in this article: Working with environments Looking at sprite layers Handling multiple resolutions An overview of parallaxing and effects Shaders in 2D – an overview (For more resources related to this topic, see here.) Backgrounds and layers Now that we have our hero in play, it would be nice to give him a place to live and walk around, so let's set up the home town and decorate it. Firstly, we are going to need some more assets. So, from the asset pack you downloaded earlier, grab the following assets from the Environments pack, place them in the AssetsSpritesEnvironment folder, and name them as follows: Name the ENVIRONMENTS STEAMPUNKbackground01.png file Assets SpritesEnvironmentbackground01 Name the ENVIRONMENTSSTEAMPUNKenvironmentalAssets.png file AssetsSpritesEnvironmentenvironmentalAssets Name the ENVIRONMENTSFANTASYenvironmentalAssets.png file Assets SpritesEnvironmentenvironmentalAssets2 To slice or not to slice It is always better to pack many of the same images on to a single asset/atlas and then use the Sprite Editor to define the regions on that texture for each sprite, as long as all the sprites on that sheet are going to get used in the same scene. The reason for this is when Unity tries to draw to the screen, it needs to send the images to draw to the graphics card; if there are many images to send, this can take some time. If, however, it is just one image, it is a lot simpler and more performant with only one file to send. There needs to be a balance; too large an image and the upload to the graphics card can take up too many resources, too many individual images and you have the same problem. The basic rule of thumb is as follows: If the background is a full screen background or large image, then keep it separately. If you have many images and all are for the same scene, then put them into a spritesheet/atlas. If you have many images but all are for different scenes, then group them as best you can—common items on one sheet and scene-specific items on different sheets. You'll have several spritesheets to use. You basically want to keep as much stuff together as makes sense and not send unnecessary images that won't get used to the graphics card. Find your balance. The town background First, let's add a background for the town using the AssetsSpritesEnvironmentbackground01 texture. It is shown in the following screenshot: With the background asset, we don't need to do anything else other than ensure that it has been imported as a sprite (in case your project is still in 3D mode), as shown in the following screenshot: The town buildings For the steampunk environmental assets (AssetsSpritesEnvironmentenvironmentalAssets) that are shown in the following screenshot, we need a bit more work; once these assets are imported, change the Sprite Mode to Multiple and load up the Sprite Editor using the Sprite Editor button. Next, click on the Slice button, leave the settings at their default options, and then click on the Slice button in the new window as shown in the following screenshot: Click on Apply and close the Sprite Editor. You will have four new sprite textures available as seen in the following screenshot: The extra scenery We saw what happens when you use a grid type split on a spritesheet and when the automatic split works well, so what about when it doesn't go so well? If we look at the Fantasy environment pack (AssetsSpritesEnvironmentenvironmentalAssets2), we will see the following: After you have imported it and run the Split in Sprite Editor, you will notice that one of the sprites does not get detected very well; altering the automatic split settings in this case doesn't help, so we need to do some manual manipulation as shown in the following screenshot: In the previous screenshot, you can see that just two of the rocks in the top-right sprite have been identified by the splicing routine. To fix this, just delete one of the selections and then expand the other manually using the selection points in the corner of the selection box (after clicking on the sprite box). Here's how it will look before the correction: After correction, you should see something like the following screenshot: This gives us some nice additional assets to scatter around our towns and give it a more homely feel, as shown in the following screenshot: Building the scene So, now that we have some nice assets to build with, we can start building our first town. Adding the town background Returning to the scene view, you should see the following: If, however, we add our town background texture (AssetsSpritesBackgroundsBackground.png) to the scene by dragging it to either the project hierarchy or the scene view, you will end up with the following: Be sure to set the background texture position appropriately once you add it to the scene; in this case, be sure the position of the transform is centered in the view at X = 0, Y = 0, Z = 0. Unity does have a tendency to set the position relative to where your 3D view is at the time of adding it—almost never where you want it. Our player has vanished! The reason for this is simple: Unity's sprite system has an ordering system that comes in two parts. Sprite sorting layers Sorting Layers (Edit | Project Settings | Tags and Layers) are a collection of sprites, which are bulked together to form a single group. Layers can be configured to be drawn in a specific order on the screen as shown in the following screenshot: Sprite sorting order Sprites within an individual layer can be sorted, allowing you to control the draw order of sprites within that layer. The sprite Inspector is used for this purpose, as shown in the following screenshot: Sprite's Sorting Layers should not be confused with Unity's rendering layers. Layers are a separate functionality used to control whether groups of game objects are drawn or managed together, whereas Sorting Layers control the draw order of sprites in a scene. So the reason our player is no longer seen is that it is behind the background. As they are both in the same layer and have the same sort order, they are simply drawn in the order that they are in the project hierarchy. Updating the scene Sorting Layers To resolve the update of the scene's Sorting Layers, let's organize our sprite rendering by adding some sprite Sorting Layers. So, open up the Tags and Layers inspector pane as shown in the following screenshot (by navigating to Edit | Project settings | Tags and Layers), and add the following Sorting Layers: Background Player Foreground GUI You can reorder the layers underneath the default anytime by selecting a row and dragging it up and down the sprite's Sorting Layers list. With the layers set up, we can now configure our game objects accordingly. So, set the Sorting Layer on our background01 sprite to the Background layer as shown in the following screenshot: Then, update the PlayerSprite layer to Player; our character will now be displayed in front of the background. You can just keep both objects on the same layer and set the Sort Order value appropriately, keeping the background to a Sort Order value of 0 and the player to 10, which will draw the player in front. However, as you add more items to the scene, things will get tricky quickly, so it is better to group them in a layer accordingly. Now when we return to the scene, our hero is happily displayed but he is seen hovering in the middle of our village. So let's fix that next by simply changing its position transform in the Inspector window. Setting the Y position transform to -2 will place our hero nicely in the middle of the street (provided you have set the pivot for the player sprite to bottom), as shown in the following screenshot: Feel free at this point to also add some more background elements such as trees and buildings to fill out the scene using the environment assets we imported earlier. Working with the camera If you try and move the player left and right at the moment, our hero happily bobs along. However, you will quickly notice that we run into a problem: the hero soon disappears from the edge of the screen. To solve this, we need to make the camera follow the hero. When creating new scripts to implement something, remember that just about every game that has been made with Unity has most likely implemented either the same thing or something similar. Most just get on with it, but others and the Unity team themselves are keen to share their scripts to solve these challenges. So in most cases, we will have something to work from. Don't just start a script from scratch (unless it is a very small one to solve a tiny issue) if you can help it; here's some resources to get you started: Unity sample projects: http://Unity3d.com/learn/tutorials/projects Unity Patterns: http://unitypatterns.com/ Unity wiki scripts section: http://wiki.Unity3d.com/index.php/Scripts (also check other stuff for detail) Once you become more experienced, it is better to just use these scripts as a reference and try to create your own and improve on them, unless they are from a maintained library such as https://github.com/nickgravelyn/UnityToolbag. To make the camera follow the players, we'll take the script from the Unity 2D sample and modify it to fit in our game. This script is nice because it also includes a Mario style buffer zone, which allows the players to move without moving the camera until they reach the edge of the screen. Create a new script called FollowCamera in the AssetsScripts folder, remove the Start and Update functions, and then add the following properties: using UnityEngine;   public class FollowCamera : MonoBehavior {     // Distance in the x axis the player can move before the   // camera follows.   public float xMargin = 1.5f;     // Distance in the y axis the player can move before the   // camera follows.   public float yMargin = 1.5f;     // How smoothly the camera catches up with its target   // movement in the x axis.   public float xSmooth = 1.5f;     // How smoothly the camera catches up with its target   // movement in the y axis.   public float ySmooth = 1.5f;     // The maximum x and y coordinates the camera can have.   public Vector2 maxXAndY;     // The minimum x and y coordinates the camera can have.   public Vector2 minXAndY;     // Reference to  the player's transform.   public Transform player; } The variables are all commented to explain their purpose, but we'll cover each as we use them. First off, we need to get the player object's position so that we can track the camera to it by discovering it from the object it is attached to. This is done by adding the following code in the Awake function: void Awake()     {         // Setting up the reference.         player = GameObject.Find("Player").transform;   if (player == null)   {     Debug.LogError("Player object not found");   }       } An alternative to discovering the player this way is to make the player property public and then assign it in the editor. There is no right or wrong way—just your preference. It is also a good practice to add some element of debugging to let you know if there is a problem in the scene with a missing reference, else all you will see are errors such as object not initialized or variable was null. Next, we need a couple of helper methods to check whether the player has moved near the edge of the camera's bounds as defined by the Max X and Y variables. In the following code, we will use the settings defined in the preceding code to control how close you can get to the end result:   bool CheckXMargin()     {         // Returns true if the distance between the camera and the   // player in the x axis is greater than the x margin.         return Mathf.Abs (transform.position.x - player.position.x) > xMargin;     }       bool CheckYMargin()     {         // Returns true if the distance between the camera and the   // player in the y axis is greater than the y margin.         return Mathf.Abs (transform.position.y - player.position.y) > yMargin;     } To finish this script, we need to check each frame when the scene is drawn to see whether the player is close to the edge and update the camera's position accordingly. Also, we need to check if the camera bounds have reached the edge of the screen and not move it beyond. Comparing Update, FixedUpdate, and LateUpdate There is usually a lot of debate about which update method should be used within a Unity game. To put it simply, the FixedUpdate method is called on a regular basis throughout the lifetime of the game and is generally used for physics and time sensitive code. The Update method, however, is only called after the end of each frame that is drawn to the screen, as the time taken to draw the screen can vary (due to the number of objects to be drawn and so on). So, the Update call ends up being fairly irregular. For more detail on the difference between Update and FixedUpdate see the Unity Learn tutorial video at http://unity3d.com/learn/tutorials/modules/beginner/scripting/update-and-fixedupdate. As the player is being moved by the physics system, it is better to update the camera in the FixedUpdate method: void FixedUpdate()     {         // By default the target x and y coordinates of the camera         // are it's current x and y coordinates.         float targetX = transform.position.x;         float targetY = transform.position.y;           // If the player has moved beyond the x margin...         if (CheckXMargin())             // the target x coordinate should be a Lerp between             // the camera's current x position and the player's  // current x position.             targetX = Mathf.Lerp(transform.position.x,  player.position.x, xSmooth * Time.fixedDeltaTime );           // If the player has moved beyond the y margin...         if (CheckYMargin())             // the target y coordinate should be a Lerp between             // the camera's current y position and the player's             // current y position.             targetY = Mathf.Lerp(transform.position.y,  player.position.y, ySmooth * Time. fixedDeltaTime );           // The target x and y coordinates should not be larger         // than the maximum or smaller than the minimum.         targetX = Mathf.Clamp(targetX, minXAndY.x, maxXAndY.x);         targetY = Mathf.Clamp(targetY, minXAndY.y, maxXAndY.y);           // Set the camera's position to the target position with         // the same z component.         transform.position =          new Vector3(targetX, targetY, transform.position.z);     } As they say, every game is different and how the camera acts can be different for every game. In a lot of cases, the camera should be updated in the LateUpdate method after all drawing, updating, and physics are complete. This, however, can be a double-edged sword if you rely on math calculations that are affected in the FixedUpdate method, such as Lerp. It all comes down to tweaking your camera system to work the way you need it to do. Once the script is saved, just attach it to the Main Camera element by dragging the script to it or by adding a script component to the camera and selecting the script. Finally, we just need to configure the script and the camera to fit our game size as follows: Set the orthographic Size of the camera to 2.7 and the Min X and Max X sizes to 5 and -5 respectively. The perils of resolution When dealing with cameras, there is always one thing that will trip us up as soon as we try to build for another platform—resolution. By default, the Unity player in the editor runs in the Free Aspect mode as shown in the following screenshot: The Aspect mode (from the Aspect drop-down) can be changed to represent the resolutions supported by each platform you can target. The following is what you get when you switch your build target to each platform: To change the build target, go into your project's Build Settings by navigating to File | Build Settings or by pressing Ctrl + Shift + B, then select a platform and click on the Switch Platform button. This is shown in the following screenshot: When you change the Aspect drop-down to view in one of these resolutions, you will notice how the aspect ratio for what is drawn to the screen changes by either stretching or compressing the visible area. If you run the editor player in full screen by clicking on the Maximize on Play button () and then clicking on the play icon, you will see this change more clearly. Alternatively, you can run your project on a target device to see the proper perspective output. The reason I bring this up here is that if you used fixed bounds settings for your camera or game objects, then these values may not work for every resolution, thereby putting your settings out of range or (in most cases) too undersized. You can handle this by altering the settings for each build or using compiler predirectives such as #if UNITY_METRO to force the default depending on the build (in this example, Windows 8). To read more about compiler predirectives, check the Unity documentation at http://docs.unity3d.com/Manual/PlatformDependentCompilation.html. A better FollowCamera script If you are only targeting one device/resolution or your background scrolls indefinitely, then the preceding manual approach works fine. However, if you want it to be a little more dynamic, then we need to know what resolution we are working in and how much space our character has to travel. We will perform the following steps to do this: We will change the min and max variables to private as we no longer need to configure them in the Inspector window. The code is as follows:   // The maximum x and y coordinates the camera can have.     private Vector2 maxXAndY;       // The minimum x and y coordinates the camera can have.     private Vector2 minXAndY; To work out how much space is available in our town, we need to interrogate the rendering size of our background sprite. So, in the Awake function, we add the following lines of code: // Get the bounds for the background texture - world       size     var backgroundBounds = GameObject.Find("background")      .renderer.bounds; In the Awake function, we work out our resolution and viewable space by interrogating the ViewPort method on the camera and converting it to the same coordinate type as the sprite. This is done using the following code:   // Get the viewable bounds of the camera in world     // coordinates     var camTopLeft = camera.ViewportToWorldPoint      (new Vector3(0, 0, 0));     var camBottomRight = camera.ViewportToWorldPoint      (new Vector3(1, 1, 0)); Finally, in the Awake function, we update the min and max values using the texture size and camera real-world bounds. This is done using the following lines of code: // Automatically set the min and max values     minXAndY.x = backgroundBounds.min.x - camTopLeft.x;     maxXAndY.x = backgroundBounds.max.x - camBottomRight.x; In the end, it is up to your specific implementation for the type of game you are making to decide which pattern works for your game. Transitioning and bounds So our camera follows our player, but our hero can still walk off the screen and keep going forever, so let us stop that from happening. Towns with borders As you saw in the preceding section, you can use Unity's camera logic to figure out where things are on the screen. You can also do more complex ray testing to check where things are, but I find these are overly complex unless you depend on that level of interaction. The simpler answer is just to use the native Box2D physics system to keep things in the scene. This might seem like overkill, but the 2D physics system is very fast and fluid, and it is simple to use. Once we add the physics components, Rigidbody 2D (to apply physics) and a Box Collider 2D (to detect collisions) to the player, we can make use of these components straight away by adding some additional collision objects to stop the player running off. To do this and to keep things organized, we will add three empty game objects (either by navigating to GameObject | Create Empty, or by pressing Ctrl + Shift +N) to the scene (one parent and two children) to manage these collision points, as shown in the following screenshot: I've named them WorldBounds (parent) and LeftBorder and RightBorder (children) for reference. Next, we will position each of the child game objects to the left- and right-hand side of the screen, as shown in the following screenshot: Next, we will add a Box Collider 2D to each border game object and increase its height just to ensure that it works for the entire height of the scene. I've set the Y value to 5 for effect, as shown in the following screenshot: The end result should look like the following screenshot with the two new colliders highlighted in green: Alternatively, you could have just created one of the children, added the box collider, duplicated it (by navigating to Edit | Duplicate or by pressing Ctrl + D), and moved it. If you have to create multiples of the same thing, this is a handy tip to remember. If you run the project now, then our hero can no longer escape this town on his own. However, as we want to let him leave, we can add a script to the new Boundary game object so that when the hero reaches the end of the town, he can leave. Journeying onwards Now that we have collision zones on our town's borders, we can hook into this by using a script to activate when the hero approaches. Create a new C# script called NavigationPrompt, clear its contents, and populate it with the following code: using UnityEngine;   public class NavigationPrompt : MonoBehavior {     bool showDialog;     void OnCollisionEnter2D(Collision2D col)   {     showDialog = true;   }     void OnCollisionExit2D(Collision2D col)   {     showDialog = false;   } } The preceding code gives us the framework of a collision detection script that sets a flag on and off if the character interacts with what the script is attached to, provided it has a physics collision component. Without it, this script would do nothing and it won't cause an error. Next, we will do something with the flag and display some GUI when the flag is set. So, add the following extra function to the preceding script: void OnGUI()     {       if (showDialog)       {         //layout start         GUI.BeginGroup(new Rect(Screen.width / 2 - 150, 50, 300,           250));           //the menu background box         GUI.Box(new Rect(0, 0, 300, 250), "");           // Information text         GUI.Label(new Rect(15, 10, 300, 68), "Do you want to           travel?");           //Player wants to leave this location         if (GUI.Button(new Rect(55, 100, 180, 40), "Travel"))         {           showDialog = false;             // The following line is commented out for now           // as we have nowhere to go :D           //Application.LoadLevel(1);}           //Player wants to stay at this location         if (GUI.Button(new Rect(55, 150, 180, 40), "Stay"))         {           showDialog = false;         }           //layout end         GUI.EndGroup();       }     } The function itself is very simple and only activates if the showDialog flag is set to true by the collision detection. Then, we will perform the following steps: In the OnGUI method, we set up a dialog window region with some text and two buttons. One button asks if the player wants to travel, which would load the next area (commented out for now as we only have one scene), and close the dialog. One button simply closes the dialog if the hero didn't actually want to leave. As we haven't stopped moving the player, the player can also do this by moving away. If you now add the NavigationPrompt script to the two world border (LeftBorder and RightBorder) game objects, this will result in the following simple UI whenever the player collides with the edges of our world: We can further enhance this by tagging or naming our borders to indicate a destination. I prefer tagging, as it does not interfere with how my scene looks in the project hierarchy; also, I can control what tags are available and prevent accidental mistyping. To tag a game object, simply select a Tag using the drop-down list in the Inspector when you select the game object in the scene or project. This is shown in the following screenshot: If you haven't set up your tags yet or just wish to add a new one, select Add Tag in the drop-down menu; this will open up the Tags and Layers window of Inspector. Alternatively, you can call up this window by navigating to Edit | Project Settings | Tags and layers in the menu. It is shown in the following screenshot: You can only edit or change user-defined tags. There are several other tags that are system defined. You can use these as well; you just cannot change, remove, or edit them. These include Player, Respawn, Finish, Editor Only, Main Camera, and GameController. As you can see from the preceding screenshot, I have entered two new tags called The Cave and The World, which are the two main exit points from our town. Unity also adds an extra item to the arrays in the editor. This helps you when you want to add more items; it's annoying when you want a fixed size but it is meant to help. When the project runs, however, the correct count of items will be exposed. Once these are set up, just return to the Inspector for the two borders, and set the right one to The World and the left to The Cave. Now, I was quite specific in how I named these tags, as you can now reuse these tags in the script to both aid navigation and also to notify the player where they are going. To do this, simply update the Do you want to travel to line to the following: //Information text GUI.Label(new Rect(15, 10, 300, 68), "Do you want to travel to " +   this.tag + "?"); Here, we have simply appended the dialog as it is presented to the user with the name of the destination we set in the tag. Now, we'll get a more personal message, as shown in the following screenshot: Planning for the larger picture Now for small games, the preceding implementation is fine; however, if you are planning a larger world with a large number of interactions, provide complex decisions to prevent the player continuing unless they are ready. As the following diagram shows, there are several paths the player can take and in some cases, these is only one way. Now, we could just build up the logic for each of these individually as shown in the screenshot, but it is better if we build a separate navigation system so that we have everything in one place; it's just easier to manage that way. This separation is a fundamental part of any good game design. Keeping the logic and game functionality separate makes it easier to maintain in the future, especially when you need to take internationalization into account (but we will learn more about that later). Now, we'll change to using a manager to handle all the world/scene transitions, and simplify the tag names we use as they won't need to be displayed. So, The Cave will be renamed as just Cave, and we will get the text to display from the navigation manager instead of the tag. So, by separating out the core decision making functionality out of the prompt script, we can build the core manager for navigation. Its primary job is to maintain where a character can travel and information about that destination. First, we'll update the tags we created earlier to simpler identities that we can use in our navigation manager (update The Cave to Cave01 and The World to World). Next, we'll create a new C# script called NavigationManager in our AssetsScripts folder, and then replace its contents with the following lines of code: public static class NavigationManager {       public static Dictionary<string,string> RouteInformation =     new Dictionary<string,string>()   {     { "World", "The big bad world"},     { "Cave", "The deep dark cave"},   };     public static string GetRouteInfo(string destination)   {     return RouteInformation.ContainsKey(destination) ?     RouteInformation[destination] : null;   }     public static bool CanNavigate(string destination)   {     return true;   }     public static void NavigateTo(string destination)   {     // The following line is commented out for now     // as we have nowhere to go :D     //Application.LoadLevel(destination);   } } Notice the ? and : operators in the following statement: RouteInformation.ContainsKey(destination) ?   RouteInformation[destination] : null; These operators are C# conditional operators. They are effectively the shorthand of the following: if(RouteInformation.ContainsKey(destination)) {   return RouteInformation[destination]; } else {   return null; } Shorter, neater, and much nicer, don't you think? For more information, see the MSDN C# page at http://bit.ly/csharpconditionaloperator. The script is very basic for now, but contains several following key elements that can be expanded to meet the design goals of your game: RouteInformation: This is a list of all the possible destinations in the game in a dictionary. A static list of possible destinations in the game, and it is a core part of the manager as it knows everywhere you can travel in the game in one place. GetRouteInfo: This is a basic information extraction function. A simple controlled function to interrogate the destination list. In this example, we just return the text to be displayed in the prompt, which allows for more detailed descriptions that we could use in tags. You could use this to provide alternate prompts depending on what the player is carrying and whether they have a lit torch, for example. CanNavigate: This is a test to see if navigation is possible. If you are going to limit a player's travel, you need a way to test if they can move, allowing logic in your game to make alternate choices if the player cannot. You could use a different system for this by placing some sort of block in front of a destination to limit choice (as used in the likes of Zelda), such as an NPC or rock. As this is only an example, we can always travel and add logic to control it if you wish. NavigateTo: This is a function to instigate navigation. Once a player can travel, you can control exactly what happens in the game: does navigation cause the next scene to load straight away (as in the script currently), or does the current scene fade out and then a traveling screen is shown before fading the next level in? Granted, this does nothing at present as we have nowhere to travel to. The script you will notice is different to the other scripts used so far, as it is a static class. This means it sits in the background, only exists once in the game, and is accessible from anywhere. This pattern is useful for fixed information that isn't attached to anything; it just sits in the background waiting to be queried. Later, we will cover more advanced types and classes to provide more complicated scenarios. With this class in place, we just need to update our previous script (and the tags) to make use of this new manager. Update the NavigationPrompt script as follows: Update the collision function to only show the prompt if we can travel. The code is as follows: void OnCollisionEnter2D(Collision2D col) {   //Only allow the player to travel if allowed   if (NavigationManager.CanNavigate(this.tag))   {     showDialog = true;   } } When the dialog shows, display the more detailed destination text provided by the manager for the intended destination. The code is as follows: //Dialog detail - updated to get better detail GUI.Label(new Rect(15, 10, 300, 68), "Do you want to travel   to " + NavigationManager.GetRouteInfo(this.tag) + "?"); If the player wants to travel, let the manager start the travel process. The code is as follows: //Player wants to leave this location if (GUI.Button(new Rect(55, 100, 180, 40), "Travel")) {   showDialog = false;   NavigationManager.NavigateTo(this.tag); } The functionality I've shown here is very basic and it is intended to make you think about how you would need to implement it for your game. With so many possibilities available, I could fill several articles on this kind of subject alone. Backgrounds and active elements A slightly more advanced option when building game worlds is to add a level of immersive depth to the scene. Having a static image to show the village looks good, especially when you start adding houses and NPCs to the mix; but to really make it shine, you should layer the background and add additional active elements to liven it up. We won't add them to the sample project at this time, but it is worth experimenting with in your own projects (or try adding it to this one)—it is a worthwhile effect to look into. Parallaxing If we look at the 2D sample provided by Unity, the background is split into several panes—each layered on top of one another and each moving at a different speed when the player moves around. There are also other elements such as clouds, birds, buses, and taxes driving/flying around, as shown in the following screenshot: Implementing these effects is very easy technically. You just need to have the art assets available. There are several scripts in the wiki I described earlier, but the one in Unity's own 2D sample is the best I've seen. To see the script, just download the Unity Projects: 2D Platformer asset from https://www.assetstore.unity3d.com/en/#!/content/11228, and check out the BackgroundParallax script in the AssetsScripts folder. The BackgroundParallax script in the platformer sample implements the following: An array of background images, which is layered correctly in the scene (which is why the script does not just discover the background sprites) A scaling factor to control how much the background moves in relation to the camera target, for example, the camera A reducing factor to offset how much each layer moves so that they all don't move as one (or else what is the point, might as well be a single image) A smoothing factor so that each background moves smoothly with the target and doesn't jump around Implementing this same model in your game would be fairly simple provided you have texture assets that could support it. Just replicate the structure used in the platformer 2D sample and add the script. Remember to update the FollowCamera script to be able to update the base background, however, to ensure that it can still discover the size of the main area. Foreground objects The other thing you can do to liven up your game is to add random foreground objects that float across your scene independently. These don't collide with anything and aren't anything to do with the game itself. They are just eye candy to make your game look awesome. The process to add these is also fairly simple, but it requires some more advanced Unity features such as coroutines, which we are not going to cover here. So, we will come back to these later. In short, if you examine the BackgroundPropSpawner.cs script from the preceding Unity platformer 2D sample, you will have to perform the following steps: Create/instantiate an object to spawn. Set a random position and direction for the object to travel. Update the object over its lifetime. Once it's out of the scene, destroy or hide it. Wait for a time, and then start again. This allows them to run on their own without impacting the gameplay itself and just adds that extra bit of depth. In some cases, I've seen particle effects are also used to add effect, but they are used sparingly. Shaders and 2D Believe it or not, all 2D elements (even in their default state) are drawn using a shader—albeit a specially written shader designed to light and draw the sprite in a very specific way. If you look at the player sprite in the inspector, you will see that it uses a special Material called Sprites-Default, as shown in the following screenshot: This section is purely meant to highlight all the shading options you have in the 2D system. Shaders have not changed much in this update except for the addition of some 2D global lighting found in the default sprite shader. For more detail on shaders in general, I suggest a dedicated Unity shader book such as https://www.packtpub.com/game-development/unity-shaders-and-effects-cookbook. Clicking on the button next to Material field will bring up the material selector, which also shows the two other built-in default materials, as shown in the following screenshot: However, selecting either of these will render your sprite invisible as they require a texture and lighting to work; they won't inherit from the Sprite Renderer texture. You can override this by creating your own material and assigning alternate sprite style shaders. To create a new material, just select the AssetsMaterials folder (this is not crucial, but it means we create the material in a sensible place in our project folder structure) and then right click on and select Create | Material. Alternatively, do the same using the project view's Edit... menu option, as shown in the following screenshot: This gives us a basic default Diffuse shader, which is fine for basic 3D objects. However, we also have two default sprite rendering shaders available. Selecting the shader dropdown gives us the screen shown in the following screenshot: Now, these shaders have the following two very specific purposes: Default: This shader inherits its texture from the Sprite Renderer texture to draw the sprite as is. This is a very basic functionality—just enough to draw the sprite. (It contains its own static lighting.) Diffuse: This shader is the same as the Default shader; it inherits the texture of Default, but it requires an external light source as it does not contain any lighting—this has to be applied separately. It is a slightly more advanced shader, which includes offsets and other functions. Creating one of these materials and applying it to the Sprite Renderer texture of a sprite will override its default constrained behavior. This opens up some additional shader options in the Inspector, as shown in the following screenshot: These options include the following: Sprite Texture: Although changing the Tiling and Offset values causes a warning to appear, they still display a function (even though the actual displayed value resets). Tint: This option allows changing the default light tint of the rendered sprite. It is useful to create different colored objects from the same sprite. Pixel snap: This option makes the rendered sprite crisper but narrows the drawn area. It is a trial and error feature (see the following sections for more information). Achieving pixel perfection in your game in Unity can be a challenge due to the number of factors that can affect it, such as the camera view size, whether the image texture is a Power Of Two (POT) size, and the import setting for the image. This is basically a trial and error game until you are happy with the intended result. If you are feeling adventurous, you can extend these default shaders (although this is out of the scope of this article). The full code for these shaders can be found at http://Unity3d.com/unity/download/archive. If you are writing your own shaders though, be sure to add some lighting to the scene; otherwise, they are just going to appear dark and unlit. Only the default sprite shader is automatically lit by Unity. Alternatively, you can use the default sprite shader as a base to create your new custom shader and retain the 2D basic lighting. Another worthy tip is to check out the latest version of the Unity samples (beta) pack. In it, they have added logic to have two sets of shaders in your project: one for mobile and one for desktop, and a script that will swap them out at runtime depending on the platform. This is very cool; check out on the asset store at https://www.assetstore.unity3d.com/#/content/14474 and the full review of the pack at http://darkgenesis.zenithmoon.com/unity3dsamplesbeta-anoverview/. Going further If you are the adventurous sort, try expanding your project to add the following: Add some buildings to the town Set up some entry points for a building and work that into your navigation system, for example, a shop Add some rocks to the scene and color each differently using a manual material, maybe even add a script to randomly set the pixel color in the shader instead of creating several materials Add a new scene for the cave using another environment background, and get the player to travel between them Summary This certainly has been a very busy article just to add a background to our scene, but working out how each scene will work is a crucial design element for the entire game; you have to pick a pattern that works for you and your end result once as changing it can be very detrimental (and a lot of work) in the future. In this article, we covered the following topics: Some more practice with the Sprite Editor and sprite slicer including some tips and tricks when it doesn't work (or you want to do it yourself) Some camera tips, tricks, and scripts An overview of sprite layers and sprite sorting Defining boundaries in scenes Scene navigation management and planning levels in your game Some basics of how shaders work for 2D For learning Unity 2D from basic you can refer to https://www.packtpub.com/game-development/learning-unity-2d-game-development-example. Resources for Article:   Further resources on this subject: Build a First Person Shooter [article] Let's Get Physical – Using GameMaker's Physics System [article] Using the Tiled map editor [article]
Read more
  • 0
  • 0
  • 3667
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 $15.99/month. Cancel anytime
article-image-construct-game-development-platformer-revisited-2d-shooter
Packt
05 Jun 2012
11 min read
Save for later

Construct Game Development: Platformer Revisited, a 2D Shooter

Packt
05 Jun 2012
11 min read
(For more resources on Game Development, see here.) Before we start As there is a large amount of ground to cover in this chapter, we'll be moving quickly through steps similar to those we've done before. If it's been a while since you've used Construct, then you may find it useful to read through a chapter or two again before continuing, because certain steps assume that you are able to complete acO ons we have performed in the past. Multiplayer: getting your friends involved We're ready to start our next game, a multiplayer side-scrolling shooter, but before we add any shooO ng, we'll need to have the multiplayer side-scroller part finished first. So let's get to it! Time for action – creating the game assets and title screen The first thing we will need to do is to create our game content and create the first layout of the game, the title screen. First, draw up our player graphics and guns. We'll want the torso to be a separate object from the legs for easier animation. Use red dots where the legs will be attached to as markers for image point placement later on. Also include drawings for three weapons: a pistol, an uzi, and a shotgun: Next, we can draw up our enemies for the game. In this case, we'll use an enemy robot with a turret arm that shoots balls of plasma: We'll also need some scenery and ground objects for the levels: Finally, we'll need a graphic to tell the player to go onto the next screen when no enemies are present: Now we can move on to starting our game. Create a new project and set its Name to SideShooter, and enter your Creator name. Then set the window size to 800 by 600. Create the global variables CanGoNext, CurrentScreen, NumPlayers, P1Lives, P2Lives, GameWonLost, and RespawnY with values 0, 0, 1, 3, 3, 0, and 100 respectively. Following that, rename the first layout to Title and its event sheet to Title events . On this layout, create the layers Text, Buttons, and Background in top-to-bottom order. Selecting the Background layer, create a Panel object and name it Background before setting its top corner filters to Green and bottom corner filters to DarkGreen. Stretch this object to cover the whole of the layout and lock the layer. Now, on the Buttons layer, create a sprite and draw a box with the word Play in it. PosiO on this object in the center of the layout. This will be the start button for our game and should have the name btnPlay. Next, add the Mouse & Keyboard and XAudio2 objects into the layout and give them the global property. In order to finish the layout design, create a Text object on the Text layer and set its name to Title and its text to SideShooter, and position it above the btnPlay object: Switch over to the event sheet editor to add in a Start of layout event and use it to play a music file, Title.mp3, and loop it. This can be any title music you'd like, and it will also be played at the game's end screen. Next, create an event for the MouseKeyboard object with the condition Mouse is over object to check if the mouse overlaps btnPlay. Give this event the action Set colour filter for the btnPlay object to set the filter to Grey – 40. Now create an Else event to set the filter color back to White. In order to finish the event sheet, create an event with the condition On object clicked and select the object btnPlay. Add actions to this event to set the value of NumPlayers to 1 and the value of CurrentScreen to 0 before adding the final System action Next Layout: Time for action – designing the level Now that we have our graphics created, we can put them into our second layout and make the first playable level. Now we're ready to create a level layout. Create a layout and name it Level1 and its event sheet Level1 events . Then create our main game event sheet Game. For the layout, set its width to a multiple of the screen width (800), and check the box for Unbounded scrolling. In the event sheet of this layout, include our main event sheet Game. Next, give this layout the layers HUD, Foreground, FrontScenery, Objects, LevelLights, ShadowCasters, BackScenery, and Walls in top-to-bottom order. After that, set the ScrollX and ScrollY rates of the HUD and Walls layers to 0% On the objects layer, create a Tiled Background object named Ground and give it the smaller light gray tile image. Ensure it has the Solid attribute and stretch and place some of them to form a level design. Now create a Sprite object with the light green tile image and name it CrateBox . Give it the Solid attribute as well and place some around the level too. Have its collisions mode set to Bounding box. Next, create a Sprite named ExitLevel and fill it with a solid color. Give it a width of 32 and stretch it so that it's taller than the display height (600). Then finish the object by checking the box for Invisible on start and placing it at the end of the level: With the base layout complete, we can now add in three more invisible objects to handle scrolling. These are going to be Box objects with the names BackStopper, FrontStopper, and GoNextScreen. Have the BackStopper and FrontStopper objects colored red and marked Solid with a width of 120. Set the Hotspot property of the BackStopper object to Bottom-right, and the FrontStopper to Bottom-Left, before positioning them at 0,600 and 800,600 respectively. Next, have the GoNextScreen box colored green and a width of 32 as well as a Hotspot of Bottom-right. Position this object at 800,600: Time for action – creating player characters and conveyor belt objects Now we can create our player character objects and also add moving and staO c conveyor belts into our level. We are now ready to create our player objects. Start by inserting a Sprite and paste the standing image of the player character legs into it. Have an image point named 1 at the red spot that we drew earlier, and then place the hotspot at the bottom-middle point of the image ( num-pad 2) as shown in the following image: Name this sprite P1Legs, and for its Default animation, set the animation Tag to Stopped before checking the Auto Mirror checkbox in the main Appearance settings of the object. Next, give it the platform behavior with the settings shown as follows: Next, scroll down to the Angle properties box and check the Auto mirror checkbox. Now we are ready to add the object to a family. Scroll up to the Groups sub-group Families and click on Add to bring up the Construct: New Family screen. Note that Construct Classic comes with some default families, but will also display any family that have been used previously: Click on Edit Families to bring up the Construct: Families editor screen. On this screen, enter the name Legs and click on the + button: We can now draw the family image using the image editor that appears, shown as follows: After finishing the image, save it by exiting out of the image editor and check that the family is now in the list. Once finished, click on Done to return to the family selection page. Now select the Legs family and click on Ok: Now we can add the animation Walk in for our object. In this animation, set the Tag to Walking, and add the other leg sprites so they match the following image: Now change the settings of the animation to have an Animation speed of 5 and check the Loop checkbox. Next, copy the object and right-click on the layout to use the Paste Clone option, and name this clone as P2Legs. In the Platform behavior for this object, change the Control setting to Player 2 . Now go into the project properties and scroll to the bottom section, Controls. Click on the Add / Edit option for the Manage Controls setting at the bottom to bring up the controls box: Use the red X button to remove all of the controls below Jump. Then click on the Green plus button to add a new control. Select this control and click on the Pencil and paper button to change its name to Move Left. Click on the Key name for this control to bring up a drop-down box and set it to A. Now, in the Player drop-down box, select Player 2 for this control. It should match the following screenshot: Now continue to add controls until it matches the settings in the following screenshot before clicking on Done: Next, we'll create the bodies of our player objects. Create a Sprite called P1Torso and paste the normal body image of our character into it. Then position the Hotspot in the bottom-middle of the body. Give this sprite an image point 1 in the centre of its hand: Rename the Default animation to Normal and set its speed to 0, and check the Auto Mirror checkbox for this object as well. Create two more animations, Up and Down respectively. Set their frames to match the following screenshot: Now give the object a new family and name it Body. Then create Private Variables of names Weapon, Ammo, and GunAngle . Set the starting values to 0, 99, and 0 respectively. Clone this object as well to create P2Torso, and replace the sprites with the second player graphics. Now select P1Legs and scroll down to the Groups | Container properties to click on Add object and select P1Torso. The properties box and sprites should match the following screenshot: Next, put the P2Torso object into the container P2Legs using the same method. Now, on the Walls layer, create a Tiled Background object named FactoryWall and paste the dark wall graphic into it. Then resize it to 800 in Width by 600 in Height and set its position to 0,0 . The layout should look similar to the following screenshot: Switch to the FrontScenery layer and create a Tiled Background object called ConveyorMiddle, and give it the center piece of the conveyor belt images: Give this object the Private Variables of Direction and Speed with starting values of 0. Place these around the map to act as scenery, as well as using them as an object to move players with at certain points. Set the Direction variable to 1 to have the conveyor belt move right, and 2 to move left. Speed is the attribute used to determine how fast a player character is moved by the conveyor belt; a speed of 25 works well in this instance. The following screenshot shows a moving conveyor belt in the layout: On the same layer, create a Sprite with the name ConveyorStart and Collisions set to None. Use the starting conveyor belt image for this object and set the Hotspot to the middle-right (num-pad 6). Give this sprite the Attribute of Destroy on Startup. Create a second Sprite with the same setttigs called ConveyorEnd and a Hotspot in the middle-left (num-pad 4). Both sprites are shown in the following screenshot: Time for action – creating the HUD objects Now, we will move on to creating the Heads Up Display (HUD) objects for our game. We are now ready to create the HUD objects for our game. Switch to the HUD layer and create a Sprite called P1Face, and give it an image of the P1Torso head and set its Collisions mode to Bounding Box: Next, create a Text object called P1Info and set it up to match the following screenshot: Create similar objects replacing P1 with P2 for the second player. In this case, have the objects match the following layout screenshot: Now create a Text object for when the second player is not playing, and call it P2Join. Set its text to Press left control to join and have it matched to the following screenshot: Give it a Sine behavior to make it fade in and out by matching its settings to those in the following screenshot: Now create the final HUD item, a Sprite called NextSign, and place the next arrow image into it. Set the Collisions of this object to None:
Read more
  • 0
  • 0
  • 3524

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
  • 3519

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
  • 3454

article-image-buildbox-2-game-development-peek-boo
Packt
23 Sep 2016
20 min read
Save for later

Buildbox 2 Game Development: peek-a-boo

Packt
23 Sep 2016
20 min read
In this article by Ty Audronis author of the book Buildbox 2 Game Development, teaches the reader the Buildbox 2 game development environment by example.The following excerpts from the book should help you gain an understanding of the teaching style and the feel of the book.The largest example we give is by making a game called Ramblin' Rover (a motocross-style game that uses some of the most basic to the most advanced features of Buildbox).Let's take a quick look. (For more resources related to this topic, see here.) Making the Rover Jump As we've mentioned before, we're making a hybrid game. That is, it's a combination of a motocross game, a platformer, and a side-scrolling shooter game. Our initial rover will not be able to shoot at anything (we'll save this feature for the next upgraded rover that anyone can buy with in-game currency). But this rover will need to jump in order to make the game more fun. As we know, NASA has never made a rover for Mars that jumps. But if they did do this, how would they do it? The surface of Mars is a combination of dust and rocks, so the surface conditions vary greatly in both traction and softness. One viable way is to make the rover move in the same way a spacecraft manoeuvres (using little gas jets). And since the gravity on Mars is lower than that on Earth, this seems legit enough to include it in our game. While in our Mars Training Ground world, open the character properties for Training Rover. Drag the animated PNG sequence located in our Projects/RamblinRover/Characters/Rover001-Jump folder (a small four-frame animation) into the JumpAnimation field. Now we have an animation of a jump-jet firing when we jump. We just need to make our rover actually jump. Your Properties window should look like the following screenshot: The preceding screenshot shows the relevant sections of the character's properties window We're now going to revisit the Character Gameplay Settings section. Scroll the Properties window all the way down to this section. Here's where we actually configure a few settings in order to make the rover jump. The previous screenshot shows the section as we're going to set it up. You can configure your settings similarly. The first setting we are considering is Jump Force. You may notice that the vertical force is set to 55. Since our gravity is -20 in this world, we need enough force to not only counteract the gravity, but also to give us a decent height (about half the screen). A good rule is to just make our Jump Force 2x our Gravity. Next is Jump Counter. We've set it to 1. By default, it's set to 0. This actually means infinity. When JumpCounter is set to 0, there is no limit to how many times a player can use the jump boost… They could effectively ride the top of the screen using the jump boost,such as a flappy bird control. So, we set it to 1 in order to limit the jumps to one at a time. There is also a strange oddity with the Buildbox that we can exploit with this. The jump counter resets only after the rover hits the ground. But, there's a funny thing… The rover itself never actually touches the ground (unless it crashes), only the wheels do. There is one other way to reset the jump counter: by doing a flip. What this means is that once players use their jump up, the only way to reset it is to do a flip-trick off a ramp. Add a level of difficulty and excitement to the game using a quirk of the development software! We could trick the software into believing that the character is simply close enough to the ground to reset the counter by increasing Ground Threshold to the distance that the body is from the ground when the wheels have landed. But why do this? It's kind of cool that a player has to do a trick to reset the jump jets. Finally, let's untick the Jump From Ground checkbox. Since we're using jets for our boost, it makes sense that the driver could activate them while in the air. Plus, as we've already said, the body never meets the ground. Again, we could raise the ground threshold, but let's not (for the reasonsstated previously). Awesome! Go ahead and give it a try by previewing the level. Try jumping on the small ramp that we created, which is used to get on top of our cave. Now, instead of barely clearing it, the rover will easily clear it, and the player can then reset the counter by doing a flip off the big ramp on top. Making a Game Over screen This exercise will show you how to make some connections and new nodes using Game Mind Map. The first thing we're going to want is an event listener to sense when a character dies. It sounds complex, and if we were coding a game, this would take several lines of code to accomplish. In Buildbox, it's a simple drag-and-drop method. If you double-click on the Game Field UI node, you'll be presented with the overlay for the UI and controls during gameplay. Since this is a basic template, you are actually presented with a blank screen. This template is for you to play around with on a computer, so no controls are on the screen. Instead, it is assumed that you would use keyboard controls to play the demo game. This is why the screen looks blank: There are some significant differences between the UI editor and the World editor. You can notice that the Character tab from the asset library is missing, and there is a timeline editor on the bottom. We'll get into how to use this timeline later. For now, let's keep things simple and add our Game Over sensor. If you expand the Logic tab in the asset library, you'll find the Event Observer object. You can drag this object anywhere onto the stage. It doesn't even have to be in the visible window (the dark area in the center of the stage). So long as it's somewhere on the stage, the game can use this logic asset. If you do put it on the visible area of the stage, don't worry; it's an invisible asset, and won't show in your game. While the Event observer is selected on the stage, you'll notice that its properties pop up in the properties window (on the right side of the screen). By default, the Game Over type of event is selected. But if you select this drop-down menu, you'll notice a ton of different event types that this logic asset can handle. Let's leave all of the properties at their default values (except the name; change this to Game Over) and go back to Game Mind Map (the top-left button): Do you notice anything different? The Game Field UI node now has a Game Over output. Now, we just need a place to send this output. Right-click on the blank space of the grid area. Now you can either create a new world or new UI. Select Add New UI and you'll see a new green node that is titled New UI1. This new UI will be your Game Over screen when a character dies. Before we can use this new node, it needs to be connected to the Game Over output of Game Field UI. This process is exceedingly simple. Just hold down your left mouse button on the Game Over output's dark dot, and drag it to the New UI1's Load dark dot (on the left side of the New UI1 node). Congratulations, you've just created your first connected node. We're not done yet, though. We need to make this Game Over screen link back to restart the game. First, by selecting the New UI1 node, change its name using the parameters window (on the right of the screen) to Game Over UI. Make sure you hit your Enter key; this will commit the changed name. Now double-click on the Game Over UI node so we can add some elements to the screen. You can't have a Game Over screen without the words Game Over, so let's add some text. So, we've pretty much completed the game field (except for some minor items that we'll address quite soon). But believe it or not, we're only halfway there! In this article, we're going to finally create our other two rovers, and we'll test and tweak our scenes with them. We'll set up all of our menus, information screens, and even a coin shop where we can use in-game currency to buy the other two rovers, or even use some real-world currency to short-cut and buy more in-game currency. And speaking of monetization, we'll set up two different types of advertising from multiple providers to help us make some extra cash. Or, in the coin-shop, players can pay a modest fee to remove all advertising! Ready? Well, here we go! We got a fever, and the only cure is more rovers! So now that we've created other worlds, we definitely need to set up some rovers that are capable of traversing them. Let's begin with the optimal rover for Gliese. This one is called the K.R.A.B.B. (no, it doesn't actually stand for anything…but the rover looks like a crab, and acronyms look more military-like). Go ahead and drag all of the images in the Rover002-Body folder as characters. Don't worry about the error message. This just tells you that only one character can be on the stage at a time. The software still loads this new character into the library, and that's all we really want at this time anyway. Of course, drag the images in the Rover002-Jump folder to the Jump Animation field, and the LaserShot.png file to the Bullet Animation field. Set up your K.R.A.B.B. with the following settings: For Collision Shape, match this: In the Asset Library, drag the K.R.A.B.B. above the Mars Training Rover. This will make it the default rover. Now, you can test your Gliese level (by soloing each scene) with this rover to make sure it's challenging, yet attainable. You'll notice some problems with the gun destroying ground objects, but we'll solve that soon enough. Now, let's do the same with Rover 003. This one uses a single image for the Default Animation, but an image sequence for the jump. We'll get to the bullet for this one in a moment, but set it up as follows: Collision Shape should look as follows: You'll notice that a lot of the settings are different on this character, and you may wonder what the advantage of this is (since it doesn't lean as much as the K.R.A.B.B.). Well, it's a tank, so the damage it can take will be higher (which we'll set up shortly), and it can do multiple jumps before recharging (five, to be exact). This way, this rover can fly using flappy-bird style controls for short distances. It's going to take a lot more skill to pilot this rover, but once mastered, it'll be unstoppable. Let's move onto the bullet for this rover. Click on the Edit button (the little pencil icon) inside Bullet Animation (once you've dragged the missile.png file into the field), and let's add a flame trail. Set up a particle emitter on the missile, and position it as shown in the following screenshots: The image on the left shows the placement of the missile and the particle emitter. On the right, you can see the flame set up. You may wonder why it is pointed in the opposite direction. This will actually make the flames look more realistic (as if they're drifting behind the missile). Preparing graphic assets for use in Buildbox Okay, so as I said before, the only graphic assets that Buildbox can use are PNG files. If this was just a simple tutorial on how to make Ramblin' Rover, we could leave it there. But it's not just it. Ramblin' Rover is just an example of how a game is made, but we want to give you all of the tools and baseknowledge you need to create all of your own games from scratch. Even if you don't create your own graphic assets, you need to be able to tell anybody creating them for you how you want them. And more importantly, you need to know why. Graphics are absolutely the most important thing in developing a game. After all, you saw how just some eyes and sneakers made a cute character that people would want to see. Graphics create your world. They create characters that people want to succeed. Most importantly, graphics create the feel of your game, and differentiate it from other games on the market. What exactly is a PNG file? Anybody remember GIF files? No, not animated GIFs that you see on most chat-rooms and on Facebook (although they are related). Back in the 1990s, a still-frame GIF file was the best way to have a graphics file that had a transparent background. GIFs can be used for animation, and can have a number of different purposes. However, GIFs were clunky. How so? Well, they had a type of compression known as lossy. This just means that when compressed, information was lost, and artifacts and noise could pop up and be present. Furthermore, GIFs used indexed colors. This means that anywhere from 2 to 256 colors could be used, and that's why you see something known as banding in GIF imagery. Banding is where something in real life goes from dark to light because of lighting and shadows. In real life, it's a smooth transition known as a gradient. With indexed colors, banding can occur when these various shades are outside of the index. In this case, the colors of these pixels are quantized (or snapped) to the nearest color in the index. The images here show a noisy and banded GIF (left) versus the original picture (right): So, along came PNGs (Portable Network Graphics is what it stands for). Originally, the PNG format was what a program called Macromedia Fireworks used to save projects. Now,the same software is called Adobe Fireworks and is part of the Creative Cloud. Fireworks would cut up a graphics file into a table or image map and make areas of the image clickable via hyperlink for HTML web files. PNGs were still not widely supported by web browsers, so it would export the final web files as GIFs or JPEGs. But somewhere along the line, someone realized that the PNG image itself was extremely bandwidthefficient. So, in the 2000s, PNGs started to see some support on browsers. Up until around 2008, though, Microsoft's Internet Explorer still did not support PNGs with transparency, so some strange CSS hacks needed to be done to utilize them. Today, though, the PNG file is the most widely used network-based image file. It's lossless, has great transparency, and is extremely efficient. Since PNGs are very widely used, and this is probably why Buildbox restricts compatibility to this format. Remember, Buildbox can export for multiple mobile and desktop platforms. Alright, so PNGs are great and very compatible. But there are multiple flavours of PNG files. So, what differentiates them? What bit-ratings mean? When dealing with bit-ratings, you have to understand that when you hear 8-bit image and 24-bit image, it maybe talking about two different types of rating, or even exactly the same type of image. Confused? Good, because when dealing with a graphics professional to create your assets, you're going to have to be a lot more specific, so let's give you a brief education in this. Your typical image is 8 bits per channel (8 bpc), or 24 bits total (because there are three channels: red, green, and blue). This is also what they mean by a 16.7 million-color image. The math is pretty simple. A bit is either 0 or 1. 8 bits may look something as 01100110. This means that there are 256 possible combinations on that channel. Why? Because to calculate the number of possibilities, you take the number of possible values per slot and take it to that power. 0 or 1; that's 2 possibilities, and 8 bit is 8 slots. 2x2x2x2x2x2x2x2 (2 to the 8th power) is 256. To combine colors on a pixel, you'd need to multiply the possibilities such as 256x256x256 (which is 16.7 million). This is how they know that there are 16.7 million possible colors in an 8 bpc or 24-bit image. So saying 8 bit may mean per channel or overall. This is why it's extremely important to add thr "channel" word if that's what you mean. Finally, there is a fourth channel called alpha. The alpha channel is the transparency channel. So when you're talking about a 24-bit PNG with transparency, you're really talking about a 32-bit image. Why is this important to know? This is because some graphics programs (such as Photoshop) have 24-bit PNG as an option with a checkbox for transparency. But some other programs (such as the 3D software we used called Lightwave) have an option for a 24-bit PNG and a 32-bit PNG. This is essentially the same as the Photoshop options, but with different names. By understanding what these bits per channel are and what they do, you can navigate your image-creating software options better. So, what's an 8-bit PNG, and why is it so important to differentiate it from an 8-bit per channel PNG (or 24-bit PNG)? It is because an 8-bit PNG is highly compressed. Much like a GIF, it uses indexed colors. It also uses a great algorithm to "dither" or blend the colors to fill them in to avoid banding. 8-bit PNG files are extremely efficient on resources (that is, they are much smaller files), but they still look good, unless they have transparency. Because they are so highly compressed, the alpha channel is included in the 8-bits. So, if you use 8-bit PNG files for objects that require transparency, they will end up with a white-ghosting effect around them and look terrible on screen, much like a weather report where the weather reporter's green screen is bad. So, the rule is… So, what all this means to you is pretty simple. For objects that require transparency channels, always use 24-bit PNG files with transparency (also called 8 bits per channel, or 32-bit images). For objects that have no transparency (such as block-shaped obstacles and objects), use 8-bit PNG files. By following this rule, you'll keep your game looking great while avoiding bloating your project files. In the end, Buildbox repacks all of the images in your project into atlases (which we'll cover later) that are 32 bit. However, it's always a good practice to stay lean. If you were a Buildbox 1.x user, you may remember that Buildbox had some issues with DPI (dots per inch) between the standard 72 and 144 on retina displays. This issue is a thing of the past with Buildbox 2. Image sequences Think of a film strip. It's just a sequence of still-images known as frames. Your standard United States film runs at 24 frames per second (well, really 23.976, but let's just round up for our purposes). Also, in the US, television runs at 30 frames per second (again, 29.97, but whatever…let's round up). Remember that each image in our sequence is a full image with all of the resources associated with it. We can quite literally cut our necessary resources in half by cutting this to 15 frames per second (fps). If you open the content you downloaded, and navigate to Projects/RamblinRover/Characters/Rover001-Body, you'll see that the images are named Rover001-body_001.png, Rover001-body_002.png and so on. The final number indicates the number that should play in the sequence (first 001, then 002, and so on). The animation is really just the satellite dish rotating, and the scanner light in the window rotating as well. But what you'll really notice is that this animation is loopable. All loopable means is that the animation can loop (play over and over again) without you noticing a bump in the footage (the final frame leads seamlessly back to the first). If you're not creating these animations yourself, you'll need to make sure to specify to your graphics professional to make these animations loopable at 15 fps. They should understand exactly what you mean, and if they don't…you may consider finding a new animator. Recommended software for graphics assets For the purposes of context (now that you understand more about graphics and Buildbox), a bit of reinforcement couldn't hurt. A key piece of graphics software is the Adobe Creative Cloud subscription (http://www.adobe.com/CreativeCloud ). Given its bang for the buck, it just can't be beaten. With it, you'll get Photoshop (which can be used for all graphics assets from your game's icon to obstacles and other objects), Illustrator (which is great for navigational buttons), After Effects (very useful for animated image sequences), Premiere Pro (a video editing application for marketing videos from screen-captured gameplay), and Audition (for editing all your sound). You may also want some 3D software, such as Lightwave, 3D Studio Max, or Maya. This can greatly improve the ability to make characters, enemies, and to create still renders for menus and backgrounds. Most of the assets in Ramblin' Rover were created with the 3D software Lightwave. There are free options for all of these tools. However, there are not nearly as many tutorials and resources available on the web to help you learn and create using these. One key thing to remember when using free software: if it's free…you're the product. In other words, some benefits come with paid software, such as better support, and being part of the industry standard. Free software seems to be in a perpetual state of "beta testing." If using free software, read your End User License Agreement (known as a EULA) very carefully. Some software may require you to credit them in some way for the privilege of using their software for profit. They may even lay claim to part of your profits. Okay, let's get to actually using our graphics in Ramblin' Rover… Summary See? It's not that tough to follow. By using plain-English explanations combined with demonstrating some significant and intricate processes, you'll be taken on a journey meant to stimulate your imagination and educate you on how to use the software. Along with the book comes the complete project files and assets to help you follow along the entire way through the build process. You'll be making your own games in no time! Resources for Article: Further resources on this subject: What Makes a Game a Game? [article] Alice 3: Controlling the Behavior of Animations [article] Building a Gallery Application [article]
Read more
  • 0
  • 0
  • 3330
article-image-introducing-variables
Packt
24 Jun 2014
6 min read
Save for later

Introducing variables

Packt
24 Jun 2014
6 min read
(For more resources related to this topic, see here.) In order to store data, you have to store data in the right kind of variables. We can think of variables as boxes, and what you put in these boxes depends on what type of box it is. In most native programming languages, you have to declare a variable and its type. Number variables Let's go over some of the major types of variables. The first type is number variables. These variables store numbers and not letters. That means, if you tried to put a name in, let's say "John Bura", then the app simply won't work. Integer variables There are numerous different types of number variables. Integer variables, called Int variables, can be positive or negative whole numbers—you cannot have a decimal at all. So, you could put -1 as an integer variable but not 1.2. Real variables Real variables can be positive or negative, and they can be decimal numbers. A real variable can be 1.0, -40.4, or 100.1, for instance. There are other kinds of number variables as well. They are used in more specific situations. For the most part, integer and real variables are the ones you need to know—make sure you don't get them mixed up. If you were to run an app with this kind of mismatch, chances are it won't work. String variables There is another kind of variable that is really important. This type of variable is called a string variable. String variables are variables that comprise letters or words. This means that if you want to record a character's name, then you will have to use a string variable. In most programming languages, string variables have to be in quotes, for example, "John Bura". The quote marks tell the computer that the characters within are actually strings that the computer can use. When you put a number 1 into a string, is it a real number 1 or is it just a fake number? It's a fake number because strings are not numbers—they are strings. Even though the string shows the number 1, it isn't actually the number 1. Strings are meant to display characters, and numbers are meant to do math. Strings are not meant to do math—they just hold characters. If you tried to do math with a string, it wouldn't work (except in JavaScript, which we will talk about shortly). Strings shouldn't be used for calculations—they are meant to hold and display characters. If we have a string "1", it will be recorded as a character rather than an integer that can be used for calculations. Boolean variables The last main type of variable that we need to talk about is Boolean variables. Boolean variables are either true or false, and they are very important when it comes to games. They are used where there can only be two options. The following are some examples of Boolean variables: isAlive isShooting isInAppPurchaseCompleted isConnectedToInternet Most of these variables start off with the word is. This is usually done to signify that the variable that we are using is a Boolean. When you make games, you tend to use a lot of Boolean variables because there are so many states that game objects can be in. Often, these states have only two options, and the best thing to do is use a Boolean. Sometimes, you need to use an integer instead of a Boolean. Usually, 0 equals false and 1 equals true. Other variables When it comes to game production, there are a lot of specific variables that differ from environment to environment. Sometimes, there are GameObject variables, and there can also be a whole bunch of more specific variables. Declaring variables If you want to store any kind of data in variables, you have to declare them first. In the backend of Construct 2, there are a lot of variables that are already declared for you. This means that Construct 2 takes out the work of declaring variables. The variables that are taken care of for you include the following: Keyboard Mouse position Mouse angle Type of web browser Writing variables in code When we use Construct 2, a lot of the backend busywork has already been done for us. So, how do we declare variables in code? Usually, variables are declared at the top of the coding document, as shown in the following code: Int score; Real timescale = 1.2; Bool isDead; Bool isShooting = false; String name = "John Bura"; Let's take a look at all of them. The type of variable is listed first. In this case, we have the Int, Real, Bool (Boolean), and String variables. Next, we have the name of the variable. If you look carefully, you can see that certain variables have an = (equals sign) and some do not. When we have a variable with an equals sign, we initialize it. This means that we set the information in the variable right away. Sometimes, you need to do this and at other times, you do not. For example, a score does not need to be initialized because we are going to change the score as the game progresses. As you already know, you can initialize a Boolean variable to either true or false—these are the only two states a Boolean variable can be in. You will also notice that there are quotes around the string variable. Let's take a look at some examples that won't work: Int score = -1.2; Bool isDead = "false"; String name = John Bura; There is something wrong with all these examples. First of all, the Int variable cannot be a decimal. Second, the Bool variable has quotes around it. Lastly, the String variable has no quotes. In most environments, this will cause the program to not work. However, in HTML5 or JavaScript, the variable is changed to fit the situation. Summary In this article, we learned about the different types of variables and even looked at a few correct and incorrect variable declarations. If you are making a game, get used to making and setting lots of variables. The best part is that Construct 2 makes handling variables really easy. Resources for Article: Further resources on this subject: 2D game development with Monkey [article] Microsoft XNA 4.0 Game Development: Receiving Player Input [article] Flash Game Development: Making of Astro-PANIC! [article]
Read more
  • 0
  • 0
  • 3176

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
  • 3152

article-image-designing-avatar-flash-multiplayer-virtual-worlds
Packt
27 Aug 2010
9 min read
Save for later

Designing an Avatar in Flash Multiplayer Virtual Worlds

Packt
27 Aug 2010
9 min read
(For more resources on Flash, see here.) Designing an avatar Avatar is very important in a virtual world because most of the features are designed around avatars. Users interact with each other via their avatars, they explore the virtual world via avatars, and they complete challenges to level up their avatars. An avatar is composited by graphics and animation. The avatar graphics are its looks. It is not a static image but a collection of images to display the directions and appearance. There are different approaches of drawing the avatar graphics depending on the render methods and how many directions and animations the avatar needs. Animations represent different actions of the avatar. The most basic animation is walking. Other animations such as hand waving and throwing objects are also common. There will be different animation sets for different virtual world designs. A fighting topic virtual world will probably contain a collection of fighting animation sets. A hunting topic virtual world will contain animations of collection items and using hunting tools. Determining the direction numbers of avatars' views Isometric tile is composed by diamond shapes with four-edge connection to the other tiles. It is not hard to imagine that every avatar in the isometric view may face towards four directions. They are the north east, south east, south west, and north west. However, sometimes using only these four directions may not be enough; some game designs may require the avatar to face the user or walk to the other isometric tile a cross the diamond corner. In this case, eight directions are required. The direction number of the avatars affects the artwork drawing directly. Just imagine that we are building a virtual world where players can fight with each other. How many animations are there for an avatar to fight? Say, five sets of animations. How many directions can the avatar faces? 4? 8? Or even 12? For example, we are now talking about five sets of animations with 8 directions of each avatar. That's already 40 animations for only one avatar. We may design the virtual world to have 12 kinds of avatars and each avatar to have different clothes for customization. The graphics workload keeps increasing when only one of these aspects increases. That's why I often consider different approaches that reduce the graphic workload of the avatars. Take four directions as an example. In most cases, we have very similar animations when the avatar is facing south-east and south-west. And the animation of north-east and north-west are similar too. Therefore, it is a common technique that mirrors the animation of west side into east side. It can be easily done in Flash by just changing the x-axis of the scaling property to between -1 and 1. This property results in the avatar flipping from one side to another side. For a 4-directions animation set, only 2 directions need to be drawn. In an 8-directions animation set, only 5 directions need to be drawn. Next, we will discuss the rendering methods that will conclude how the amount of directions, animations, and customization affect the graphic workload. Rendering avatars in Flash virtual world There are different approaches to render avatars in Flash virtual world. Each rendered method comes with both advantages and disadvantages. Some methods take more time to draw with fancy outlook while others may take more time to program. It is important to decide which rendering methods of the avatar are required in predevelopment stage. It will be much more difficult to change the rendering method after the project is in development. We will discuss different rendering methods and the pros and cons of them. Drawing an avatar using vector animation It is convenient to use the Flash native vector drawing for avatar because every drawing can be done within the Flash. The output can be cute and cartoon style. One advantage of using vector is that color customization is easy to implement by using the native ActionScript color transform. We can easily assign different colors to different parts of the avatar without extra graphic drawing. Another advantage of using vector animation is that we can scale up and down the avatars whenever needed. It is useful when we need to zoom in or out of the map and the avatars in the virtual world. The following graph shows the comparison of scaling up a vector and bitmap graphic: The disadvantage is that we need to draw the animation of every part of the avatar in every direction frame by frame. Flash tweening can help but the workload is heavier than other methods. We can prerender the animations or control them by ActionScript in methods discussed later. In vector animation, every animation is hand-drawn and thus any late modification on the avatar design can cost quite a lot of workload. There may not be too many directions of the avatars meaning the rotation of the avatars will not be very smooth. Rendering avatars using bitmap sprite sheet Sprite sheet is a graphics technique that is used in almost all game platforms. Sprite sheet is a large bitmap file that contains every frame of animation. Bitmap data from each frame is masked and rendered to the screen. A Flash developer may think that there is a timeline with frame one on the top left and counting the frame from left to right in each row from top to bottom. This technique is useful when the avatar graphic designer has experience in other game platforms. Another advantage of using bitmap data is faster rendering than vector in Flash player. The other advantage is the sprite sheet can be rendered from 3D software. For example, we can make an avatar model in Maya (http://autodesk.com/maya) or 3Ds Max (http://autodesk.com/3dsmax) with animations set up. Then we set up eight cameras with orthographic perspective. The orthographic perspective ensures the rendered image fits the isometric world. After setting up the scene, just render the whole animation with eight different cameras and we will get all the bitmap files of the avatar. The benefit is that the rendering process is automatic so that we can reduce the workload a lot. Later if we want to modify the character, we only need to modify it in the 3D software and render it again. One big disadvantage of using sprite sheet is the file size. The sprite sheets are in bitmap format and one set of animation can cost up to several hundred kilobytes. The file size can be very large when there are many animations and many more bitmaps for switching styles of the avatar. The other disadvantage is that changing color is quite difficult. Unlike vector rendering where color replacement can be done by ActionScript, we need to replace another bitmap data to change the color. That means every available color doubles the file size. Rendering avatars using real-time 3D engine We described how to use 3D software to prerender graphics of the avatars in the previous section. Instead of prerendering the graphics into 2D bitmap, we can integrate a Flash 3D engine to render the 3D model into isometric view in real time. Real-time 3D rendering is the next trend of Flash. There are several 3D engines available in the market that support rendering complex 3D models with animations. Papervision3D (http://blog.papervision3d.org/) and Away3D (http://away3d.com/) are two examples among them. The advantage of using 3D rendering in isometric is that the rotation of avatars can be very smooth. Also different textures can share the same model and different models can share the same animation skeleton. Thanks to this great graphic reusability, 3D rendering virtual world can create different combinations of avatar appearance and animations without adding extra graphic workload in development. However, one disadvantage of using 3D rendering is the Flash player performance. The latest version of Flash player is 10.1 at the time of writing. The following screenshots show that the CPU resources usage is very high when rendering the isometric 3D environment with three avatars on screen: Rendering avatars using 2D bone skeleton Bone skeleton used to be an uncommon method to render avatar. What it does is creates an animated skeleton and then glues different parts of body together onto the skeleton. It is somehow similar to the skeleton and mesh relationship in 3-D software but in two dimensions instead. A lot of mathematics is needed to calculate the position and rotation of each part of the body and make the implementation difficult. Thanks to the introduction of bone tool and inverse kinematics in Flash CS4, this technique is becoming more mature and easier to be used in the Flash world. Adobe has posted a tutorial about using bone tool to create a 2D character (http://www.adobe.com/devnet/flash/articles/character_animation_ik.html). The following screenshot shows another bone skeleton example from gotoAndPlay demonstrating how to glue the parts into a walking animation. The post can be found in this link: http://www.gotoandplay.it/_articles/2007/04/skeletal_animation.php The advantage of using 2D bone skeleton is that animations are controlled by ActionScript. The reusing of animations means this technique fits those game designs that require many animations. A dancing virtual world that requires a lot of different unique animations is one example that may need this technique. One disadvantage is that the large amount of mathematic calculation for the animations makes it difficult to implement. Every rendering methods has its own advantages and disadvantages and not one of the methods fits all type of games. It is the game designer's job to decide a suitable rendering method for a game or virtual world project. Therefore, it is important to know their limitations and consider thoughtfully before getting started with development. We can take a look at how other Flash virtual worlds render avatars by checking the showcase of the SmartFoxServer (http://www.smartfoxserver.com/showcase/).
Read more
  • 0
  • 0
  • 2971
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
  • 2865

article-image-data-driven-design
Packt
10 Jul 2013
21 min read
Save for later

Data-driven Design

Packt
10 Jul 2013
21 min read
(For more resources related to this topic, see here.) Loading XML files I have chosen to use XML files because they are so easy to parse. We are not going to write our own XML parser, rather we will use an open source library called TinyXML. TinyXML was written by Lee Thomason and is available under the zlib license from http://sourceforge.net/projects/tinyxml/. Once downloaded the only setup we need to do is to include a few of the files in our project: tinyxmlerror.cpp tinyxmlparser.cpp tinystr.cpp tinystr.h tinyxml.cpp tinyxml.h Also, at the top of tinyxml.h, add this line of code: #define TIXML_USE_STL By doing this we ensure that we are using the STL versions of the TinyXML functions. We can now go through a little of how an XML file is structured. It's actually fairly simple and we will only give a brief overview to help you get up to speed with how we will use it. Basic XML structure Here is a basic XML file: <?xml version="1.0" ?> <ROOT> <ELEMENT> </ELEMENT> </ROOT> The first line of the file defines the format of the XML file. The second line is our Root element; everything else is a child of this element. The third line is the first child of the root element. Now let's look at a slightly more complicated XML file: <?xml version="1.0" ?> <ROOT> <ELEMENTS> <ELEMENT>Hello,</ELEMENT> <ELEMENT> World!</ELEMENT> </ELEMENTS> </ROOT> As you can see we have now added children to the first child element. You can nest as many children as you like. But without a good structure, your XML file may become very hard to read. If we were to parse the above file, here are the steps we would take: Load the XML file. Get the root element, <ROOT>. Get the first child of the root element, <ELEMENTS>. For each child, <ELEMENT> of <ELEMENTS>, get the content. Close the file. Another useful XML feature is the use of attributes. Here is an example: <ROOT> <ELEMENTS> <ELEMENT text="Hello,"/> <ELEMENT text=" World!"/> </ELEMENTS> </ROOT> We have now stored the text we want in an attribute named text. When this file is parsed, we would now grab the text attribute for each element and store that instead of the content between the <ELEMENT></ELEMENT> tags. This is especially useful for us as we can use attributes to store lots of different values for our objects. So let's look at something closer to what we will use in our game: <?xml version="1.0" ?> <STATES> <!--The Menu State--> <MENU> <TEXTURES> <texture filename="button.png" ID="playbutton"/> <texture filename="exit.png" ID="exitbutton"/> </TEXTURES> <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton"/> </OBJECTS> </MENU> <!--The Play State--> <PLAY> </PLAY> <!-- The Game Over State --> <GAMEOVER> </GAMEOVER> </STATES> This is slightly more complex. We define each state in its own element and within this element we have objects and textures with various attributes. These attributes can be loaded in to create the state. With this knowledge of XML you can easily create your own file structures if what we cover within this book is not to your needs. Implementing Object Factories We are now armed with a little XML knowledge but before we move forward, we are going to take a look at Object Factories. An object factory is a class that is tasked with the creation of our objects. Essentially, we tell the factory the object we would like it to create and it goes ahead and creates a new instance of that object and then returns it. We can start by looking at a rudimentary implementation: GameObject* GameObjectFactory::createGameObject(ID id) { switch(id) { case "PLAYER": return new Player(); break; case "ENEMY": return new Enemy(); break; // lots more object types } } This function is very simple. We pass in an ID for the object and the factory uses a big switch statement to look it up and return the correct object. Not a terrible solution but also not a particularly good one, as the factory will need to know about each type it needs to create and maintaining the switch statement for many different objects would be extremely tedious. We want this factory not to care about which type we ask for. It shouldn't need to know all of the specific types we want it to create. Luckily this is something that we can definitely achieve. Using Distributed Factories Through the use of Distributed Factories we can make a generic object factory that will create any of our types. Distributed factories allow us to dynamically maintain the types of objects we want our factory to create, rather than hard code them into a function (like in the preceding simple example). The approach we will take is to have the factory contain std::map that maps a string (the type of our object) to a small class called Creator whose only purpose is the creation of a specific object. We will register a new type with the factory using a function that takes a string (the ID) and a Creator class and adds them to the factory's map. We are going to start with the base class for all the Creator types. Create GameObjectFactory.h and declare this class at the top of the file. #include <string> #include <map> #include "GameObject.h" class BaseCreator { public: virtual GameObject* createGameObject() const = 0; virtual ~BaseCreator() {} }; We can now go ahead and create the rest of our factory and then go through it piece by piece. class GameObjectFactory { public: bool registerType(std::string typeID, BaseCreator* pCreator) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); // if the type is already registered, do nothing if(it != m_creators.end()) { delete pCreator; return false; } m_creators[typeID] = pCreator; return true; } GameObject* create(std::string typeID) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); if(it == m_creators.end()) { std::cout << "could not find type: " << typeID << "n"; return NULL; } BaseCreator* pCreator = (*it).second; return pCreator->createGameObject(); } private: std::map<std::string, BaseCreator*> m_creators; }; This is quite a small class but it is actually very powerful. We will cover each part separately starting with std::map m_creators. std::map<std::string, BaseCreator*> m_creators; This map holds the important elements of our factory, the functions of the class essentially either add or remove from this map. This becomes apparent when we look at the registerType function: bool registerType(std::string typeID, BaseCreator* pCreator) This function takes the ID we want to associate the object type with (as a string), and the creator object for that class. The function then attempts to find the type using the std::mapfind function: std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); If the type is found then it is already registered. The function then deletes the passed in pointer and returns false: if(it != m_creators.end()) { delete pCreator; return false; } If the type is not already registered then it can be assigned to the map and then true is returned: m_creators[typeID] = pCreator; return true; } As you can see, the registerType function is actually very simple; it is just a way to add types to the map. The create function is very similar: GameObject* create(std::string typeID) { std::map<std::string, BaseCreator*>::iterator it = m_creators.find(typeID); if(it == m_creators.end()) { std::cout << "could not find type: " << typeID << "n"; return 0; } BaseCreator* pCreator = (*it).second; return pCreator->createGameObject(); } The function looks for the type in the same way as registerType does, but this time it checks whether the type was not found (as opposed to found). If the type is not found we return 0, and if the type is found then we use the Creator object for that type to return a new instance of it as a pointer to GameObject. It is worth noting that the GameObjectFactory class should probably be a singleton. We won't cover how to make it a singleton in this article. Try implementing it yourself or see how it is implemented in the source code download. Fitting the factory into the framework With our factory now in place, we can start altering our GameObject classes to use it. Our first step is to ensure that we have a Creator class for each of our objects. Here is one for Player: class PlayerCreator : public BaseCreator { GameObject* createGameObject() const { return new Player(); } }; This can be added to the bottom of the Player.h file. Any object we want the factory to create must have its own Creator implementation. Another addition we must make is to move LoaderParams from the constructor to their own function called load. This stops the need for us to pass the LoaderParams object to the factory itself. We will put the load function into the GameObject base class, as we want every object to have one. class GameObject { public: virtual void draw()=0; virtual void update()=0; virtual void clean()=0; // new load function virtual void load(const LoaderParams* pParams)=0; protected: GameObject() {} virtual ~GameObject() {} }; Each of our derived classes will now need to implement this load function. The SDLGameObject class will now look like this: SDLGameObject::SDLGameObject() : GameObject() { } voidSDLGameObject::load(const LoaderParams *pParams) { m_position = Vector2D(pParams->getX(),pParams->getY()); m_velocity = Vector2D(0,0); m_acceleration = Vector2D(0,0); m_width = pParams->getWidth(); m_height = pParams->getHeight(); m_textureID = pParams->getTextureID(); m_currentRow = 1; m_currentFrame = 1; m_numFrames = pParams->getNumFrames(); } Our objects that derive from SDLGameObject can use this load function as well; for example, here is the Player::load function: Player::Player() : SDLGameObject() { } void Player::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); } This may seem a bit pointless but it actually saves us having to pass through LoaderParams everywhere. Without it, we would need to pass LoaderParams through the factory's create function which would then in turn pass it through to the Creator object. We have eliminated the need for this by having a specific function that handles parsing our loading values. This will make more sense once we start parsing our states from a file. We have another issue which needs rectifying; we have two classes with extra parameters in their constructors (MenuButton and AnimatedGraphic). Both classes take an extra parameter as well as LoaderParams. To combat this we will add these values to LoaderParams and give them default values. LoaderParams(int x, int y, int width, int height, std::string textureID,int numFrames, int callbackID = 0, int animSpeed = 0) : m_x(x), m_y(y), m_width(width), m_height(height), m_textureID(textureID), m_numFrames(numFrames), m_callbackID(callbackID), m_animSpeed(animSpeed) { } In other words, if the parameter is not passed in, then the default values will be used (0 in both cases). Rather than passing in a function pointer as MenuButton did, we are using callbackID to decide which callback function to use within a state. We can now start using our factory and parsing our states from an XML file. Parsing states from an XML file The file we will be parsing is the following (test.xml in source code downloads): <?xml version="1.0" ?> <STATES> <MENU> <TEXTURES> <texture filename="assets/button.png" ID="playbutton"/> <texture filename="assets/exit.png" ID="exitbutton"/> </TEXTURES> <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton" numFrames="0" callbackID="1"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton" numFrames="0" callbackID="2"/> </OBJECTS> </MENU> <PLAY> </PLAY> <GAMEOVER> </GAMEOVER> </STATES> We are going to create a new class that parses our states for us called StateParser. The StateParser class has no data members, it is to be used once in the onEnter function of a state and then discarded when it goes out of scope. Create a StateParser.h file and add the following code: #include <iostream> #include <vector> #include "tinyxml.h" class GameObject; class StateParser { public: bool parseState(const char* stateFile, std::string stateID, std::vector<GameObject*> *pObjects); private: void parseObjects(TiXmlElement* pStateRoot, std::vector<GameObject*> *pObjects); void parseTextures(TiXmlElement* pStateRoot, std::vector<std::string> *pTextureIDs); }; We have three functions here, one public and two private. The parseState function takes the filename of an XML file as a parameter, along with the current stateID value and a pointer to std::vector of GameObject* for that state. The StateParser.cpp file will define this function: bool StateParser::parseState(const char *stateFile, string stateID, vector<GameObject *> *pObjects, std::vector<std::string> *pTextureIDs) { // create the XML document TiXmlDocument xmlDoc; // load the state file if(!xmlDoc.LoadFile(stateFile)) { cerr << xmlDoc.ErrorDesc() << "n"; return false; } // get the root element TiXmlElement* pRoot = xmlDoc.RootElement(); // pre declare the states root node TiXmlElement* pStateRoot = 0; // get this states root node and assign it to pStateRoot for(TiXmlElement* e = pRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == stateID) { pStateRoot = e; } } // pre declare the texture root TiXmlElement* pTextureRoot = 0; // get the root of the texture elements for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("TEXTURES")) { pTextureRoot = e; } } // now parse the textures parseTextures(pTextureRoot, pTextureIDs); // pre declare the object root node TiXmlElement* pObjectRoot = 0; // get the root node and assign it to pObjectRoot for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("OBJECTS")) { pObjectRoot = e; } } // now parse the objects parseObjects(pObjectRoot, pObjects); return true; } There is a lot of code in this function so it is worth covering in some depth. We will note the corresponding part of the XML file, along with the code we use, to obtain it. The first part of the function attempts to load the XML file that is passed into the function: // create the XML document TiXmlDocument xmlDoc; // load the state file if(!xmlDoc.LoadFile(stateFile)) { cerr << xmlDoc.ErrorDesc() << "n"; return false; } It displays an error to let you know what happened if the XML loading fails. Next we must grab the root node of the XML file: // get the root element TiXmlElement* pRoot = xmlDoc.RootElement(); // <STATES> The rest of the nodes in the file are all children of this root node. We must now get the root node of the state we are currently parsing; let's say we are looking for MENU: // declare the states root node TiXmlElement* pStateRoot = 0; // get this states root node and assign it to pStateRoot for(TiXmlElement* e = pRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == stateID) { pStateRoot = e; } } This piece of code goes through each direct child of the root node and checks if its name is the same as stateID. Once it finds the correct node it assigns it to pStateRoot. We now have the root node of the state we want to parse. <MENU> // the states root node Now that we have a pointer to the root node of our state we can start to grab values from it. First we want to load the textures from the file so we look for the <TEXTURE> node using the children of the pStateRoot object we found before: // pre declare the texture root TiXmlElement* pTextureRoot = 0; // get the root of the texture elements for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("TEXTURES")) { pTextureRoot = e; } } Once the <TEXTURE> node is found, we can pass it into the private parseTextures function (which we will cover a little later). parseTextures(pTextureRoot, std::vector<std::string> *pTextureIDs); The function then moves onto searching for the <OBJECT> node and, once found, it passes it into the private parseObjects function. We also pass in the pObjects parameter: // pre declare the object root node TiXmlElement* pObjectRoot = 0; // get the root node and assign it to pObjectRoot for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { if(e->Value() == string("OBJECTS")) { pObjectRoot = e; } } parseObjects(pObjectRoot, pObjects); return true; } At this point our state has been parsed. We can now cover the two private functions, starting with parseTextures. void StateParser::parseTextures(TiXmlElement* pStateRoot, std::vector<std::string> *pTextureIDs) { for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { string filenameAttribute = e->Attribute("filename"); string idAttribute = e->Attribute("ID"); pTextureIDs->push_back(idAttribute); // push into list TheTextureManager::Instance()->load(filenameAttribute, idAttribute, TheGame::Instance()->getRenderer()); } } This function gets the filename and ID attributes from each of the texture values in this part of the XML: <TEXTURES> <texture filename="button.png" ID="playbutton"/> <texture filename="exit.png" ID="exitbutton"/> </TEXTURES> It then adds them to TextureManager. TheTextureManager::Instance()->load(filenameAttribute, idAttribute, TheGame::Instance()->getRenderer()); The parseObjects function is quite a bit more complicated. It creates objects using our GameObjectFactory function and reads from this part of the XML file: <OBJECTS> <object type="MenuButton" x="100" y="100" width="400" height="100" textureID="playbutton" numFrames="0" callbackID="1"/> <object type="MenuButton" x="100" y="300" width="400" height="100" textureID="exitbutton" numFrames="0" callbackID="2"/> </OBJECTS> The parseObjects function is defined like so: void StateParser::parseObjects(TiXmlElement *pStateRoot, std::vector<GameObject *> *pObjects) { for(TiXmlElement* e = pStateRoot->FirstChildElement(); e != NULL; e = e->NextSiblingElement()) { int x, y, width, height, numFrames, callbackID, animSpeed; string textureID; e->Attribute("x", &x); e->Attribute("y", &y); e->Attribute("width",&width); e->Attribute("height", &height); e->Attribute("numFrames", &numFrames); e->Attribute("callbackID", &callbackID); e->Attribute("animSpeed", &animSpeed); textureID = e->Attribute("textureID"); GameObject* pGameObject = TheGameObjectFactory::Instance() ->create(e->Attribute("type")); pGameObject->load(new LoaderParams (x,y,width,height,textureID,numFrames,callbackID, animSpeed)); pObjects->push_back(pGameObject); } } First we get any values we need from the current node. Since XML files are pure text, we cannot simply grab ints or floats from the file. TinyXML has functions with which you can pass in the value you want to be set and the attribute name. For example: e->Attribute("x", &x); This sets the variable x to the value contained within attribute "x". Next comes the creation of a GameObject * class using the factory. GameObject* pGameObject = TheGameObjectFactory::Instance()->create(e->Attribute("type")); We pass in the value from the type attribute and use that to create the correct object from the factory. After this we must use the load function of GameObject to set our desired values using the values loaded from the XML file. pGameObject->load(new LoaderParams(x,y,width,height,textureID,numFrames,callbackID)); And finally we push pGameObject into the pObjects array, which is actually a pointer to the current state's object vector. pObjects->push_back(pGameObject); Loading the menu state from an XML file We now have most of our state loading code in place and can make use of this in the MenuState class. First we must do a little legwork and set up a new way of assigning the callbacks to our MenuButton objects, since this is not something we could pass in from an XML file. The approach we will take is to give any object that wants to make use of a callback an attribute named callbackID in the XML file. Other objects do not need this value and LoaderParams will use the default value of 0. The MenuButton class will make use of this value and pull it from its LoaderParams, like so: void MenuButton::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); m_callbackID = pParams->getCallbackID(); m_currentFrame = MOUSE_OUT; } The MenuButton class will also need two other functions, one to set the callback function and another to return its callback ID: void setCallback(void(*callback)()) { m_callback = callback;} int getCallbackID() { return m_callbackID; } Next we must create a function to set callbacks. Any state that uses objects with callbacks will need an implementation of this function. The most likely states to have callbacks are menu states, so we will rename our MenuState class to MainMenuState and make MenuState an abstract class that extends from GameState. The class will declare a function that sets the callbacks for any items that need it and it will also have a vector of the Callback objects as a member; this will be used within the setCallbacks function for each state. class MenuState : public GameState { protected: typedef void(*Callback)(); virtual void setCallbacks(const std::vector<Callback>& callbacks) = 0; std::vector<Callback> m_callbacks; }; The MainMenuState class (previously MenuState) will now derive from this MenuState class. #include "MenuState.h" #include "GameObject.h" class MainMenuState : public MenuState { public: virtual void update(); virtual void render(); virtual bool onEnter(); virtual bool onExit(); virtual std::string getStateID() const { return s_menuID; } private: virtual void setCallbacks(const std::vector<Callback>& callbacks); // call back functions for menu items static void s_menuToPlay(); static void s_exitFromMenu(); static const std::string s_menuID; std::vector<GameObject*> m_gameObjects; }; Because MainMenuState now derives from MenuState, it must of course declare and define the setCallbacks function. We are now ready to use our state parsing to load the MainMenuState class. Our onEnter function will now look like this: bool MainMenuState::onEnter() { // parse the state StateParser stateParser; stateParser.parseState("test.xml", s_menuID, &m_gameObjects, &m_textureIDList); m_callbacks.push_back(0); //pushback 0 callbackID start from 1 m_callbacks.push_back(s_menuToPlay); m_callbacks.push_back(s_exitFromMenu); // set the callbacks for menu items setCallbacks(m_callbacks); std::cout << "entering MenuStaten"; return true; } We create a state parser and then use it to parse the current state. We push any callbacks into the m_callbacks array inherited from MenuState. Now we need to define the setCallbacks function: void MainMenuState::setCallbacks(const std::vector<Callback>& callbacks) { // go through the game objects for(int i = 0; i < m_gameObjects.size(); i++) { // if they are of type MenuButton then assign a callback based on the id passed in from the file if(dynamic_cast<MenuButton*>(m_gameObjects[i])) { MenuButton* pButton = dynamic_cast<MenuButton*>(m_gameObjects[i]); pButton->setCallback(callbacks[pButton->getCallbackID()]); } } } We use dynamic_cast to check whether the object is a MenuButton type; if it is then we do the actual cast and then use the objects callbackID as the index into the callbacks vector and assign the correct function. While this method of assigning callbacks could be seen as not very extendable and could possibly be better implemented, it does have a redeeming feature; it allows us to keep our callbacks inside the state they will need to be called from. This means that we won't need a huge header file with all of the callbacks in. One last alteration we need is to add a list of texture IDs to each state so that we can clear all of the textures that were loaded for that state. Open up GameState.h and we will add a protected variable. protected: std::vector<std::string> m_textureIDList; We will pass this into the state parser in onEnter and then we can clear any used textures in the onExit function of each state, like so: // clear the texture manager for(int i = 0; i < m_textureIDList.size(); i++) { TheTextureManager::Instance()-> clearFromTextureMap(m_textureIDList[i]); } Before we start running the game we need to register our MenuButton type with the GameObjectFactory. Open up Game.cpp and in the Game::init function we can register the type. TheGameObjectFactory::Instance()->registerType("MenuButton", new MenuButtonCreator()); We can now run the game and see our fully data-driven MainMenuState.
Read more
  • 0
  • 0
  • 2864