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
Explore 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 - Game Development

370 Articles
article-image-improving-inspector-property-and-decorator-drawers
Packt
19 Aug 2015
10 min read
Save for later

Improving the Inspector with Property and Decorator Drawers

Packt
19 Aug 2015
10 min read
In this article by Angelo Tadres, author of the book Extending Unity with Editor Scripting, we will explore a way to create a custom GUI for our properties using Property Drawers. If you've worked on a Unity project for a long time, you know that the bigger your scripts get, the more unwieldy they become. All your public variables take up space in the Inspector Window, and as they accumulate, they begin to convert into one giant and scary monster. Sometimes, organization is the clue. So, in this article, you will learn how to improve your inspectors using Property and Decorator Drawers. (For more resources related to this topic, see here.) A Property Drawer is an attribute that allows you to control how the GUI of a Serializable class or property is displayed in the inspector window. An Attribute is a C# way of defining declarative tags, which you can place on certain entities in your source code to specify additional information. The information that attributes contain is retrieved at runtime through reflection. This approach significantly reduces the amount of work you have to do for the GUI customization because you don't need to write an entire Custom Inspector; instead, you can just apply appropriate attributes to variables in your scripts to tell the editor how you want those properties to be drawn. Unity has several Property Drawers implemented by default. Let's take a look at one of them called Range: using UnityEngine; public class RangeDrawerDemo : MonoBehaviour { [Range (0, 100)] public int IntValue = 50; } If you attach this script to a game object and then select it, you will see the following: Using the Range attribute, we rendered a slider that moves between 0 and 100 instead of the common int field. This is valuable in terms of validating the input for this field. Avoid possible mistakes such as using a negative value to define the radius of a sphere collider, and so on. Let's take a look to the rest of the built-in Property Drawers. Built-in Property Drawers The Unity documentation has information about the built-in property drawers, but there is no such place where you can check all the available ones listed. In this section, we will resolve this. Range The Range attribute restricts a float or int variable in a script to a specific range. When this attribute is used, the float or int will be shown as a slider in the inspector instead of the default number field: public RangeAttribute(float min, float max); public RangeAttribute(float min, float max); [Range (0, 1)] public float FloatRange = 0.5f; [Range (0, 100)] public int IntRange = 50; You will get the following output: Multiline The Multiline attribute is used to show a string value in a multiline text area. You can decide the number of lines of text to make room for. The default is 3 and the text doesn't wrap. The following is an example of the Multiline attribute: public MultilineAttribute(); public MultilineAttribute(int lines); [Multiline (2)] public string StringMultiline = "This text is using a multiline property drawer"; You will get the following output: TextArea The TextArea attribute allows a string to be edited with a height-flexible and scrollable text area. You can specify the minimum and maximum values; a scrollbar will appear if the text is bigger than the area available. Its behavior is better compared to Multiline. The following is an example of the TextArea attribute: public TextAreaAttribute(); public TextAreaAttribute(int minLines, int maxLines); [TextArea (2, 4)] public string StringTextArea = "This text is using a textarea property drawer"; You will get the following output: ContextMenu The ContextMenu attribute adds the method to the context menu of the component. When the user selects the context menu, the method will be executed. The method has to be nonstatic. In the following example, we call the method DoSomething, printing a log in the console: public ContextMenu(string name); [ContextMenu ("Do Something")] public void DoSomething() { Debug.Log ("DoSomething called..."); } You will get the following output: ContextMenuItem The ContextMenuItem attribute is used to add a context menu to a field that calls a named method. In the following example, we call a method to reset the value of the IntReset variable to 0: public ContextMenuItemAttribute(string name, string function); [ContextMenuItem("Reset this value", "Reset")] public int IntReset = 100; public void Reset() { IntReset = 0; } You will get the following output: Built-in Decorator Drawers There is another kind of drawer called Decorator Drawer. These drawers are similar in composition to the Property Drawers, but the main difference is that Decorator Drawers are designed to draw decoration in the inspector and are unassociated with a specific field. This means, while you can only declare one Property Drawer per variable, you can stack multiple decorator drawers in the same field. Let's take a look in the following built-in Decorator Drawers. Header This is the attribute that adds a header to some fields in the inspector: public HeaderAttribute(string header); [Header("This is a group of variables")] public int VarA = 10; public int VarB = 20; You will get the following output: Space The space attribute adds some spacing in the inspector: public SpaceAttribute(float height); public int VarC = 10; [Space(40)] public int VarD = 20; You will get the following output: Tooltip This Tooltip attribute specifies a tooltip for a field: public TooltipAttribute(string tooltip); [Tooltip("This is a tooltip")] public int VarE = 30; You will get the following output: Creating you own Property Drawers If you have a serializable parameter or structure that repeats constantly in your video game and you would like to improve how this renders in the Inspector, you can try to write your own Property Drawer. We will create a property drawer for an integer meant to be a variable to save time in seconds. This Property Drawer will draw a normal int field but also a label with the number of seconds converted to the m:s or h:m:s time format. To implement a Property Drawer, you must create two scripts: The attribute: This declares the attribute and makes it usable in your MonoBehaviour scripts. This will be part of your video game scripts. The drawer: This is responsible for rendering the custom GUI and handling the input of the user. This is placed inside a folder called Editor. The Editor folder is one of the several special folders Unity has. All scripts inside this folder will be treated as editor scripts rather than runtime scripts. For the first script, create one file inside your Unity project called TimeAttribute.cs and then add the following code: using UnityEngine; public class TimeAttribute : PropertyAttribute { public readonly bool DisplayHours; public TimeAttribute (bool displayHours = false) { DisplayHours = displayHours; } } Here we defined the name of the attribute and its parameters. You must create your attribute class extending from the PropertyAttribute class. The name of the class contains the suffix "attribute"; however, when you want to use the attribute over a certain property, the suffix is not needed. In this case, we will use Time and not TimeAttribute to use the property drawer. The TimeAttribute has an optional parameter called DisplayHours. The idea is to display a label under the int field with the time in m:s format by default; if the DisplayHours parameter is true, this will be displayed in h:m:s format. Now is the moment to implement the drawer. To do this, let's create a new script called TimeDrawer.cs inside an Editor folder: using UnityEngine; using UnityEditor; [CustomPropertyDrawer (typeof(TimeAttribute))] public class TimeDrawer : PropertyDrawer { public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { return EditorGUI.GetPropertyHeight (property) * 2; } public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { if (property.propertyType == SerializedPropertyType.Integer) { property.intValue = EditorGUI.IntField (new Rect (position.x, position.y, position.width, position.height / 2), label, Mathf.Abs(property.intValue)); EditorGUI.LabelField (new Rect (position.x, position.y + position.height / 2, position.width, position.height / 2), " ", TimeFormat (property.intValue)); } else { EditorGUI.LabelField (position, label.text, "Use Time with an int."); } } private string TimeFormat (int seconds) { TimeAttribute time = attribute as TimeAttribute; if (time.DisplayHours) { return string.Format ("{0}:{1}:{2} (h:m:s)", seconds / (60 * 60), ((seconds % (60 * 60)) / 60).ToString ().PadLeft(2,'0'), (seconds % 60).ToString ().PadLeft(2,'0')); } else { return string.Format ("{0}:{1} (m:s)", (seconds / 60).ToString (), (seconds % 60).ToString ().PadLeft(2,'0')); } } } Property Drawers don't support layouts to create a GUI; for this reason, the class you must use here is EditorGUI instead of EditorGUILayout. Using this class requires a little extra effort; you need to define the Rect function that will contain the GUI element each time you want to use it. The CustomPropertyDrawer attribute is part of the UnityEditor namespace, and this is what Unity uses to bind a drawer with a Property attribute. In this case, we passed the TimeAttribute. Your must extend the TimeAttribute from the PropertyDrawer class, and in this way, you will have access to the core methods to create Property Drawers: GetPropertyHeight: This method is responsible for handling the height of the drawer. You need to overwrite this method in order to use it. In our case, we force the size of the drawer to be double. OnGUI: This is where you place all the code related to rendering the GUI. You can create Decorator Drawers too. You just need to follow the same steps we performed to create a Property Drawer, but instead of extending your Drawer from PropertyDrawer, you need to extend from DecoratorDrawer. Also, you will have access to the variable attribute. This has a reference to the attribute class we created, and with this we can access to their variables. To test our code, create a new script called TimeDrawerDemo.cs and add the following code: using UnityEngine; using UnityEditor; [CustomPropertyDrawer (typeof(TimeAttribute))] public class TimeDrawer : PropertyDrawer { public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { return EditorGUI.GetPropertyHeight (property) * 2; } public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { if (property.propertyType == SerializedPropertyType.Integer) { property.intValue = EditorGUI.IntField (new Rect (position.x, position.y, position.width, position.height / 2), label, Mathf.Abs(property.intValue)); EditorGUI.LabelField (new Rect (position.x, position.y + position.height / 2, position.width, position.height / 2), " ", TimeFormat (property.intValue)); } else { EditorGUI.LabelField (position, label.text, "Use Time with an int."); } } private string TimeFormat (int seconds) { TimeAttribute time = attribute as TimeAttribute; if (time.DisplayHours) { return string.Format ("{0}:{1}:{2} (h:m:s)", seconds / (60 * 60), ((seconds % (60 * 60)) / 60).ToString ().PadLeft(2,'0'), (seconds % 60).ToString ().PadLeft(2,'0')); } else { return string.Format ("{0}:{1} (m:s)", (seconds / 60).ToString (), (seconds % 60).ToString ().PadLeft(2,'0')); } } } After compiling, attach this script to a game object. You will see this on the inspector: The time property uses the time attribute. We can check the three possible scenarios here: The attribute uses a property that is not an int The attribute has the variable DisplayHours = false The attribute has the variable DisplayHours = true A little change makes it easier to set up this data in our game objects. Summary In this article, we created a custom Property Drawer to be used in properties mean to store time in seconds, and in the process, you learned how these are implemented. We also explored the available built-in Property Drawers and Decorator Drawers in Unity. Applying this knowledge to your projects will enable you to add validation to sensible data in your properties and make your scripts more developer friendly. This will also allow you to have a professional look. Resources for Article: Further resources on this subject: Adding a Graphical User Interface [article] Animation features in Unity 5 [article] Event-driven Programming [article]
Read more
  • 0
  • 0
  • 9681

article-image-advanced-effects-using-blender-particle-system-sequel
Packt
18 Aug 2010
6 min read
Save for later

Advanced Effects using Blender Particle System: A Sequel

Packt
18 Aug 2010
6 min read
(For more resources on Blender, see here.) Multiple Particle Systems Sometimes, singular particle systems are not enough to create the effects we want. This is when multiple particles come in, enabling us to create things like welding sparks, fireworks, rain splash, object shatters, etc. Thankfully, we have Reactor Particles which does just that. In most cases, reactor particles are born when other particle systems' events happen. You'll see more of that later in this part. I guess the best way to explain these things is through valid examples. Let's begin by simulating the fireworks effect we see during New Year or when there are festivities. First thing we need to do is to setup our scene. We'll be creating a very simple setup for now and it's up to you to take it further when needed. Fire up Blender 2.49 (sadly, Blender 2.5 doesn't support reactor particles yet) and orient the camera such that it is facing towards the positive y axis, as shown in the screenshot below: (Move the mouse over the image to enlarge it.) (Positioning the Camera) Next, delete the default Cube and add a Plane object then position it just below the camera's view. You can scale it according to your liking but we can also adjust it later on if need be. Let's name this plane “emitter”. (Plane Added in the Scene) With the “emitter” object still selected, go to Object (F7) then click on Particle buttons and add a new particle system. (Adding a New Particle System) This particle system will be the single burst that will eventually give birth to hundreds of particles later on. Let's name it “single” for easier reference later on. Keep the amount at a minimum, probably something like lower than 20, so it won't get too chaotic later on once the bursting of particles begin. Also change the Life of the particle system such that you see them die on your camera's view, otherwise we won't be able to see the bursting effect happening at all. And lastly, increase the Normal value under Physics, this will tell how the particles are shot and how fast. You might also have to edit the Life later on once you have decided on the Normal settings. (“single” Particle Settings) (Single Frame from the Particle System Animation) Now if we play back our animation, it should look something like this: (fireworks_1.avi) Next is the main particles for this system – the burst. With the plane emitter still selected, head over to the same window where the Particle System was created. On the upper right hand corner of the Particle System tab, you'll notice a portion there with “1 Part 1” on it with arrows left and right. These are indications of how many sub-particle systems there are in our main system. Our reaction particles would not act if it was a different system, so this is one crucial part. Where you see the 1 Part 1 area, click on the right arrow to register another sub-particle system, making it a “2 Part 2”. Then click on Add New again, just like how we did in the previous part. (Adding a Sub-Particle System) The important options that we have to take note here are the particle system type, emit from values, and target. With the Physics option, I'll leave it to you to tweak around. As you might have already guessed, we must first and foremost, choose Reactor as the particle system type, otherwise it would be useless doing so. Next is to change from which will the particles be emitted from (emit from), choose Particle since we wanted it to be emitted from the first particle system that we created. Then in the Target portion, you can choose from which system will it be emitted from, the default which is Psys:1, you can change this setting accordingly depending on which order you want it to be emitted. (Reactor Particle System Settings) Then here's the hardware rendered version of the system: (fireworks_2.avi) And that's about it! This is also applicable to things like welding sparks or rain splashes. If you want your particle reactor to happen on object collision, instead of setting the particle reactor to begin on Death, change it to Collision then set your objects as collision objects. You can check Part 1 of this article to see more on the material settings. And just a bonus, I've added some tiny details to particle system. You can check out the video below and also download the .blend file. (fireworks_3.avi) Boids Particle boids, are by far, one of the most underestimated and overlooked strengths of Blender's particle system. Not only is it very daunting at first, but the complexity it can offer you is mind blowing. Simply put, Particle Boid or Boids Particle System is a type of Particle Physics whereby it follows rules and protocols that a user has set. These rules may range from prey and predator relationships to advanced crowd simulation. They can be related to artificial intelligence in ways that each particle point can behave differently and act on its own regardless of the other particle points present in space. I don't, however, claim to have really surmounted boids particle systems, even before in earlier versions of Blender. It is so exciting and exhilirating yet frustrating at times. To fully comprehend it, you need lots and lots of patience, probably more time to tweak around, and a note near you to have the settings listed down. For the sake of this article, I'll keep this introduction to boids particle system as simple and as quick as possible, giving you more freedom to experiment and just give you a basic understanding on what this particle system is all about. Let's get going, shall we? Fire up Blender 2.53 and delete the default Cube in your scene. (Deleting the Default Cube) Next, we need to add the basic elements for our basic boids particle system. It includes 1) an emitter, 2) the visualization object, and 3) the goal object. With the cube deleted in our 3d viewport, add a plane object (or anything you wish) and leave the default levels of division as it is, this would act as our particle emitter. Name it “emitter”. (Plane Object Added) Next, add a simple UV Sphere above the plane, this would then act as our goal object. Smooth the shading and scale to something like 0.200. Name this uv sphere as “goal”.
Read more
  • 0
  • 0
  • 9666

article-image-bringing-your-game-life-ai-and-animations
Packt
26 Aug 2013
16 min read
Save for later

Bringing Your Game to Life with AI and Animations

Packt
26 Aug 2013
16 min read
(For more resources related to this topic, see here.) After going through these principles, we will be completing the tasks to enhance the maze game and the gameplay. We will apply animations to characters and trigger these in particular situations. We will improve the gameplay by allowing NPCs to follow the player where he/she is nearby (behavior based on distance), and attack the user when he/she is within reach. All material required to complete this article is available for free download on the companion website: http://patrickfelicia.wordpress.com/publications/books/unity-outbreak/. The pack for this article includes some great models and animations that were provided by the company Mixamo to enhance the quality of our final game. The characters were animated using Mixamo's easy online sequences and animation building tools. For more information on Mixamo and its easy-to-use 3D character rigging and animation tools, you can visit http://www.mixamo.com. Before we start creating our level, we will need to rename our scene and download the necessary assets from the companion website as follows: Duplicate the scene we have by saving the current scene (File Save | Scene), and then saving this scene as chapter5 (File | Save Scene As…). Open the link for the companion website: http://patrickfelicia.wordpress.com/publications/books/unity-outbreak/. Click on the link for the chapter5 pack to download this file. In Unity3D, create a new folder, chapter5, inside the Assets folder and select this folder (that is, chapter5). From Unity, select Assets | Import Package | Custom Package, and import the package you have just downloaded. This should create a folder, chapter5_pack, within the folder labeled chapter5. Importing and configuring the 3D character We will start by inserting and configuring the zombie character in the scene as shown in the following steps: Open the Unity Assets Store window (Window | Asset Store). In the Search field located in the top-right corner, type the text zombie. Click on the search result labeled Zombie Character Pack, and then click on the button labeled Import. In the new window entitled Importing package, uncheck the last box for the low-resolution zombie character and then click on Import. This will import the high-resolution zombie character inside our project and create a corresponding folder labeled ZombieCharacterPack inside the Assets folder. Locate the prefab zombie_hires by navigating to Assets | ZombieCharacterPack. Select this prefab and open the Inspector window, if it is not open yet. Click on the Rig tag, set the animation type to humanoid, and leave the other options as default. Click on the Apply button and then click on the Configure button; a pop-up window will appear: click on Save. In the new window, select: Mapping | Automap, as shown in the following screenshot: After this step, if we check the Hierarchy window, we should see a hierarchy of bones for this character. Select Pose | Enforce T-Pose as shown in the following screenshot: Click on the Muscles tab and then click on Apply in the new pop-up window. The Muscles tab makes it possible to apply constraints on our character. Check whether the mapping is correct by moving some of the sliders and ensuring that the character is represented properly. After this check, click on Done to go back to the previous window. Animating the character for the game Once we have applied these settings to the character, we will now use it for our scene. Drag-and-drop the prefab labeled zombie_hires by navigating to Assets | ZombieCharacterPack to the scene, change its position to (x=0, y =0, z=0), and add a collider to the character. Select: Component | Physics | Capsule Collider. Set the center position of this collider to (x=0, y=0.7, z=0), the radius to 0.5, the height to 2, and leave the other options as default, as illustrated in the following screenshot: Select: Assets | chapter5 | chapter5_pack; you will see that it includes several animations, including Zombie@idle, Zombie@walkForward, Zombie@attack, Zombie@hit, and Zombie@dead. We will now create the necessary animation for our character. Click once on the object zombie_hires in the Hierarchy window. We should see that it includes a component called Animator. This component is related to the animation of the character through Mecanim. You will also notice an empty slot for an Animator Controller. This controller will be created so that we can animate the character and control its different states, using a state machine. Let's create an Animator Controller that will be used for this character: From the project folder, select the chapter5 folder, then select Create | Animator Controller in the Project window. This should create a new Animator Controller labeled New Animator Controller in the folder chapter5. Rename this controller zombieController. Select the object labeled zombie_hires in the Hierarchy window. Locate the Animator Controller that we have just created by navigating to Assets | chapter5 (zombieController), drag-and-drop it to the empty slot to the right of the attribute controller in the Animator component of the zombie character, and check that the options Apply Root Motion and Animate Physics are selected. Our character is now ready to receive the animations. Open the Animator window (Window | Animator). This window is employed to display and manage the different states of our character. Since no animation is linked to the character, the default state is Any State. Select the object labeled zombie_hires in the Hierarchy window. Rearrange the windows in our project so that we can see both the state machine window and the character in the Scene view: we can drag the tab labeled Scene for the Scene view at the bottom of the Animator window, so that both windows can be seen simultaneously. We will now apply our first animation to the character: Locate the prefab Zombie@idle by navigating to Assets | chapter5 | chapter5_pack. Click once on this prefab, and in the Inspector window, click the Rig tab. In the new window, select the option Humanoid for the attribute Animation Type and click on Apply. Click on the Animations tab, and then click on the label idle, this will provide information on the idle clip. Scroll down the window, check the box for the attribute Loop Pose, and click on Apply to apply this change (you will need to scroll down to locate this button). In the Project view, click on the arrow located to the left (or right, depending on how much we have zoomed-in within this window) of the prefab Zombie@idle; it will reveal items included in this prefab, including an animation called idle, symbolized by a gray box with a white triangle. Make sure that the Animator window is active and drag this animation (idle) to the Animator window. This will create an idle state, and this state will be colored in orange, which means that it is the default state for our character. Rename this state Idle (upper case I) using the Inspector. Play the scene and check that the character is in an idle state. Repeat steps 1-9 for the prefab Zombie@walkForward and create a state called WalkForward. To test the second animation, we can temporarily set the state walkForward to be the default state by right-clicking on the walkForward state in the Animator window, and selecting Set As Default. Once we have tested this animation, set the state Idle as the default state. While the zombie is animated properly, you may notice that the camera on the First Person Controller might be too high. You will address this by changing the height of the camera so that it is at eye-level. In the Hierarchy view, select the object Main Camera that is located with the object First Person Controller and change its position to (x=0, y=0.5, z=0). We now have two animations. At present, the character is in the Idle state, and we need to de fine triggers or conditions for the character to start or stop walking toward the player. In this game, we will have enemies with different degrees of intelligence. This first type will follow the user when it sees the user, is close to the user, or is being attacked by the user. The Animator window will help to create animations and to apply transition conditions and blending between them so that transitions between each animation are smoother. To move around this window, we can hold the Alt key while simultaneously dragging-and-dropping the mouse. We can also select states by clicking on them or de fining a selection area (drag-and-drop the mouse to define the area). If needed, it is also possible to maximize this window using the icon located at its top-right corner. Creating parameters and transitions First, let's create transitions. Open the Animator window, right-click on the state labeled Idle, and select the option Make Transition from the contextual menu. This will create an arrow that symbolizes the transition from this state to another state. While this arrow is visible, click on the state labeled WalkForward. This will create a transition between the states WalkForward and Idle as illustrated in the following screenshot: Repeat the last step to create a transition between the state WalkForward and Idle: right-click on the state labeled WalkForward, select the option Make Transition from the contextual menu, and click on the state labeled Idle. Now that these transitions have been defined, we will need to specify how the animations will change from one state to the other. This will be achieved using parameters. In the Animator window, click on the + button located at the bottom-right corner of the window, as indicated in the following screenshot: Doing so will display a contextual menu, from which we can choose the type of the parameter. Select the option Bool to create a Boolean parameter. A new window should now appear with a default name for our new parameter as illustrated in the following screenshot: change the name of the parameter to walking. Now that the parameter has been defined, we can start defining transitions based on this parameter. Let's start with the first transition from the Idle state to the Walkforward state: Select the transition from the Idle state to the Walkforward state (that is, click on the corresponding transition in the Animator window). If we look at the Inspector window, we can see that this object has several components, including Transitions and Conditions. Let's focus on the Conditions component for the time being. We can see that the condition for the transition is based on a parameter called ExitTime and that the value is 0.98. This means that the transition will occur when the current animation has reached 98 percent completion. However, we would like to use the parameter labeled walking instead. Click on the parameter ExitTime, this should display other parameters that we can use for this transition. Select walking from the contextual menu and make sure that the condition is set to true as shown in the following screenshot: The process will be similar for the other transition (that is, from WalkForward to Idle), except that the condition for the transition for the parameter walking will be false: select the second transition (WalkForward to Idle) and set the transition condition of walking to false. To check that the transitions are working, we can do the following: Play the scene and look at the Scene view (not the Game view). In the Animator window, change the parameter walking to true by checking the corresponding box, as highlighted in the following screenshot: Check that the zombie character starts walking; click on this box again to set the variable walking to false, check that the zombie stops walking, and stop the Play mode (Ctrl + P). Adding basic AI to enemies We have managed to set transitions for the animations and the state of the zombie from Idle to walking. To add some challenge to the game, we will equip this enemy with some AI and create a script that changes the state of the enemy from Idle to WalkForward whenever it sees the player. First, let's allocate the predefined-tag player to First Person Controller: select First Person Controller from the Hierarchy window, and in the Inspector window, click on the drop-down menu to the right of the label Tag and select the tag Player. Then, we can start creating a script that will set the direction of the zombie toward the player. Create a folder labeled Scripts inside the folder Assets | chapter5, create a new script, rename it controlZombie, and add the following code to the start of the script: public var walking:boolean = false;public var anim:Animator;public var currentBaseState:AnimatorStateInfo; public var walkForwardState:int = Animator.StringToHash("Base Layer.WalkForward");public var idleState:int = Animator.StringToHash("Base Layer.Idle");private var playerTransform:Transform;private var hit:RaycastHit; In statement 1 of the previous code, a Boolean value is created. It is linked to the parameter used for the animation in the Animator window. In statement 2 of the previous code, we define an Animator object that will be used to manage the animator component of the zombie character. In statement 3 of the previous code, we create an AnimatorStateInfo variable that will be used to determine the current state of the animation (for example, Idle or WalkForward). In statement 4 of the previous code, we create a variable, walkForwardState , that will represent the state WalkForward previously de fined in the Animator window. We use the method Animator.StringToHash to convert this state initially from a string to an integer that can then be used to monitor the active state. In statement 5 of the previous code, similar to the previous comments, a variable is created for the state Idle. In statement 6 of the previous code, we create a variable that will be used to detect the position of the player. In statement 7 of the previous code, we create a ray that will be employed later on to detect the player. Next, let's add the following function to the script: function Start (){ anim = GetComponent(Animator); playerTransform = GameObject.FindWithTag("Player").transform;} In line 3 of the previous code, we initialize the variable anim with the Animator component linked to this GameObject. We can then add the following lines of code: function Update (){ currentBaseState = anim.GetCurrentAnimatorStateInfo(0); gameObject.transform.LookAt(playerTransform);} In line 3 of the previous code, we determine the current state for our animation. In line 4 of the previous code, the transform component of the current game object is oriented so that it is looking at the First Person Controller. Therefore, when the zombie is walking, it will follow the player. Save this script, and drag-and-drop it to the character labeled zombie_hires in the Hierarchy window. As we have seen previously, we will need to manage several states through our script, including the states Idle and WalkForward. Let's add the following code in the Update function: switch (currentBaseState.nameHash){case idleState:break;case walkForwardState:break;default:break;} In line 1 of the previous code, depending on the current state, we will switch to a different set of instructions All code related to the state Idle will be included within lines 3-4 of the previous code All code related to the state WalkForward will be included within lines 6-7 If we play the scene, we may notice that the zombie rotates around the x and z axes when near the player; its y position also changes over time. To correct this issue, let's add the following code at the end of the function Update: transform.position.y = -0.5;transform.rotation.x = 0.0;transform.rotation.z = 0.0; We now need to detect whether the zombie can see the player, or detect its presence within a radius of two meters(that is, the zombie would hear the player if he/she is within two meters). This can be achieved using two techniques: by calculating the distance between the zombie and the player, and by casting a ray from the zombie and detecting whether the player is in front of the zombie. If this is the case, the zombie will start walking toward the player. We need to calculate the distance between the player and the zombie by adding the following code to the script controlZombie, at the start of the function Update, before the switch statement: var distance:float = Vector3.Distance(transform.position, playerTransform.position); In the previous code, we create a variable labeled distance and initialize it with the distance between the player and the zombie. This is achieved using the built-in function Vector3.Distance. Now that the distance is calculated (and updated in every frame), we can implement the code that will serve to detect whether the player is near or in front of the zombie. Open the script entitled controlZombie, and add the following lines to the function Update within the block of instructions for the Idle state, so that it looks as follows: case idleState: if ((Physics.Raycast (Vector3(transform.position.x,transform.position.y+.5,transform.po sition.z), transform.forward, hit,40) && hit.collider.gameObject.tag == "Player") || distance <2.0f) { anim.SetBool("walking",true); }break; In the previous lines of code, a ray or ray cast is created. It is casted forward from the zombie, 0.5 meters above the ground and over 40 meters. Thanks to the variable hit, we read the tag of the object that is colliding with our ray and check whether this object is the player. If this is the case, the parameter walking is set to true. Effectively, this should trigger a transition to the state walking, as we have defined previously, so that the zombie starts walking toward the player. Initially, our code was written so that the zombie rotated around to face the player, even in the Idle state (using the built-in function LookAt). However, we need to modify this feature so that the zombie only turns around to face the player while it is following the player, otherwise, the player will always be in sight and the zombie will always see him/her, even in the Idle state. We can achieve this by deleting the code highlighted in the following code snippet (from the start of the function Update), and adding it to the code for the state WalkForward: case walkForwardState: transform.LookAt(playerTransform); break; In the previous lines, we checked whether the zombie is walking forward, and if this is the case, the zombie will rotate in order to look at and follow the player. Test our code by playing the scene and either moving within two meters of the zombie or in front of the zombie.
Read more
  • 0
  • 0
  • 9613

article-image-modeling-furniture-blender
Packt
22 Oct 2009
10 min read
Save for later

Modeling Furniture in Blender

Packt
22 Oct 2009
10 min read
Create Models or Use a Library? There are two possibilities when working with furniture. We can create new furniture, or use pre-made models from a library. The question is: when must we use each type? Some people say that using a pre-made model is not very professional thing but what they forget to say is that most projects have a tight deadline, and we need a quick modeling process to be ready on time. So, what's most important for professionals? Getting things done, or telling the client that all the models were created just for his project? Of course, the deadline is the most important, and your clients normally won't mind if you use pre-made models. Probably they won't even notice. So don't be ashamed to use pre-made models they won't make your projects any less professional. It's even recommended to use these models to speed-up the process, and allow you to spend more time on lighting or texturing. Is there any situation that demands the creation of a furniture model from scratch? Well, there are some. First, if you can't find the model in any library that you know, then it's going to be necessary to create it from scratch. If you are working with an architect who designs the spaces and furniture as well, you will probably have to model the furniture too, since it won't be available at any public library. Any project that deals with customized furniture will require that we work on the modeling for the furniture. Create your own libraryA good practice for anyone doing architectural visualization is to collect a lot of 3D models from public libraries for use in future projects. Keep these models for later, but don't forget to check if the author has released the models with no restrictions for commercial use. Otherwise, you must get their permission to use them. If you want to create your library, with no restrictions, why not create your own models? This could be a good exercise: take a few examples, and start creating some furniture. With time, you will have a good number of models. How to Get Started? In most cases, we have to get used to all that furniture modeling. We will have to start from scratch, with no blueprints available. The only references that we will have would be the photos, either provided by our clients, or provided from some web resources. If you have the time, visit a real store, and take some pictures and measures on your own. Sometimes, these stores will give you fliers and brochures, especially if you work with architecture. With time, you will get a lot of good reference material, and some of them come with measurements. But, if you don't know where to get started, let me point out some great web resources: http://www.e-interiors.net http://resources.blogoscopia.com http://blender-archi.tuxfamily.org/Models http://www.katorlegaz.com/ http://sketchup.google.com/3dwarehouse The first link has a lot of reference images classified by furniture type and designer. And sometimes, they even provide free 3D models. Most models there are saved in DXF, or 3DS file formats. Appending Models Before we start to model, let's see how we can import a model form an external library into Blender. The process is very simple, and what we have to do is to use the File menu, and access the Append or Link option. There is a shortcut for that too - just press SHIFT+F1 to call the same function. With this option, we have to select file that is already in the Blender file format. This option won't import files in other formats. When we select a file, a list of elements available in that particular file will be displayed, for us to select what we want to import. In most cases, the models will be stored under Object. When we click the Object option, all of the objects available in the file will be listed. If you know the name of the object that you want to import, just select the name, and click Load Library. The object will be loaded into our scene. Here, we have two options to handle this object: Append or Link: Append: If we choose this option, the object will be merged into ourcurrent scene. Link: With this option, an external link to the object file will be created. Any modifications to the original file will be reflected in our current scene. What is the best method to use? It will depend on whether we are willing to track all modifications applied to our furniture models. Using the Link method is a great way of keeping the furniture updated, because every modification at the original file is reflected immediately in the scene in which this model is placed. However, we will have to take the original file with the scene file every time we need to put our scene on another computer. They always have to go together. But if you choose to use the Append option, things will be a bit simpler, because the object will be incorporated into the scene file. We won't have to be worried about moving the furniture file along with the scene. Always use the Append option when you want to use furniture, or any other model, saved in another Blender file. To use a furniture model saved in another file, with a type other than “.blend”, we have to use the Import option. Importing Models To import a model, the process is very simple. We must use the File menu and select, Import. Then we have to select the proper file type from the list. The best file type, and the most common for furniture blocks, is the 3Ds file format, which belongs to the old 3D Studio application. There are some other good formats that work well with Blender, such as OBJ and LWO. The 3Ds file format can store lights, and it works well with Blender. The only thing we have to take extra care about is that most models imported come with triangular faces, which are a bit harder to edit. But, if you don't need to make any modifications to the model, this won't be a problem. Append or Import?Just to make things clearer, if you download a furniture model from a web site, and it's saved in the Blender native file format (.blend), you should append the model. If you download or get a furniture model on any file format other than “.blend”, you will have to import it. Since most models aren't saved in the Blender native file format, we can safely say that almost all furniture models that you find will require an import action to be placed in your scenes. Modeling a Chair Let's start with something simple, such as a chair. Even for a simple model, it will help us deal with smaller dimensions and details. Here is an image of the model: What's the main objective of this modeling? We have to create this chair, with the minimum use of faces and vertices. A good amount of detail can be left for textures, and it's always a good choice to use a lower number of vertices and faces in a model. If you consider one model, it won't matter much. But with a large number of chairs, such as in a theater room, it can make a difference in render time. Let's get started with a simple cube. Select this cube, and change the work mode to Edit. Select all vertices and press the W key. This will open the Specials menu. Choose subdivide, just once, from this menu. This will create new vertices and edges. Once these new vertices have been created, as shown in the image to the left, below, press the A key to remove all of the objects from the selection. Now, select the vertices to the right, using the B key. Remember to change the view mode to Wireframe before using the B key, otherwise, we won't be able to select the vertices behind the visible faces. When these vertices are selected, press the X key and choose Vertices to erase only the selected vertices. Using the CTRL+R key, add a new edge loop to the model, as shown in the following image: The next step is to change the scale of our model. Rotate the view to see the model in perspective view. Select all objects and press the S key, immediately after pressing the Z key. This will make the scale work only in the Z axis. Now, select the vertices identified in the following image and erase them using the X key. Change the selection mode to Edges, and select the edges identified in the following image. With the edges selected, press the E key to extrude them. With the new faces created, we can now add some detail to the model. Select only the top edge of the previously created faces. Move this edge down just a bit. This will add a small declivity to the seat. Now, we can move on to the next extrude, which must be from the selected edges identified in the following image. I'm not using any kind of measure for this example, but if you like to work only with real measurements, remember to hold the CTRL key every time a new extrude or edge is moved. This way, all transformations will use the grid lines. For this model, I'm not using vertex snap. With the new faces created, select just the two edges identified in the following image. Extrude these edges until they reach the other side of the base model. Hold the CTRL key, while you extrude them, to help with the precision. If you already want to remove duplicated vertices, select all objects, and press the W key. Choose Remove doubles to erase any duplicated vertices. Select the edges identified in the image to keep adding more parts to the chair. Extrude the edges three times until you have the same structure showed here. Now, we have to close the top with a face. To do that, we must select all four vertices on the top. When the vertices are selected, press the F key to create a new face. The next step is to select the small side edges to create some detail. Select just one edge, beginning from bottom to top, and move it just a bit. Repeat this operation with the other edges until we get the edges positioned as in the following image. The basic shape of our chair has now been created. Now, we can make some adjustments for improving the overall proportions. Select all edges or vertices on the left side, and move them a bit to the left. This will make the model wider. Did you notice that we have modeled only half a chair? Now we can make the other half, using the Mirror modifier. Add the modifier, and choose the right axis to make a perfect copy. If the center point for the model has been moved, you might need to edit the model to create a perfect mirrored match. Don't worry if you have moved the model by accident - this can happen sometimes. Along with the Mirror modifier, add a Subsurf modifier, too. With the Subsurf modifier, we realize that this model needs a new edge loop on the left side. Just press CTRL+R, and add a new loop, as in the following image.
Read more
  • 0
  • 0
  • 9612

article-image-panda3d-game-development-scene-effects-and-shaders
Packt
20 Apr 2011
8 min read
Save for later

Panda3D game development: scene effects and shaders

Packt
20 Apr 2011
8 min read
While brilliant gameplay is the key to a fun and successful game, it is essential to deliver beautiful visuals to provide a pleasing experience and immerse the player in the game world. The looks of many modern productions are massively dominated by all sorts of visual magic to create the jaw-dropping visual density that is soaked up by players with joy and makes them feel connected to the action and the gameplay they are experiencing. The appearance of your game matters a lot to its reception by players. Therefore it is important to know how to leverage your technology to get the best possible looks out of it. This is why this article will show you how Panda3D allows you to create great looking games using lights, shaders, and particles. Adding lights and shadows in Panda3d Lights and shadows are very important techniques for producing a great presentation. Proper scene lighting sets the mood and also adds depth to an otherwise flat-looking scene, while shadows add more realism, and more importantly, root the shadow-casting objects to the ground, destroying the impression of models floating in mid-air. This recipe will show you how to add lights to your game scenes and make objects cast shadows to boost your visuals. Getting ready You need to create the setup presented in Setting up the game structure before proceeding, as this recipe continues and builds upon this base code. How to do it... This recipe consists of these tasks: Add the following code to Application.py: from direct.showbase.ShowBase import ShowBase from direct.actor.Actor import Actor from panda3d.core import * class Application(ShowBase): def __init__(self): ShowBase.__init__(self) self.panda = Actor("panda", {"walk": "panda-walk"}) self.panda.reparentTo(render) self.panda.loop("walk") cm = CardMaker("plane") cm.setFrame(-10, 10, -10, 10) plane = render.attachNewNode(cm.generate()) plane.setP(270) self.cam.setPos(0, -40, 6) ambLight = AmbientLight("ambient") ambLight.setColor(Vec4(0.2, 0.1, 0.1, 1.0)) ambNode = render.attachNewNode(ambLight) render.setLight(ambNode) dirLight = DirectionalLight("directional") dirLight.setColor(Vec4(0.1, 0.4, 0.1, 1.0)) dirNode = render.attachNewNode(dirLight) dirNode.setHpr(60, 0, 90) render.setLight(dirNode) pntLight = PointLight("point") pntLight.setColor(Vec4(0.8, 0.8, 0.8, 1.0)) pntNode = render.attachNewNode(pntLight) pntNode.setPos(0, 0, 15) self.panda.setLight(pntNode) sptLight = Spotlight("spot") sptLens = PerspectiveLens() sptLight.setLens(sptLens) sptLight.setColor(Vec4(1.0, 0.0, 0.0, 1.0)) sptLight.setShadowCaster(True) sptNode = render.attachNewNode(sptLight) sptNode.setPos(-10, -10, 20) sptNode.lookAt(self.panda) render.setLight(sptNode) render.setShaderAuto() Start the program with the F6 key. You will see the following scene: How it works... As we can see when starting our program, the panda is lit by multiple lights, casting shadows onto itself and the ground plane. Let's see how we achieved this effect. After setting up the scene containing our panda and a ground plane, one of each possible light type is added to the scene. The general pattern we follow is to create new light instances before adding them to the scene using the attachNewNode() method. Finally, the light is turned on with setLight(), which causes the calling object and all of its children in the scene graph to receive light. We use this to make the point light only affect the panda but not the ground plane. Shadows are very simple to enable and disable by using the setShadowCaster() method, as we can see in the code that initializes the spotlight. The line render.setShaderAuto() enables the shader generator, which causes the lighting to be calculated pixel perfect. Additionally, for using shadows, the shader generator needs to be enabled. If this line is removed, lighting will look coarser and no shadows will be visible at all. Watch the amount of lights you are adding to your scene! Every light that contributes to the scene adds additional computation cost, which will hit you if you intend to use hundreds of lights in a scene! Always try to detect the nearest lights in the level to use for lighting and disable the rest to save performance. There's more... In the sample code, we add several types of lights with different properties, which may need some further explanation. Ambient light sets the base tone of a scene. It has no position or direction—the light color is just added to all surface colors in the scene, which avoids unlit parts of the scene to appear completely black. You shouldn't set the ambient color to very high intensities. This will decrease the effect of other lights and make the scene appear flat and washed out. Directional lights do not have a position, as only their orientation counts. This light type is generally used to simulate sunlight—it comes from a general direction and affects all light-receiving objects equally. A point light illuminates the scene from a point of origin from which light spreads towards all directions. You can think of it as a (very abstract) light bulb. Spotlights, just like the headlights of a car or a flashlight, create a cone of light that originates from a given position and points towards a direction. The way the light spreads is determined by a lens, just like the viewing frustum of a camera. Using light ramps The lighting system of Panda3D allows you to pull off some additional tricks to create some dramatic effects with scene lights. In this recipe, you will learn how to use light ramps to modify the lights affect on the models and actors in your game scenes. Getting ready In this recipe we will extend the code created in Adding lights and shadows found in this article. Please review this recipe before proceeding if you haven't done so yet. How to do it... Light ramps can be used like this: Open Application.py and add and modify the existing code as shown: from direct.showbase.ShowBase import ShowBase from direct.actor.Actor import Actor from panda3d.core import * from direct.interval.IntervalGlobal import * class Application(ShowBase): def __init__(self): ShowBase.__init__(self) self.panda = Actor("panda", {"walk": "panda-walk"}) self.panda.reparentTo(render) self.panda.loop("walk") cm = CardMaker("plane") cm.setFrame(-10, 10, -10, 10) plane = render.attachNewNode(cm.generate()) plane.setP(270) self.cam.setPos(0, -40, 6) ambLight = AmbientLight("ambient") ambLight.setColor(Vec4(0.3, 0.2, 0.2, 1.0)) ambNode = render.attachNewNode(ambLight) render.setLight(ambNode) dirLight = DirectionalLight("directional") dirLight.setColor(Vec4(0.3, 0.9, 0.3, 1.0)) dirNode = render.attachNewNode(dirLight) dirNode.setHpr(60, 0, 90) render.setLight(dirNode) pntLight = PointLight("point") pntLight.setColor(Vec4(3.9, 3.9, 3.8, 1.0)) pntNode = render.attachNewNode(pntLight) pntNode.setPos(0, 0, 15) self.panda.setLight(pntNode) sptLight = Spotlight("spot") sptLens = PerspectiveLens() sptLight.setLens(sptLens) sptLight.setColor(Vec4(1.0, 0.4, 0.4, 1.0)) sptLight.setShadowCaster(True) sptNode = render.attachNewNode(sptLight) sptNode.setPos(-10, -10, 20) sptNode.lookAt(self.panda) render.setLight(sptNode) render.setShaderAuto() self.activeRamp = 0 toggle = Func(self.toggleRamp) switcher = Sequence(toggle, Wait(3)) switcher.loop() def toggleRamp(self): if self.activeRamp == 0: render.setAttrib(LightRampAttrib.makeDefault()) elif self.activeRamp == 1: render.setAttrib(LightRampAttrib.makeHdr0()) elif self.activeRamp == 2: render.setAttrib(LightRampAttrib.makeHdr1()) elif self.activeRamp == 3: render.setAttrib(LightRampAttrib.makeHdr2()) elif self.activeRamp == 4: render.setAttrib(LightRampAttrib. makeSingleThreshold(0.1, 0.3)) elif self.activeRamp == 5: render.setAttrib(LightRampAttrib. makeDoubleThreshold(0, 0.1, 0.3, 0.8)) self.activeRamp += 1 if self.activeRamp > 5: self.activeRamp = 0 Press F6 to start the sample and see it switch through the available light ramps as shown in this screenshot: How it works... The original lighting equation that is used by Panda3D to calculate the final screen color of a lit pixel limits color intensities to values within a range from zero to one. By using light ramps we are able to go beyond these limits or even define our own ones to create dramatic effects just like the ones we can see in the sample program. In the sample code, we increase the lighting intensity and add a method that switches between the available light ramps, beginning with LightRampAttrib.makeDefault() which sets the default clamping thresholds for the lighting calculations. Then, the high dynamic range ramps are enabled one after another. These light ramps allow you to have a higher range of color intensities that go beyond the standard range between zero and one. These high intensities are then mapped back into the displayable range, allocating different amounts of values within it to displaying brightness. By using makeHdr0(), we allocate a quarter of the displayable range to brightness values that are greater than one. With makeHdr1() it is a third and with makeHdr2() we are causing Panda3D to use half of the color range for overly bright values. This doesn't come without any side effects, though. By increasing the range used for high intensities, we are decreasing the range of color intensities available for displaying colors that are within the limits of 0 and 1, thus losing contrast and making the scene look grey and washed out. Finally, with the makeSingleThreshold() and makeDoubleThreshold() methods, we are able to create very interesting lighting effects. With a single threshold, lighting values below the given limit will be ignored, while anything that exceeds the threshold will be set to the intensity given in the second parameter of the method. The double threshold system works analogous to the single threshold, but lighting intensity will be normalized to two possible values, depending on which of the two thresholds was exceeded.
Read more
  • 0
  • 0
  • 9439

article-image-using-glew
Packt
30 Jul 2013
9 min read
Save for later

Using GLEW

Packt
30 Jul 2013
9 min read
(For more resources related to this topic, see here.) Quick start – using GLEW You have now installed GLEW successfully and configured your OpenGL project in Visual Studio to use it. In this article, you will learn how to use GLEW by playing with a simple OpenGL program that displays a teapot. We will extend this program to render the teapot with toon lighting by using shader programs. To do this, we will use GLEW to set up the OpenGL extensions necessary to use shader programs. This example gives you a chance to experience GLEW to utilize a popular OpenGL extension. Step 1 – using an OpenGL program to display a teapot Consider the following OpenGL program that displays a teapot with a light shining on it: #include <GL/glut.h>void initGraphics(){glEnable(GL_LIGHTING);glEnable(GL_LIGHT0);const float lightPos[4] = {1, .5, 1, 0};glLightfv(GL_LIGHT0, GL_POSITION, lightPos);glEnable(GL_DEPTH_TEST);glClearColor(1.0, 1.0, 1.0, 1.0);}void onResize(int w, int h){glMatrixMode(GL_PROJECTION);glLoadIdentity();glViewport(0, 0, w, h);gluPerspective(40, (float) w / h, 1, 100);glMatrixMode(GL_MODELVIEW);}void onDisplay(){glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glLoadIdentity();gluLookAt(0.0, 0.0, 5.0,0.0, 0.0, 1.0,0.0, 1.0, 0.0);11Instant GLEWglutSolidTeapot(1);glutSwapBuffers();}int main(int argc, char** argv){glutInit(&argc, argv);glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE);glutInitWindowSize(500, 500);glutCreateWindow("Teapot");initGraphics();glutDisplayFunc(onDisplay);glutReshapeFunc(onResize);glutMainLoop();return 0;} Create a new C++ console project in Visual Studio and copy the above code into the source file. On compiling and running this code in Visual Studio, you will see a window with a grey colored teapot displayed inside it as shown in the screenshot below: Let us briefly examine this OpenGL program and try to understand it. The main function shown below uses the GLUT API to create an OpenGL context, to create a window to render in and to set up the display function that is invoked on every frame. Instead of GLUT, you could also use other cross-platform alternatives such as the OpenGL Framework (GLFW) library or the windowing API of your platform. int main(int argc, char** argv){glutInit(&argc, argv);glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE);glutInitWindowSize(500, 500);glutCreateWindow("Teapot");initGraphics();glutDisplayFunc(onDisplay);glutReshapeFunc(onResize);glutMainLoop();return 0;} Here, the call to glutInit creates an OpenGL context and the calls to glutInitDisplayMode, glutInitWindowSize, and glutCreateWindow help create a window in which to render the teapot. If you examine the initGraphics function, you can see that it enables lighting, creates a light at a given position in 3D space, and sets the background color to white. Similarly, the onResize function sets the size of the viewport based on the size of the rendering window. Passing a pointer to the onResize function as input to glutReshapeFunc ensures that GLUT calls onResize every time the window is resized. And finally, the onDisplay function does the main job of setting the camera and drawing a teapot. Passing a pointer to the onDisplay function as input to glutDisplayFunc ensures that GLUT calls onDisplayfunction every time a frame is rendered. Step 2 – using OpenGL extensions to apply vertex and fragment shaders One of the most common uses of GLEW is to use vertex and fragment shader programs in an OpenGL program. These programs can be written using the OpenGL Shading Language (GLSL). This was standardized in OpenGL 2.0. But, most of the versions of Windows support only OpenGL 1.0 or 1.1. On these operating systems, shader programs can be used only if they are supported by the graphics hardware through OpenGL extensions. Using GLEW is an excellent way to write portable OpenGL programs that use shader programs. The program can be written such that shaders are used when they are supported by the system, and the program falls back on simpler rendering methods when they are not supported. In this section, we extend our OpenGL program to render the teapot using toon lighting. This is a simple trick to render the teapot using cartoonish colors. We first create two new text files: one for the vertex shader named teapot.vert and another for the fragment shader named teapot.frag. You can create these files in the directory that has your OpenGL source program. Copy the following code to the teapot.vert shader file: varying vec3 normal, lightDir;void main(){lightDir = normalize(vec3(gl_LightSource[0].position));normal = normalize(gl_NormalMatrix * gl_Normal);gl_Position = ftransform();} Do not worry if you do not know GLSL or cannot understand this code. We are using this shader program only as an example to demonstrate the use of OpenGL extensions. This shader code applies the standard transformations on vertices. In addition, it also notes down the direction of the light and the normal of the vertex. These variables are passed to the fragment shader program which is described next. Copy the following code to the teapot.frag shader file: varying vec3 normal, lightDir;void main(){float intensity;vec3 n;vec4 color;n = normalize(normal);intensity = max(dot(lightDir, n), 0); if (intensity > 0.97)color = vec4(1, .8, .8, 1.0);else if (intensity > 0.25)color = vec4(.8, 0, .8, 1.0);elsecolor = vec4(.4, 0, .4, 1.0);gl_FragColor = color;} Again, do not worry if you do not understand this code. This fragment shader program is executed at every pixel that is generated for display. The result of this program is a color, which is used to draw that pixel. This program uses the light direction and the normal passed from the vertex shader program to determine the light intensity at a pixel. Based on the intensity value, it picks one of three possible shades of purple to color the pixel. By employing these shader programs, the teapot is rendered in toon lighting like this: However, to get this output our OpenGL program needs to be modified to compile and load these shader programs. Step 3 – including the GLEW header file To be able to call the GLEW API, you need to include the glew.h header file in your OpenGL code. Make sure it is placed above the include files of gl.h, glext.h, glut.h, or any other OpenGL header files. Also, if you include glew.h, you don't really need to include gl.h or glext.h. This is because GLEW redefines the types and function declarations that are in these OpenGL header files. #include <GL/glew.h>#include <GL/glut.h> Step 4 – initializing GLEW GLEW should be initialized before calling any of its other API functions. This can be performed by calling the glewInit function. Ensure that this is called after an OpenGL context has been created. For example, if you are using GLUT in your OpenGL program, call glewInit only after a GLUT window has been created. The code shown below initializes GLEW: GLenum err = glewInit();if (GLEW_OK != err){printf("GLEW init failed: %s!n", glewGetErrorString(err));exit(1);}else{printf("GLEW init success!n");} The call to glewInit does the hard work of determining all the OpenGL extensions that are supported on your system. It returns a value of GLEW_OK or GLEW_NO_ERROR if the initialization was successful; otherwise, it returns a different value. For example, if glewInit is called before an OpenGL context was created, it returns a value of GLEW_ERROR_NO_GL_VERSION. You can find out the cause of a GLEW error by passing the return value of glewInit to the function glewGetErrorString as shown above. This returns a human-readable string that explains the error. Step 5 – checking if an OpenGL extension is supported New or enhanced functionality in the OpenGL API is provided by the means of an extension. This typically means that new data types and API functions are added to the OpenGL specification. Details of the name and functionality of any extension can be found in the OpenGL. In our example, we want our OpenGL program to be able to use GLSL vertex and fragment shaders. This functionality has been provided using extensions that are named GL_ARB_vertex_shader and GL_ARB_fragment_shader. These extensions provide functions to create shader objects, set the shader source code, compile it, link it, and use them with an OpenGL program. Some of the functions provided by this extension are listed below: glCreateShaderObjectARB();glShaderSourceARB();glCompileShaderARB();glCreateProgramObjectARB();glAttachObjectARB();glLinkProgramARB();glUseProgramObjectARB(); To be able to use these functions in our OpenGL program, we first check if the extension is enabled in our system. Depending on the graphics hardware and drivers on your system, not every OpenGL extension might be available and usable on your system. For example, most versions of Windows support only OpenGL 1.0 or 1.1. The drivers supplied by graphics hardware vendors, such as NVIDIA or AMD for example, might support more recent versions of OpenGL and OpenGL extensions. Every OpenGL extension has a name of the form GL_VENDOR_extension_name. The VENDOR may be NV, ATI, APPLE, EXT, ARB, or any such supported vendor name. An extension created by a single vendor is called a vendor-specific extension. If it is created by many vendors, it is called a multivendor extension. If many users find an extension to be a good enhancement, it is promoted to an ARB-approved extension. Such extensions might be integrated into future versions of OpenGL as a core feature. To check for an extension using GLEW, you check if a global boolean variable named GLEW_VENDOR_extension_name is set to true. These variables are defined and their values are set when you initialize GLEW using glewInit. So, to test if vertex and fragment shaders are supported, we add the following code: if (!GLEW_ARB_vertex_shader || !GLEW_ARB_fragment_shader){printf("No GLSL supportn");exit(1);} In this example, we exit the program if these extensions are not supported. Alternatively, you could write the program so that it switches to a simpler or alternate rendering method if the extension you want is not supported. Summary This article provided you the details to use GLEW with OpenGL code using a simple example of teapot rendering. Resources for Article : Further resources on this subject: Introduction to Modern OpenGL [Article] Tips and Tricks for Getting Started with OpenGL and GLSL 4.0 [Article] Android Native Application API [Article]
Read more
  • 0
  • 0
  • 9221
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-cryengine-3-terrain-sculpting
Packt
21 Jun 2011
11 min read
Save for later

CryENGINE 3: Terrain Sculpting

Packt
21 Jun 2011
11 min read
CryENGINE 3 Cookbook Over 100 recipes written by Crytek developers for creating AAA games using the technology that created Crysis 2 Creating a new level Before we can do anything with the gameplay of the project that you are creating, we first need a foundation of a new level for the player to stand on. This recipe will cover how to create a new level from scratch. Getting ready Before we begin, you must have Sandbox 3 open. How to do it... At any point, with Sandbox open, you may create a new level by following these steps: Click File (found in the top -left of the Sandbox's main toolbar). Click New. From here, you will see a new dialog screen that will prompt you for information on how you want to set up your level. The most important aspect of a level is naming it, as you will not be able to create a level without some sort of proper name for the level's directory and its .cry file. You may name your level anything you wish, but for the ease of instruction we shall refer to this level as My_Level: In the Level Name dialog box, type in My_Level. For the Terrain properties, use the following values: Use Custom Terrain Size: True Heightmap Resolution:512x512 Meters Per Unit: 1 Click OK. Depending on your system specifications, you may find that creating a new level will require anywhere from a few seconds to a couple of minutes. Once finished, the Viewport should display a clear blue sky with the dialog in your console reading the following three lines: Finished synchronous pre-cache of render meshes for 0 CGF's Finished pre-caching camera position (1024,1024,100) in 0.0 sec Spawn player for channel 1 This means that the new level was created successfully. How it works... Let's take a closer look at each of the options used while creating this new level. Using the Terrain option This option allows the developer to control whether to have any terrain on the level to be manipulated by a heightmap or not. Sometimes terrain can be expensive for levels and if any of your future levels contain only interiors or only placed objects for the player to navigate on, then setting this value to false will be a good choice for you and will save a tremendous amount of memory and aid in the performance of the level later on. Heightmap resolution This drop-down controls the resolution of the heightmap and the base size of the play area defined. The settings can range from the smallest resolution (128 x 128) all the way up to the largest supported resolution (8192 x 8192). Meters per unit If the Heightmap Resolution is looked at in terms of pixel size, then this dialog box can also be viewed as the Meters Per Pixel. This means that each pixel of the heightmap will be represented by these many meters. For example, if a heightmap's resolution has 4 Meters Per Unit (or Pixel), then each pixel on the generated heightmap will measure four meters in length and width on the level. Even though this Meters Per Unit can be used to increase the size of your level, it will decrease the fidelity of the heightmap. You will notice that attempting to smoothen out the terrain may be difficult as there will be a wider minimum triangle size set by this value. Terrain size This is the resulting size of the level with the equation of (Heightmap Resolution) x (Meters Per unit). Here are some examples of the results you will see (m = meters): (128x128) x 4m = 512x512m (512x512) x 16m = 8192x8192m (1024x1024) x 2m = 2048x2048m There's more... If you need to change your unit size after creating the map, you may change it by going into the Terrain Editor | Modify | Set Unit Size. This will allow you to change the original Meters Per Unit to the size you want it to be.   Generating a procedural terrain This recipe deals with the procedural generation of a terrain. Although never good enough for a final product because you will want to fine tune the heightmap to your specifications, these generated terrains are a great starting point for anyone new to creating levels or for anyone who needs to set up a test level with the Sandbox. Different heightmap seeds and a couple of tweaks to the height of the level and you can generate basic mountain ranges or islands quickly that are instantly ready to use. Getting ready Have My_Level open inside of Sandbox. How to do it... Up at the top-middle of the Sandbox main toolbar, you will find a menu selection called Terrain. From there you should see a list of options, but for now you will want to click on Edit Terrain. This opens the Terrain Editor window. The Terrain Editor window has a multitude of options that can be used to manipulate the heightmap in your level. But first we want to set up a basic generated heightmap for us to build a simple map with. Before we generate anything, we should first set the maximum height of the map to something more manageable. Follow these steps: Click Modify. Then click Set Max Height. Set your Max Terrain Height to 256 (these units are in meters). Now, we may be able to generate the terrain: Click Tools. Then click Generate Terrain. Modify the Variation (Random Base) to the value of 15. Click OK. After generating, you should be able to see a heightmap similar to the following screenshot: This is a good time to generate surface texture (File | Generate surface texture | OK), which allows you to see the heightmap with a basic texture in the Perspective View. How it works... The Maximum Height value is important as it governs the maximum height at which you can raise your terrain to. This does not mean that it is the maximum height of your level entirely, as you are still able to place objects well above this value. It is also important to note that if you import a grey scale heightmap into CryENGINE then this value will be used as the upper extreme of the heightmap (255,255,255 white) and the lower extreme will always be at 0 (0,0,0 black). Therefore the heightmap will be generated within 0 m height and the maximum height. Problems such as the following are a common occurrence: Tall spikes are everywhere on the map or there are massive mountains and steep slopes: Solution: Reduce the Maximum Height to a value that is more suited to the mountains and slopes you want The map is very flat and has no hills or anything from my heightmap: Solution: Increase the Maximum Height to a value that is suitable for making the hills you want There's more... Here are some other settings you might choose to use while generating the terrain. Terrain generation settings The following are the settings to generate a procedural terrain: Feature Size: This value handles the general height manipulations within the seed and the size of each mound within the seed. As the size of the feature depends greatly on rounded numbers it is easy to end up with a perfectly rounded island, therefore it is best to leave this value at 7.0. Bumpiness / Noise (Fade): Basically, this is a noise filter for the level. The greater the value, the more noise will appear on the heightmap. Detail (Passes): This value controls how detailed the slopes will become. By default, this value is very high to see the individual bumps on the slopes to give a better impression of a rougher surface. Reducing this value will decrease the amount of detail/roughness in the slopes seen. Variation: This controls the seed number used in the overall generation of the Terrain Heightmap. There are a total of 33 seeds ranging from 0 – 32 to choose from as a starting base for a basic heightmap. Blurring (Blur Passes): This is a Blur filter. The higher the amount, the smoother the slopes will be on your heightmap. Set Water Level: From the Terrain Editor window, you can adjust the water level from Modify | Set Water Level. This value changes the base height of the ocean level (in meters). Make Isle: This tool allows you to take the heightmap from your level and automatically lowers the border areas around the map to create an island. From the Terrain Editor window, select Modify | Make Isle.   Navigating a level with the Sandbox Camera The ability to intuitively navigate levels is a basic skill that all developers should be familiar with. Thankfully, this interface is quite intuitive to anyone who is already familiar with the WASD control scheme popular in most First Person Shooters Games developed on the PC. Getting ready You should have already opened a level from the CryENGINE 3 Software Development Kit content and seen a perspective viewport displaying the level. The window where you can see the level is called the Perspective Viewport window. It is used as the main window to view and navigate your level. This is where a large majority of your level will be created and common tasks such as object placement, terrain editing, and in-editor play testing will be performed. How to do it... The first step to interacting with the loaded level is to practice moving in the Perspective Viewport window. Sandbox is designed to be ergonomic for both left and right-handed users. In this example, we use the WASD control scheme, but the arrow keys are also supported for movement of the camera. Press W to move forwards. Then press S to move backwards. A is pressed to move or strafe left. Finally, D is pressed to move or strafe right. Now you have learned to move the camera on its main axes, it's time to adjust the rotation of the camera. When the viewport is the active window, hold down the right mouse button on your mouse and move the mouse pointer to turn the view. You can also hold down the middle mouse button and move the mouse pointer to pan the view. Roll the middle mouse button wheel to move the view forward or backward. Finally, you can hold down Shift to double the speed of the viewport movements. How it works... The Viewport allows for a huge diversity of views and layouts for you to view your level; the perspective view is just one of many. The perspective view is commonly used as it displays the output of the render engine. It also presents you a view of your level using the standard camera perspective, showing all level geometry, lighting, and effects. To experiment further with the viewport, note that it can also render subsystems and their toolsets such as flow graph, or character editor. There's more... You will likely want to adjust the movement speed and how to customize the viewport to your individual use. You can also split the viewport in multiple different views, which is discussed further. Viewport movement speed control The Speed input is used to increase or decrease the movement speed of all the movements you make in the main Perspective Viewport. The three buttons to the right of the Speed: inputs are quick links to the .1, 1, and 10 speeds. Under Views you can adjust the viewport to view different aspects of your level Top View, Front, and Left views will show their respective aspects of your level, consisting of bounding boxes and line-based helpers. It should be noted that geometry is not drawn. Map view shows an overhead map of your level with helper, terrain, and texture information pertaining to your level. Splitting the main viewport to several subviewports Individual users can customize the layout and set viewing options specific to their needs using the viewport menu accessed by right-clicking on the viewports header. The Layout Configuration window can be opened from the viewport header under Configure Layout. Once selected, you will be able to select one of the preset configurations to arrange the windows of the Sandbox editor into multiple viewport configurations. It should be recognized that in multiple viewport configurations some rendering effects may be disabled or performance may be reduced.  
Read more
  • 0
  • 0
  • 9207

article-image-cryengine-3-fun-physics
Packt
12 Jul 2011
4 min read
Save for later

CryENGINE 3: Fun Physics

Packt
12 Jul 2011
4 min read
CryENGINE 3 Cookbook Over 90 recipes written by Crytek developers for creating third-generation real-time games Low gravity In this simple recipe, we will look at utilizing the GravityBox to set up a low gravity area within a level. Getting ready Have Sandbox open Then open My_Level.cry How to do it... To start, first we must place down a GravityBox. In the RollupBar, click on the Entities button. Under the Physics section, select GravityBox. Place the GravityBox on the ground: Keeping the default dimensions (20, 20, 20 meters), the only property here that we want to change is the gravity. The default settings in this box set this entire area within the level to be a zero gravity zone. To adjust the up/down gravity of this, we need to change the value of gravity and the Z axis. To mimic normal gravity, this value would need to be set to the acceleration value of -9.81. To change this value to a lower gravity value, (something like the Moon's gravity) simply change it to a higher negative value such as -1.62. How it works... The GravityBox is a simple bounding box which overrides the defined gravity in the code (-9.81) and sets its own gravity value within the bounding box. Anything physicalized and activated to receive physics updates will behave within the confines of these gravitational rules unless they fall outside of the bounding box. There's more... Here are some useful tips about the gravity objects. Uniform property The uniform property within the GravityBox defines whether the GravityBox should use its own local orientation or the world's. If true, the GravityBox will use its own local rotation for the gravitational direction. If false, it will use the world's direction. This is used when you wish to have the gravity directed sideways. Set this value to True and then rotate the GravityBox onto its side. Gravity sphere Much like the GravityBox, the GravitySphere holds all the same principles but in a radius instead of a bounding box. The only other difference with the GravitySphere is that a false uniform Boolean will cause any object within the sphere to be attracted/repulsed from the center of the axis.   Hangman on a rope In this recipe, we will look at how we can utilize a rope to hang a dead body from it. Getting ready Open Sandbox Then open My_Level.cry How to do it... Begin by drawing out a rope: Open the RollupBar. From the Misc button, select Rope. With Grid Snap on and set to 1 meter, draw out a straight rope that has increments of one meter (by clicking once for every increment) up to four meters (double-click to finalize the rope). Align the rope so that from end to end it is along the Z axis (up and down) and a few meters off the ground: Next, we will need something solid to hang the rope from. Place down a solid with 1, 1, 1 meter. Align the rope underneath the solid cube while keeping both off the ground. Make sure when aligning the rope to get the end constraint to turn from red to green. This means it is attached to a physical surface: Lastly, we will need to hang a body from this rope. However, we will not hang him in the traditional manner, but rather by one of his feet. In the RollupBar, click on the Entities button. Under the Physics section, select DeadBody. Rotate this body up-side-down and align one of his feet to the bottom end of the rope. Select the rope to make sure the bottom constraint turns green to signal that it is attached. Verify that the Hangman on a rope recipe works by going into game mode and punching the dead body: How it works... The rope is a complicated cylinder that can contain as many bending segments as defined and is allowed to stretch and compress depending on the values defined. Tension and breaking strength can also be defined. But since ropes have expensive physics properties involved, they should be used sparingly.  
Read more
  • 0
  • 0
  • 9160

article-image-parallax-scrolling
Packt
13 Mar 2014
16 min read
Save for later

Parallax scrolling

Packt
13 Mar 2014
16 min read
(For more resources related to this topic, see here.) As a developer, we have been asked numerous times about how to implement parallax scrolling in a 2D game. Cerulean Games, my game studio, has even had the elements of parallax scrolling as the "do or die" requirement to close a project deal with a client. In reality, this is incredibly easy to accomplish, and there are a number of ways to do this. In Power Rangers Samurai SMASH! (developed by Cerulean Games for Curious Brain; you can find it in the iOS App Store), we implemented a simple check that would see what the linear velocity of the player is and then move the background objects in the opposite direction. The sprites were layered on the Z plane, and each was given a speed multiplier based on its distance from the camera. So, as the player moved to the right, all parallax scrolling objects would move to the left based on their multiplier value. That technique worked, and it fared us and our client quite well for the production of the game. This is also a common and quick way to manage parallax scrolling, and it's also pretty much how we're going to manage it in this game as well. OK, enough talk! Have at you! Well, have at the code: Create a new script called ParallaxController and make it look like the following code: -using UnityEngine; using System.Collections; public class ParallaxController : MonoBehaviour {     public GameObject[] clouds;     public GameObject[] nearHills;     public GameObject[] farHills;     public float cloudLayerSpeedModifier;     public float nearHillLayerSpeedModifier;     public float farHillLayerSpeedModifier;     public Camera myCamera;     private Vector3 lastCamPos;     void Start()     {         lastCamPos = myCamera.transform.position;     }     void Update()     {         Vector3 currCamPos = myCamera.transform.position;         float xPosDiff = lastCamPos.x - currCamPos.x;         adjustParallaxPositionsForArray(clouds,           cloudLayerSpeedModifier, xPosDiff);         adjustParallaxPositionsForArray(nearHills,           nearHillLayerSpeedModifier, xPosDiff);         adjustParallaxPositionsForArray(farHills,           farHillLayerSpeedModifier, xPosDiff);         lastCamPos = myCamera.transform.position;     }     void adjustParallaxPositionsForArray(GameObject[]       layerArray, float layerSpeedModifier, float xPosDiff)     {         for(int i = 0; i < layerArray.Length; i++)         {             Vector3 objPos =               layerArray[i].transform.position;             objPos.x += xPosDiff * layerSpeedModifier;             layerArray[i].transform.position = objPos;         }     } } Create a new GameObject in your scene and call it _ParallaxLayers. This will act as the container for all the parallax layers. Create three more GameObjects and call them _CloudLayer, _NearHillsLayer, and _FarHillsLayer, respectively. Place these three objects inside the _ParallaxLayers object, and place the Parallax Controller component onto the _ParallaxLayers object. Done? Good. Now we can move some sprites. Import the sprites from SpritesParallaxScenery. Start placing sprites in the three layer containers you created earlier. For the hills you want to be closer, place the sprites in the _NearHillsLayer container; for the hills you want to be further away, place the sprites in _FarHillsLayer; and place the clouds in the _CloudLayer container. The following screenshot shows an example of what the layers will now look like in the scene: Pro tip Is this the absolute, most efficient way of doing parallax? Somewhat; however, it's a bit hardcoded to only really fit the needs of this game. Challenge yourself to extend it to be flexible and work for any scenario! Parallax layer ordering Wait, you say that the objects are layered in the wrong order? Your hills are all mixed up with your platforms and your platforms are all mixed up with your hills? OK, don't panic, we've got this. What you need to do here is change the Order in Layer option for each of the parallax sprites. You can find this property in the Sprite Renderer component. Click on one of the sprites in your scene, such as one of the clouds, and you can see it in the Inspector panel. Here's a screenshot to show you where to look: Rather than changing each sprite individually, we can easily adjust the sprites in bulk by performing the following steps: Select all of your cloud layer sprites, and under their Sprite Renderer components, set their Order in Layer to 0. Set the Order in Layer property of the _NearHillsLayer sprites to 1 and that of the _FarHillsLayer sprites to 0. Select the Prefab named Platform and set its Order in Layer to 2; you should see all of your Platform sprites instantly update in the scene. Set the Order in Layer values of the Prefabs for Enemy and Player Bullet to 2. Set the sprite on the Player object in the scene to 2 as well. Finally, set the Wall objects to 3 and you're good to go. With the layers all set up, let's finish setting up the parallax layers. First, finish placing any additional parallax sprites; I'll wait. Brilliant! Now, go to the _ParallaxLayers object and let's play around with that Parallax Controller component. We're going to want to add all of those sprites to Parallax Controller. To make this easy, look at the top-right corner of the Inspector panel. See the little lock icon? Click on it. Now, regardless of what you do, the Parallax Controller component will not be deselected. Since it can't be deselected, you can now easily drag-and-drop all of the Cloud sprites into the Clouds array in the ParallaxController component, and all of the _FarHillsLayer child objects into the Far Hills array—you see where this is going. Set the My Camera field to use the Main Camera object. Finally, let's set some values in those Layer Speed Modifier fields. The higher the number, the faster the object will move as the camera moves. As an example, we set the Cloud layer to 0.05, the Near layer to 0.2, and the Far layer to 0.1. Feel free though to play with the values and see what you like! Go ahead and play the game. Click on the play button and watch those layers move! But, what's this? The particles that burst when an enemy is defeated render behind the sprites in the background—actually, they render behind all the sprites! To fix this, we need to tell Unity to render the particles on a layer in front of the sprites. By default, the sprites render after the particles. Let's change that. First, we need to create a new sorting layer. These are special types of layers that tell Unity the order to render things in. Go to the Tags & Layers window and look out for the drop-down menu called Sorting Layers. Add a new layer called ParticleLayer on Layer 1, as shown in the following screenshot: With this in place, it means anything with the Sorting Layers menu of ParticleLayer will render after the Default layer. Now, we need a way to assign this Sorting Layer to the particle system used when enemies are defeated. Create a new script called ParticleLayering and make it look like the following code: using UnityEngine; using System.Collections; public class ParticleLayering : MonoBehaviour {     public string sortLayerString = "";     void Start ()     {         particleSystem.renderer.sortingLayerName = sortLayerString;     } } Add this script to the EnemyDeathFX Prefab and set the Sort Layer String field to ParticleLayer. Go ahead and play the game again now to watch those particles fly in front of the other objects. Finally, if you want a solid color background to your scene, you don't need to worry about adding a colored plane or anything. Simply select the Main Camera object, and in the Inspector panel, look for the Background field in the Camera component. Adjust the color there as per your need. For the example game, we made this color a nice sky blue with the following values: R: 128, G: 197, B: 232, and A: 0. The one thing you may notice we're missing is something at the bottom of the scene. Here's a nice little challenge for you. We've given you a Lava sprite. Now, add in a lava layer of parallax sprites in the foreground using all the info you've read in this article. You can do this! Let's score! One of the most important elements to a game is being able to track progress. A quick and simple way to do this is to implement a score system. In our case, we will have a score that increases whenever you defeat an enemy. Now, Unity does have a built-in GUI system. However, it has some drawbacks. With this in mind, we won't be relying on Unity's built-in system. Instead, we are going to create objects and attach them to the camera, which in turn will allow us to have a 3D GUI. Pro tip If you want to use what this author believes is the best UI system for Unity, purchase a license for NGUI from the Unity Asset Store. I'm not the only one to think it's the best; Unity hired the NGUI developer to build the new official UI system for Unity itself. Let's build out some GUI elements: Create a 3D Text object by navigating to the menu item GameObject | Create Other; name it Score. Make it a child of the Main Camera GameObject and align it such that it sits in the top-left corner of the screen. Set its position to X: -6.91, Y: 4.99, Z: 10 to get this effect. Make the text color solid black and adjust the scaling so that it looks the way you want it to. Set the Anchor field to Upper Left and Alignment to Left. Adjust the scene to your taste, but it should look a little something like the following screenshot: Pro tip Unity's default 3D text font looks rather low quality in most situations. Try importing your own font and then set the font size to something much higher than you would usually need; often around 25 to 40. Then, when you place it in the world, it will look crisp and clean. Let's make it so that the Score visual element can actually track the player's score. Create a new script called ScoreWatcher and write the following code in it: using UnityEngine; using System.Collections; public class ScoreWatcher : MonoBehaviour {     public int currScore = 0;     private TextMesh scoreMesh = null;         void Start()     {         scoreMesh = gameObject.GetComponent<TextMesh>(); scoreMesh.text = "0";     }         void OnEnable()     {         EnemyControllerScript.enemyDied += addScore;     }         void OnDisable()     {         EnemyControllerScript.enemyDied -= addScore;     }         void addScore(int scoreToAdd)     {         currScore += scoreToAdd;         scoreMesh.text = currScore.ToString();     } } You may notice that in the preceding script, we are listening to the enemyDied event on the EnemyControllerScript. What we did here was we allowed other objects to easily create scoring events that the Score object can optionally listen to. There is lots of power to this! Let's add that event and delegate to the enemy. Open up EnemyControllerScript, and in the beginning, add the following code:     // States to allow objects to know when an enemy dies     public delegate void enemyEventHandler(int scoreMod);         public static event enemyEventHandler enemyDied; Then, down in the hitByPlayerBullet function, add the following code just above Destroy(gameObject,0.1f);, right around line 95: // Call the EnemyDied event and give it a score of 25. if(enemyDied != null)     enemyDied(25); Add the ScoreWatcher component to the Score object. Now, when you play the game and defeat the enemies, you can watch the score increase by 25 points each time! Yeeee-haw! Sorry 'bout that... shootin' things for points always makes me feel a bit Texan. Enemies – forever! So, you defeated all your enemies and now find yourself without enemies to defeat. This gets boring fast; so, let's find a way to get more enemies to whack. To do this, we are going to create enemy spawn points in the form of nifty rotating vortexes and have them spit out enemies whenever we kill other enemies. It shall be glorious, and we'll never be without friends to give gifts—and by gifts, we mean bullets. First things first. We need to make a cool-looking vortex. This vortex will be a stacked, animated visual FX object that is built for a 2D world. Don't worry, we've got you covered on textures, so please go through the following steps: Import the ones in the assets folder under SpritesVortex. Create a new GameObject called Vortex and add all three of the Vortex sprites in it, with each of their Position values set to X:0, Y:0, and Z:0. Adjust their Order in Layer values so that the Vortex_Back child is set to 10, Vortex_Center is set to 11, and Vortex_Front is set to 12. You should now have an object that looks something like the following screenshot: Go ahead and give it a nice spinning animation by rotating the Z axis from 0 to 356. Once you're happy with it, create a new script called EnemyRespawner and code it up as shown in the following code snippet: using UnityEngine; using System.Collections; public class EnemyRespawner : MonoBehaviour {     public GameObject spawnEnemy = null;     float respawnTime = 0.0f;         void OnEnable()     {         EnemyControllerScript.enemyDied += scheduleRespawn;     }         void OnDisable()     {         EnemyControllerScript.enemyDied -= scheduleRespawn;     }         // Note: Even though we don't need the enemyScore, we still need to accept it because the event passes it     void scheduleRespawn(int enemyScore)     {         // Randomly decide if we will respawn or not         if(Random.Range(0,10) < 5)             return;                 respawnTime = Time.time + 4.0f;     }         void Update()     {         if(respawnTime > 0.0f)         {             if(respawnTime < Time.time)             {                 respawnTime = 0.0f;                 GameObject newEnemy = Instantiate(spawnEnemy) as GameObject;                 newEnemy.transform.position = transform.position;             }         }     } } Now attach the preceding script to your Vortex object, populate the Spawn Enemy field with the Enemy Prefab, and save the Vortex object as a Prefab. Scatter a bunch of Vortex Prefabs around the level and you can get the hydra effect, where killing one enemy will create two more enemies or even more than two! Also, if you haven't already done so, you may want to go to the Physics Manager option and adjust the settings so that enemies won't collide with other enemies. One more thing—those enemies sort of glide out of their portals very awkwardly. Let's boost the gravity so they fall faster. Click on the main Enemy Prefab and change the Gravity Scale value of the RigidBody 2D component to 30. Now, they'll fall properly! Pro tip There are so many things you can do with enemy spawners that go far, far outside the context of this article. Take a shot at adding some features yourself! Here are a few ideas: Make the spawn vortexes play a special visual effect when an enemy is spawned Give vortexes a range so that they only spawn an enemy if another enemy was killed in their range Make vortexes move around the level Make vortexes have multiple purposes so that enemies can walk into one and come out another Have a special gold enemy worth bonus points spawn after every 100 kills Make an enemy that, when defeated, spawns other enemies or even collectable objects that earn the player bonus points! Summary So, what have we learned here today aside from the fact that shooting enemies with bullets earns you points? Well, check this out. You now know how to use parallax scrolling, 2D layers, and generate objects; and how to use a scoring system. Enemies dying, enemies spawning, freakin' vortexes? I know, you're sitting there going, "Dude, OK, I'm ready to get started on my first 2D game... the next side scrolling MMO Halo meets Candy Crush with bits of Mass Effect and a little Super Mario Bros!" Resources for Article: Further resources on this subject: Unity Game Development: Interactions (Part 1) [Article] Unity Game Development: Interactions (Part 2) [Article] Unity Game Development: Welcome to the 3D world [Article]
Read more
  • 0
  • 0
  • 9088

Packt
17 Feb 2016
72 min read
Save for later

Speaking Java – Your First Game

Packt
17 Feb 2016
72 min read
In this article, we will start writing our very own Java code at the same time as we begin understanding Java syntax. We will learn how to store, retrieve, and manipulate different types of values stored in the memory. We will also look at making decisions and branching the flow of our code based on the values of this data. In this order, we will: Learn some Java syntax and see how it is turned into a running app by the compiler Store data and use it with variables Express yourself in Java with expressions Continue with the math game by asking a question Learn about decisions in Java Continue with the math game by getting and checking the answer (For more resources related to this topic, see here.) Acquiring the preceding Java skills will enable us to build the next two phases of our math game. This game will be able to ask the player a question on multiplication, check the answer and give feedback based on the answer given, as shown in the following screenshot: Java syntax Throughout this book, we will use plain English to discuss some fairly technical things. You will never be asked to read a technical explanation of a Java or Android concept that has not been previously explained in a non-technical way. Occasionally, I might ask or imply that you accept a simplified explanation in order to offer a fuller explanation at a more appropriate time, like the Java class as a black box; however, never will you need to scurry to Google in order to get your head around a big word or a jargon-filled sentence. Having said that, the Java and Android communities are full of people who speak in technical terms and to join in and learn from these communities, you need to understand the terms they use. So the approach this book takes is to learn a concept or appreciate an idea using an entirely plain speaking language, but at the same time, it introduces the jargon as part of the learning. Then, much of the jargon will begin to reveal its usefulness, usually as a way of clarification or keeping the explanation/discussion from becoming longer than it needs to be. The very term, "Java syntax," could be considered technical or jargon. So what is it? The Java syntax is the way we put together the language elements of Java in order to produce code that works in the Java/Dalvik virtual machine. Syntax should also be as clear as possible to a human reader, not least ourselves when we revisit our programs in the future. The Java syntax is a combination of the words we use and the formation of those words into sentence like structures. These Java elements or words are many in number, but when taken in small chunks are almost certainly easier to learn than any human-spoken language. The reason for this is that the Java language and its syntax were specifically designed to be as straightforward as possible. We also have Android Studio on our side which will often let us know if we make a mistake and will even sometimes think ahead and prompt us. I am confident that if you can read, you can learn Java; because learning Java is much easy. What then separates someone who has finished an elementary Java course from an expert programmer? The same things that separate a student of language from a master poet. Mastery of the language comes through practice and further study. The compiler The compiler is what turns our human-readable Java code into another piece of code that can be run in a virtual machine. This is called compiling. The Dalvik virtual machine will run this compiled code when our players tap on our app icon. Besides compiling Java code, the compiler will also check for mistakes. Although we might still have mistakes in our released app, many are stopped discovered at the when our code is compiled. Making code clear with comments As you become more advanced in writing Java programs, the solutions you use to create your programs will become longer and more complicated. Java was designed to manage complexity by having us divide our code into separate chunks, very often across multiple files. Comments are a part of the Java program that do not have any function in the program itself. The compiler ignores them. They serve to help the programmer to document, explain, and clarify their code to make it more understandable to themselves at a later date or to other programmers who might need to use or modify the code. So, a good piece of code will be liberally sprinkled with lines like this: //this is a comment explaining what is going on The preceding comment begins with the two forward slash characters, //. The comment ends at the end of the line. It is known as a single-line comment. So anything on that line is for humans only, whereas anything on the next line (unless it's another comment) needs to be syntactically correct Java code: //I can write anything I like here but this line will cause an error We can use multiple single-line comments: //Below is an important note //I am an important note //We can have as many single line comments like this as we like Single-line comments are also useful if we want to temporarily disable a line of code. We can put // in front of the code and it will not be included in the program. Recall this code, which tells Android to load our menu UI: //setContentView(R.layout.activity_main); In the preceding situation, the menu will not be loaded and the app will have a blank screen when run, as the entire line of code is ignored by the compiler. There is another type of comment in Java—the multiline comment. This is useful for longer comments and also to add things such as copyright information at the top of a code file. Also like the single-line comment, it can be used to temporarily disable code, in this case usually multiple lines. Everything in between the leading /* signs and the ending */ signs is ignored by the compiler. Here are some examples: /* This program was written by a Java expert You can tell I am good at this because my code has so many helpful comments in it. */ There is no limit to the number of lines in a multiline comment. Which type of comment is best to use will depend upon the situation. In this book, I will always explain every line of code explicitly but you will often find liberally sprinkled comments within the code itself that add further explanation, insight or clarification. So it's always a good idea to read all of the code: /* The winning lottery numbers for next Saturday are 9,7,12,34,29,22 But you still want to learn Java? Right? */ All the best Java programmers liberally sprinkle their code with comments. Storing data and using it with variables We can think of a variable as a labeled storage box. They are also like a programmer's window to the memory of the Android device, or whatever device we are programming. Variables can store data in memory (the storage box), ready to be recalled or altered when necessary by using the appropriate label. Computer memory has a highly complex system of addressing that we, fortunately, do not need to interact with in Java. Java variables allow us to make up convenient names for all the data that we want our program to work with; the JVM will handle all the technicalities that interact with the operating system, which in turn, probably through several layers of buck passing, will interact with the hardware. So we can think of our Android device's memory as a huge warehouse. When we assign names to our variables, they are stored in the warehouse, ready when we need them. When we use our variable's name, the device knows exactly what we are referring to. We can then tell it to do things such as "get box A and add it to box C, delete box B," and so on. In a game, we will likely have a variable named as something along the lines of score. It would be this score variable using which we manage anything related to the user's score, such as adding to it, subtracting or perhaps just showing it to the player. Some of the following situations that might arise: The player gets a question right, so add 10 to their existing score The player views their stats screen, so print score on the screen The player gets the best score ever, so make hiScore the same as their current score These are fairly arbitrary examples of names for variables and as long as you don't use any of the characters keywords that Java restricts, you can actually call your variables whatever you like. However, in practice, it is best to adopt a naming convention so that your variable names will be consistent. In this book, we will use a loose convention of variable names starting with a lowercase letter. When there is more than one word in the variable's name, the second word will begin with an uppercase letter. This is called "camel casing." Here are some examples of camel casing: score hiScore playersPersonalBest Before we look at some real Java code with some variables, we need to first look at the types of variables we can create and use. Types of variables It is not hard to imagine that even a simple game will probably have quite a few variables. What if the game has a high score table that remembers the names of the top 10 players? Then we might need variables for each player. And what about the case when a game needs to know if a playable character is dead or alive, or perhaps has any lives/retries left? We might need code that tests for life and then ends the game with a nice blood spurt animation if the playable character is dead. Another common requirement in a computer program, including games, is the right or wrong calculation: true or false. To cover almost these and many other types of information you might want to keep track of, every possibility Java has types. There are many types of variables and, we can also invent our own types or use other people's types. But for now, we will look at the built-in Java types. To be fair, they cover just about every situation we are likely to run into for a while. Some examples are the best way to explain this type of stuff. We have already discussed the hypothetical but highly likely score variable. The sWell score is likely to be a number, so we have to convey this (that the score is a number) to the Java compiler by giving the score an appropriate type. The hypothetical but equally likely playerName will of course, hold the characters that make up the player's name. Jumping ahead a couple of paragraphs, the type that holds a regular number is called an int, and the type that holds name-like data is called a string. And if we try and store a player name, perhaps "Ada Lovelace" in score, which is meant for numbers, we will certainly run into trouble. The compiler says no! Actually, the error would say this: As we can see, Java was designed to make it impossible for such errors to make it to a running program. Did you also spot in the previous screenshot that I had forgotten the semicolon at the end of the line? With this compiler identifying our errors, what could possibly go wrong? Here are the main types in Java. Later, we will see how to start using them: int: This type is used to store integers. It uses 32 pieces (bits) of memory and can therefore store values with a magnitude a little in excess of 2 billion, including negative values. long: As the name hints at, this data types can be used when even larger numbers are required. A long data type uses 64 bits of memory and 2 to the power of 63 is what we can store in this type. If you want to see what that looks like, try this: 9,223,372,036,854,775,807. Perhaps surprisingly, there are uses for long variables but if a smaller variable will do, we should use it so that our program uses less memory. You might be wondering when you might use numbers of this magnitude. The obvious examples would be math or science applications that do complex calculations but another use might be for timing. When you time how long something takes, the Java Date class uses the number of milliseconds since January 1, 1970. The long data type could be useful to subtract a start time from an end time to determine an elapsed time. float: This is for floating-point numbers, that is, numbers where there is precision beyond the decimal point. As the fractional part of a number takes memory space just as the whole number portion, the range of numbers possible in a float is therefore decreased compared to non-floating-point numbers. So, unless our variable will definitely use the extra precision, float would not be our data type of choice. double: When the precision in float is not enough we have double. short: When even an int data type is overkill, the super-skinny short fits into the tiniest of storage boxes, but we can only store around 64,000 values, from -32,768 to 32,767. byte: This is an even smaller storage box than a short type. There is plenty of room for these in memory but a byte can only store values from -128 to 127. boolean: We will be using plenty of Booleans throughout the book. A Boolean variable can be either true or false—nothing else. Perhaps Boolean answer questions such as: Is the player alive? Has a new high score been reached? Are two examples for a Boolean variable enough? char: This stores a single alphanumeric character. It's not going to change anything on its own but it could be useful if we put lots of them together. I have kept this discussion of data types to a practical level that is useful in the context of this book. If you are interested in how a data type's value is stored and why the limits are what they are, visit the Oracle Java tutorials site at http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html. Note that you do not need any more information than we have already discussed to continue with this book. As we just learned, each type of data that we might want to store will require a specific amount of memory. So we must let the Java compiler know the type of the variable before we begin to use it. The preceding variables are known as the primitive types. They use predefined amounts of memory and so, using our storage analogy, fit into predefined sizes of the storage box. As the "primitive" label suggests, they are not as sophisticated as the reference types. Reference types You might have noticed that we didn't cover the string variable type that we previously used to introduce the concept of variables. Strings are a special type of variable known as a reference type. They quite simply refer to a place in memory where the storage of the variable begins, but the reference type itself does not define a specific amount of memory. The reason for this is fairly straightforward: we don't always know how much data will need to be stored until the program is actually run. We can think of strings and other reference types as continually expanding and contracting storage boxes. So won't one of these string reference types bump into another variable eventually? If you think about the devices memory as a huge warehouse full of racks of labeled storage boxes, then you can think of the Dalvik virtual machine as a super-efficient forklift truck driver that puts the different types of storage boxes in the most appropriate place. And if it becomes necessary, the virtual machine will quickly move stuff around in a fraction of a second to avoid collisions. It will even incinerate unwanted storage boxes when appropriate. This happens at the same time as constantly unloading new storage boxes of all types and placing them in the best place, for that type of variable. Dalvik tends to keep reference variables in a part of the warehouse that is different from the part for the primitive variables. So strings can be used to store any keyboard character, like a char data type but of almost any length. Anything from a player's name to an entire book can be stored in a single string. There are a couple more reference types we will explore. Arrays are a way to store lots of variables of the same type, ready for quick and efficient access. Think of an array as an aisle in our warehouse with all the variables of a certain type lined up in a precise order. Arrays are reference types, so Dalvik keeps these in the same part of the warehouse as strings. So we know that each type of data that we might want to store will require an amount of memory. Hence, we must let the Java compiler know the type of the variable before we begin to use it. Declaration That's enough of theory. Let's see how we would actually use our variables and types. Remember that each primitive type requires a specific amount of real device memory. This is one of the reasons that the compiler needs to know what type a variable will be of. So we must first declare a variable and its type before we attempt to do anything with it. To declare a variable of type int with the name score, we would type: int score; That's it! Simply state the type, in this case int, then leave a space, and type the name you want to use for this variable. Also note the semicolon on the end of the line as usual to show the compiler that we are done with this line and what follows, if anything, is not part of the declaration. For almost all the other variable types, declaration would occur in the same way. Here are some examples. The variable names are arbitrary. This is like reserving a labeled storage box in the warehouse: long millisecondsElapsed; float gravity; double accurrateGravity; boolean isAlive; char playerInitial; String playerName; Initialization Here, for each type, we initialize a value to the variable. Think about placing a value inside the storage box, as shown in the following code: score = 0; millisecondsElapsed = 1406879842000;//1st Aug 2014 08:57:22 gravity = 1.256; double accurrateGravity =1.256098; isAlive = true; playerInitial = 'C'; playerName = "Charles Babbage"; Notice that the char variable uses single quotes (') around the initialized value while the string uses double quotes ("). We can also combine the declaration and initialization steps. In the following snippet of code, we declare and initialize the same variables as we did previously, but in one step each: int score = 0; long millisecondsElapsed = 1406879842000;//1st Aug 2014 08:57:22 float gravity = 1.256; double accurrateGravity =1.256098; boolean isAlive = true; char playerInitial = 'C'; String playerName = "Charles Babbage"; Whether we declare and initialize separately or together is probably dependent upon the specific situation. The important thing is that we must do both: int a; //The line below attempts to output a to the console Log.i("info", "int a = " + a); The preceding code would cause the following result: Compiler Error: Variable a might not have been initialized There is a significant exception to this rule. Under certain circumstances variables can have default values. Changing variables with operators Of course, in almost any program, we are going to need to do something with these values. Here is a list of perhaps the most common Java operators that allow us to manipulate variables. You do not need to memorize them as we will look at every line of code when we use them for the first time: The assignment operator (=): This makesthe variable to the left of the operator the same as the value to the right. For example, hiScore = score; or score = 100;. The addition operator (+): This adds the values on either side of the operator. It is usually used in conjunction with the assignment operator, such as score = aliensShot + wavesCleared; or score = score + 100;. Notice that it is perfectly acceptable to use the same variable simultaneously on both sides of an operator. The subtraction operator (-): This subtracts the value on the right side of the operator from the value on the left. It is usually used in conjunction with the assignment operator, such as lives = lives - 1; or balance = income - outgoings;. The division operator (/): This divides the number on the left by the number on the right. Again, it is usually used in conjunction with the assignment operator, as shown in fairShare = numSweets / numChildren; or recycledValueOfBlock = originalValue / .9;. The multiplication operator (*): This multiplies variables and numbers, such as answer = 10 * 10; or biggerAnswer = 10 * 10 * 10;. The increment operator (++): This is a really neat way to add 1 to the value of a variable. The myVariable = myVariable + 1; statement is the same as myVariable++;. The decrement operator (--): You guessed it: a really neat way to subtract 1 from something. The myVariable = myVariable -1; statement is the same as myVariable--;. The formal names for these operators are slightly different from the names used here for explanation. For example, the division operator is actually one of the multiplicative operators. But the preceding names are far more useful for the purpose of learning Java and if you used the term "division operator", while conversing with someone from the Java community, they would know exactly what you mean. There are actually many more operators than these in Java. If you are curious about operators there is a complete list of them on the Java website at http://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html. All the operators required to complete the projects in this book will be fully explained in this book. The link is provided for the curious among us. Expressing yourself in Java Let's try using some declarations, assignments and operators. When we bundle these elements together into some meaningful syntax, we call it an expression. So let's write a quick app to try some out. Here we will make a little side project so we can play with everything we have learned so far. Instead, we will simply write some Java code and examine its effects by outputting the values of variables to the Android console, called LogCat. We will see exactly how this works by building the simple project and examining the code and the console output: The following is a quick reminder of how to create a new project. Close any currently open projects by navigating to File | Close Project. Click on Start a new Android Studio project. The Create New Project configuration window will appear. Fill in the Application name field and Company Domain with packtpub.com or you could use your own company website name here instead. Now click on the Next button. On the next screen, make sure the Phone and Tablet checkbox has a tick in it. Now we have to choose the earliest version of Android we want to build our app for. Go ahead and play with a few options in the drop-down selector. You will see that the earlier the version we select, the greater is the percentage of devices our app can support. However, the trade-off here is that the earlier the version we select, the less are cutting-edge Android features available in our apps. A good balance is to select API 8: Android 2.2 (Froyo). Go ahead and do that now as shown in the next screenshot. Click on Next. Now select Blank Activity as shown in the next screenshot and click on Next again. On the next screen, simply change Activity Name to MainActivity and click on Finish. To keep our code clear and simple, you can delete the two unneeded methods (onCreateOptionsMenu and onOptionsItemSelected) and their associated @override and @import statements. However, this is not necessary for the example to work. As with all the examples and projects in this book, you can copy or review the code from the download bundle. Just create the project as described previously and paste the code from MainActivity.java file from the download bundle to the MainActivity.java file that was generated when you created the project in Android Studio. Just ensure that the package name is the same as the one you chose when the project was created. However, I strongly recommend going along with the tutorial so that we can learn how to do everything for ourselves. As this app uses the LogCat console to show its output, you should run this app on the emulator only and not on a real Android device. The app will not harm a real device, but you just won't be able to see anything happening. Create a new blank project called Expressions In Java. Now, in the onCreate method just after the line where we use the setContentView method, add this code to declare and initialize some variables: //first we declare and initialize a few variables int a = 10; String b = "Alan Turing"; boolean c = true; Now add the following code. This code simply outputs the value of our variables in a form where we can closely examine them in a minute: //Let's look at how Android 'sees' these variables //by outputting them, one at a time to the console Log.i("info", "a = " + a); Log.i("info", "b = " + b); Log.i("info", "c = " + c); Now let's change our variables using the addition operator and another new operator. See if you can work out the output values for variables a, b, and c before looking at the output and the code explanation: //Now let's make some changes a++; a = a + 10; b = b + " was smarter than the average bear Booboo"; b = b + a; c = (1 + 1 == 3);//1 + 1 is definitely 2! So false. Let's output the values once more in the same way we did in step 3, but this time, the output should be different: //Now to output them all again Log.i("info", "a = " + a); Log.i("info", "b = " + b); Log.i("info", "c = " + c); Run the program on an emulator in the usual way. You can see the output by clicking on the Android tab from our "useful tabs" area below the Project Explorer. Here is the output, with some of the unnecessary formatting stripped off: info? a = 10 info? b = Alan Turing info? c = true info? a = 21 info? b = Alan Turing was smarter than the average bear Booboo21 info? c = false Now let's discuss what happened. In step 2, we declared and initialized three variables: a: This is an int that holds the value 10 b: This is a string that holds the name of an eminent computer scientist. c: This is a Boolean that holds the value false So when we output the values in step 3, it should be no surprise that we get the following: info? a = 10 info? b = Alan Turing info? c = true In step 4, all the fun stuff happens. We add 1 to the value of our int a using the increment operator like this: a++;. Remember that a++ is the same as a = a + 1. We then add 10 to a. Note we are adding 10 to a after having already added 1. So we get this output for a 10 + 1 + 10 operation: info? a = 21 Now let's examine our string, b. We appear to be using the addition operator on our eminent scientist. What is happening is what you could probably guess. We are adding together two strings "Alan Turing" and "was smarter than the average bear Booboo." When you add two strings together it is called concatenating and the + symbol doubles as the concatenation operator. Finally, for our string, we appear to be adding int a to it. This is allowed and the value of a is concatenated to the end of b. info? b = Alan Turing was smarter than the average bear Booboo21 This does not work the other way round; you cannot add a string to an int. This makes sense as there is no logical answer. a = a + b Finally, let's look at the code that changes our Boolean, c, from true to false: c = (1+1=3);. Here, we are assigning to c the value of the expression contained within the brackets. This would be straightforward, but why the double equals (==)? We have jumped ahead of ourselves a little. The double equals sign is another operator called the comparison operator. So we are really asking, does 1+1 equal 3? Clearly the answer is false. You might ask, "why use == instead of =?" Simply to make it clear to the compiler when we mean to assign and when we mean to compare. Inadvertently using = instead of == is a very common error. The assignment operator (=) assigns the value on the right to the value on the left, while the comparison operator (==) compares the values on either side. The compiler will warn us with an error when we do this but at first glance you might swear the compiler is wrong. Now let's use everything we know and a bit more to make our Math game project. Math game – asking a question Now that we have all that knowledge under our belts, we can use it to improve our math game. First, we will create a new Android activity to be the actual game screen as opposed to the start menu screen. We will then use the UI designer to lay out a simple game screen so that we can use our Java skills with variables, types, declaration, initialization, operators, and expressions to make our math game generate a question for the player. We can then link the start menu and game screens together with a push button. If you want to save typing and just review the finished project, you can use the code downloaded from the Packt website. If you have any trouble getting any of the code to work, you can review, compare, or copy and paste the code from the already completed code provided in the download bundle. The completed code is in the following files that correspond to the filenames we will be using in this tutorial: Chapter 3/MathGameChapter3a/java/MainActivity.java Chapter 3/MathGameChapter3a/java/GameActivity.java Chapter 3/MathGameChapter3a/layout/activity_main.xml Chapter 3/MathGameChapter3a/layout/activity_game.xml As usual, I recommend following this tutorial to see how we can create all of the code for ourselves. Creating the new game activity We will first need to create a new Java file for the game activity code and a related layout file to hold the game Activity UI. Run Android Studio and select your Math Game Chapter 2 project. It might have been opened by default. Now we will create the new Android Activity that will contain the actual game screen, which will run when the player taps the Play button on our main menu screen. To create a new activity, we know need another layout file and another Java file. Fortunately Android Studio will help us do this. To get started with creating all the files we need for a new activity, right-click on the src folder in the Project Explorer and then go to New | Activity. Now click on Blank Activity and then on Next. We now need to tell Android Studio a little bit about our new activity by entering information in the above dialog box. Change the Activity Name field to GameActivity. Notice how the Layout Name field is automatically changed for us to activity_game and the Title field is automatically changed to GameActivity. Click on Finish. Android Studio has created two files for us and has also registered our new activity in a manifest file, so we don't need to concern ourselves with it. If you look at the tabs at the top of the editor window, you will see that GameActivity.java has been opened up ready for us to edit, as shown in the following screenshot: Ensure that GameActivity.java is active in the editor window by clicking on the GameActivity.java tab shown previously. Android overrides some methods for us by default, and that most of them were not necessary. Here again, we can see the code that is unnecessary. If we remove it, then it will make our working environment simpler and cleaner. To avoid this here, we will simply use the code from MainActivity.java as a template for GameActivity.java. We can then make some minor changes. Click on the MainActivity.java tab in the editor window. Highlight all of the code in the editor window using Ctrl + A on the keyboard. Now copy all of the code in the editor window using the Ctrl + C on the keyboard. Now click on the GameActivity.java tab. Highlight all of the code in the editor window using Ctrl + A on the keyboard. Now paste the copied code and overwrite the currently highlighted code using Ctrl + V on the keyboard. Notice that there is an error in our code denoted by the red underlining as shown in the following screenshot. This is because we pasted the code referring to MainActivity in our file which is called GameActivity. Simply change the text MainActivity to GameActivity and the error will disappear. Take a moment to see if you can work out what the other minor change is necessary, before I tell you. Remember that setContentView loads our UI design. Well what we need to do is change setContentView to load the new design (that we will build next) instead of the home screen design. Change setContentView(R.layout.activity_main); to setContentView(R.layout.activity_game);. Save your work and we are ready to move on. Note the Project Explorer where Android Studio puts the two new files it created for us. I have highlighted two folders in the next screenshot. In future, I will simply refer to them as our java code folder or layout files folder. You might wonder why we didn't simply copy and paste the MainActivity.java file to begin with and saved going through the process of creating a new activity? The reason is that Android Studio does things behind the scenes. Firstly, it makes the layout template for us. It also registers the new Activity for use through a file we will see later, called AndroidManifest.xml. This is necessary for the new activity to be able to work in the first place. All things considered, the way we did it is probably the quickest. The code at this stage is exactly the same as the code for the home menu screen. We state the package name and import some useful classes provided by Android: package com.packtpub.mathgamechapter3a.mathgamechapter3a; import android.app.Activity; import android.os.Bundle; We create a new activity, this time called GameActivity: public class GameActivity extends Activity { Then we override the onCreate method and use the setContentView method to set our UI design as the contents of the player's screen. Currently, however, this UI is empty: super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); We can now think about the layout of our actual game screen. Laying out the game screen UI As we know, our math game will ask questions and offer the player some multiple choices to choose answers from. There are lots of extra features we could add, such as difficulty levels, high scores, and much more. But for now, let's just stick to asking a simple, predefined question and offering a choice of three predefined possible answers. Keeping the UI design to the bare minimum suggests a layout. Our target UI will look somewhat like this: The layout is hopefully self-explanatory, but let's ensure that we are really clear; when we come to building this layout in Android Studio, the section in the mock-up that displays 2 x 2 is the question and will be made up of three text views (both numbers, and the = sign is also a separate view). Finally, the three options for the answer are made up of Button layout elements. We used all of these UI elements, but this time, as we are going to be controlling them using our Java code, there are a few extra things we need to do to them. So let's go through it step by step: Open the file that will hold our game UI in the editor window. Do this by double-clicking on activity_game.xml. This is located in our UI layout folder which can be found in the project explorer. Delete the Hello World TextView, as it is not required. Find the Large Text element on the palette. It can be found under the Widgets section. Drag three elements onto the UI design area and arrange them near the top of the design as shown in the next screenshot. It does not have to be exact; just ensure that they are in a row and not overlapping, as shown in the following screenshot: Notice in the Component Tree window that each of the three TextViews has been assigned a name automatically by Android Studio. They are textView , textView2 and textView3: Android Studio refers to these element names as an id. This is an important concept that we will be making use of. So to confirm this, select any one of the textViews by clicking on its name (id), either in the component tree as shown in the preceding screenshot or directly on it in the UI designer shown previously. Now look at the Properties window and find the id property. You might need to scroll a little to do this: Notice that the value for the id property is textView. It is this id that we will use to interact with our UI from our Java code. So we want to change all the IDs of our TextViews to something useful and easy to remember. If you look back at our design, you will see that the UI element with the textView id is going to hold the number for the first part of our math question. So change the id to textPartA. Notice the lowercase t in text, the uppercase P in Part, and the uppercase A. You can use any combination of cases and you can actually name the IDs anything you like. But just as with naming conventions with Java variables, sticking to conventions here will make things less error-prone as our program gets more complicated. Now select textView2 and change id to textOperator. Select the element currently with id textView3 and change it to textPartB. This TextView will hold the later part of our question. Now add another Large Text from the palette. Place it after the row of the three TextViews that we have just been editing. This Large Text will simply hold our equals to sign and there is no plan to ever change it. So we don't need to interact with it in our Java code. We don't even need to concern ourselves with changing the ID or knowing what it is. If this situation changed, we could always come back at a later time and edit its ID. However, this new TextView currently displays Large Text and we want it to display an equals to sign. So in the Properties window, find the text property and enter the value =. We have changed the text property, and you might also like to change the text property for textPartA, textPartB, and textOperator. This is not absolutely essential because we will soon see how we can change it via our Java code; however, if we change the text property to something more appropriate, then our UI designer will look more like it will when the game runs on a real device. So change the text property of textPartA to 2, textPartB to 2, and textOperator to x. Your UI design and Component tree should now look like this: For the buttons to contain our multiple choice answers, drag three buttons in a row, below the = sign. Line them up neatly like our target design. Now, just as we did for the TextViews, find the id properties of each button, and from left to right, change the id properties to buttonChoice1, buttonChoice2, and buttonChoice3. Why not enter some arbitrary numbers for the text property of each button so that the designer more accurately reflects what our game will look like, just as we did for our other TextViews? Again, this is not absolutely essential as our Java code will control the button appearance. We are now actually ready to move on. But you probably agree that the UI elements look a little lost. It would look better if the buttons and text were bigger. All we need to do is adjust the textSize property for each TextView and for each Button. Then, we just need to find the textSize property for each element and enter a number with the sp syntax. If you want your design to look just like our target design from earlier, enter 70sp for each of the TextView textSize properties and 40sp for each of the Buttons textSize properties. When you run the game on your real device, you might want to come back and adjust the sizes up or down a bit. But we have a bit more to do before we can actually try out our game. Save the project and then we can move on. As before, we have built our UI. This time, however, we have given all the important parts of our UI a unique, useful, and easy to identify ID. As we will see we are now able to communicate with our UI through our Java code. Coding a question in Java With our current knowledge of Java, we are not yet able to complete our math game but we can make a significant start. We will look at how we can ask the player a question and offer them some multiple choice answers (one correct and two incorrect). At this stage, we know enough of Java to declare and initialize some variables that will hold the parts of our question. For example, if we want to ask the times tables question 2 x 2, we could have the following variable initializations to hold the values for each part of the question: int partA = 2; int partB = 2; The preceding code declares and initializes two variables of the int type, each to the value of 2. We use int because we will not be dealing with any decimal fractions. Remember that the variable names are arbitrary and were just chosen because they seemed appropriate. Clearly, any math game worth downloading is going to need to ask more varied and advanced questions than 2 x 2, but it is a start. Now we know that our math game will offer multiple choices as answers. So, we need a variable for the correct answer and two variables for two incorrect answers. Take a look at these combined declarations and initializations: int correctAnswer = partA * partB; int wrongAnswer1 = correctAnswer - 1; int wrongAnswer2 = correctAnswer + 1; Note that the initialization of the variables for the wrong answers depends on the value of the correct answer, and the variables for the wrong answers are initialized after initializing the correctAnswer variable. Now we need to put these values, held in our variables, into the appropriate elements on our UI. The question variables (partA and partB) need to be displayed in our UI elements, textPartA and textPartB, and the answer variables (correctAnswer, wrongAnswer1, and wrongAnswer2) need to be displayed in our UI elements with the following IDs: buttonChoice1, buttonChoice2, and buttonChoice3. We will see how we do this in the next step-by-step tutorial. We will also implement the variable declaration and initialization code that we discussed a moment ago: First, open GameActivity.java in the editor window. Remember that you can do this by double-clicking on GameActivity in our java folder or clicking on its tab above the editor window if GameActivity.java is already open. All of our code will go into the onCreate method. It will go after the setContentView(R.layout.activity_game); line but before the closing curly brace } of the onCreate method. Perhaps, it's a good idea to leave a blank line for clarity and a nice explanatory comment as shown in the following code. We can see the entire onCreate method as it stands after the latest amendments. The parts in bold are what you need to add. Feel free to add helpful comments like mine if you wish: @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         //The next line loads our UI design to the screen         setContentView(R.layout.activity_game);           //Here we initialize all our variables         int partA = 9;         int partB = 9;         int correctAnswer = partA * partB;         int wrongAnswer1 = correctAnswer - 1;         int wrongAnswer2 = correctAnswer + 1;       }//onCreate ends here Now we need to add the values contained within the variables to the TextView and Button of our UI. But first, we need to get access to the UI elements we created. We do that by creating a variable of the appropriate class and linking it via the ID property of the appropriate UI element. We already know the class of our UI elements: TextView and Button. Here is the code that creates our special class variables for each of the necessary UI elements. Take a close look at the code, but don't worry if you don't understand all of it now. We will dissect the code in detail once everything is working. Enter the code immediately after the code entered in the previous step. You can leave a blank line for clarity if you wish. Just before you proceed, note that at two points while typing in this code, you will be prompted to import another class. Go ahead and do so on both occasions: /*Here we get a working object based on either the button or TextView class and base as well as link our new   objects directly to the appropriate UI elements that we created previously*/   TextView textObjectPartA =   (TextView)findViewById(R.id.textPartA);   TextView textObjectPartB =   (TextView)findViewById(R.id.textPartB);   Button buttonObjectChoice1 =   (Button)findViewById(R.id.buttonChoice1);   Button buttonObjectChoice2 =   (Button)findViewById(R.id.buttonChoice2);   Button buttonObjectChoice3 =   (Button)findViewById(R.id.buttonChoice3); In the preceding code, if you read the multiline comment, you will see that I used the term object. When we create a variable type based on a class, we call it an object. Once we have an object of a class, we can do anything that that class was designed to do. Now we have five new objects linked to the elements of our UI that we need to manipulate. What precisely are we going to do with them? We need to display the values of our variables in the text of the UI elements. We can use the objects we just created combined with a method provided by the class, and use our variables as values for that text. As usual, we will dissect this code further at the end of this tutorial. Here is the code to enter directly after the code in the previous step. Try and work out what is going on before we look at it together: //Now we use the setText method of the class on our objects //to show our variable values on the UI elements. //Just like when we output to the console in the exercise - //Expressions in Java, only now we use setText method //to put the values in our variables onto the actual UI. textObjectPartA.setText("" + partA); textObjectPartB.setText("" + partB);   //which button receives which answer, at this stage is arbitrary.   buttonObjectChoice1.setText("" + correctAnswer); buttonObjectChoice2.setText("" + wrongAnswer1); buttonObjectChoice3.setText("" + wrongAnswer2); Save your work. If you play with the assignment values for partA and partB, you can make them whatever you like and the game adjusts the answers accordingly. Obviously, we shouldn't need to reprogram our game each time we want a new question and we will solve that problem soon. All we need to do now is link the game section we have just made to the start screen menu. We will do that in the next tutorial. Now lets explore the trickier and newer parts of our code in more detail. In step 2, we declared and initialized the variables required so far: //Here we initialize all our variables int partA = 2; int partB = 2; int correctAnswer = partA * partB; int wrongAnswer1 = correctAnswer - 1; int wrongAnswer2 = correctAnswer + 1; Then in step 3, we got a reference to our UI design through our Java code. For the TextViews, it was done like this: TextView textObjectPartA = (TextView)findViewById(R.id.textPartA); For each of the buttons, a reference to our UI design was obtained like this: Button buttonObjectChoice1 =   Button)findViewById(R.id.buttonChoice1); In step 4, we did something new. We used a the setText method to show the values of our variables on our UI elements (TextView and Button) to the player. Let's break down one line completely to see how it works. Here is the code that shows the correctAnswer variable being displayed on buttonObjectChoice1. buttonObjectChoice1.setText("" + correctAnswer); By typing buttonObjectChoice1 and adding a period, as shown in the following line of code, we have access to all the preprogrammed methods of that object's class type that are provided by Android: The power of Button and the Android API There are actually lots of methods that we can perform on an object of the Button type. If you are feeling brave, try this to get a feeling of just how much functionality there is in Android. Type the following code: buttonObjectChoice1 Be sure to type the period on the end. Android Studio will pop up a list of possible methods to use on this object. Scroll through the list and get a feel of the number and variety of options: If a mere button can do all of this, think of the possibilities for our games once we have mastered all the classes contained in Android. A collection of classes designed to be used by others is collectively known as an Application Programmers Interface (API). Welcome to the Android API! In this case, we just want to set the button's text. So, we use setText and concatenate the value stored in our correctAnswer variable to the end of an empty string, like this: setText("" + correctAnswer); We do this for each of the UI elements we require to show our variables. Playing with autocomplete If you tried the previous tip, The power of Button and the Android API, and explored the methods available for objects of the Button type, you will already have some insight into autocomplete. Note that as you type, Android Studio is constantly making suggestions for what you might like to type next. If you pay attention to this, you can save a lot of time. Simply select the correct code completion statement that is suggested and press Enter. You can even see how much time you saved by selecting Help | Productivity Guide from the menu bar. Here you will see statistics for every aspect of code completion and more. Here are a few entries from mine: As you can see, if you get used to using shortcuts early on, you can save a lot of time in the long run. Linking our game from the main menu At the moment, if we run the app, we have no way for the player to actually arrive at our new game activity. We want the game activity to run when the player clicks on the Play button on the main MainActivity UI. Here is what we need to do to make that happen: Open the file activity_main.xml, either by double-clicking on it in the project explorer or by clicking its tab in the editor window. Now, just like we did when building the game UI, assign an ID to the Play button. As a reminder, click on the Play button either on the UI design or in the component tree. Find the id property in the Properties window. Assign the buttonPlay value to it. We can now make this button do stuff by referring to it in our Java code. Open the file MainActivity.java, either by double-clicking on it in the project explorer or clicking on its tab in the editor window. In our onCreate method, just after the line where we setContentView, add the following highlighted line of code: setContentView(R.layout.activity_main); Button buttonPlay = (Button)findViewById(R.id.buttonPlay); We will dissect this code in detail once we have got this working. Basically we are making a connection to the Play button by creating a reference variable to a Button object. Notice that both words are highlighted in red indicating an error. Just as before, we need to import the Button class to make this code work. Use the Alt + Enter keyboard combination. Now click on Import class from the popped-up list of options. This will automatically add the required import directive at the top of our MainActivity.java file. Now for something new. We will give the button the ability to listen to the user clicking on it. Type this immediately after the last line of code we entered: buttonPlay.setOnClickListener(this); Notice how the this keyword is highlighted in red indicating an error. Setting that aside, we need to make a modification to our code now in order to allow the use of an interface that is a special code element that allows us to add a functionality, such as listening for button clicks. Edit the line as follows. When prompted to import another class, click on OK: public class MainActivity extends Activity { to public class MainActivity extends Activity implements View.OnClickListener{ Now we have the entire line underlined in red. This indicates an error but it's where we should be at this point. We mentioned that by adding implements View.OnClickListener, we have implemented an interface. We can think of this like a class that we can use but with extra rules. The rules of the OnClickListener interface state that we must implement/use one of its methods. Notice that until now, we have optionally overridden/used methods as and when they have suited us. If we wish to use the functionality this interface provides, namely listening for button presses, then we have to add/implement the onClick method. This is how we do it. Notice the opening curly brace, {, and the closing curly brace, }. These denote the start and end of the method. Notice that the method is empty and it doesn't do anything, but an empty method is enough to comply with the rules of the OnClickListener interface, and the red line indicating an that our code has an error has gone. Make sure that you type the following code, outside the closing curly brace (}) of the onCreate method but inside the closing curly brace of our MainActivity class: @Override     public void onClick(View view) {               } Notice that we have an empty line between { and } of the onClick method. We can now add code in here to make the button actually do something. Type the following highlighted code between { and } of onClick: @Override     public void onClick(View view) {         Intent i;         i = new Intent(this, GameActivity.class);         startActivity(i);     } OK, so that code is a bit of a mouthful to comprehend all at once. See if you can guess what is happening. The clue is in the method named startActivity and the hopefully familiar term, GameActivity. Notice that we are assigning something to i. We will quickly get our app working and then diagnose the code in full. Notice that we have an error: all instances of the word Intent are red. We can solve this by importing the classes required to make Intent work. As before press Alt + Enter. Run the game in the emulator or on your device. Our app will now work. This is what the new game screen looks like after pressing Play on the menu screen: Almost every part of our code has changed a little and we have added a lot to it as well. Let's go over the contents of MainActivity.java and look at it line by line. For context, here it is in full: package com.packtpub.mathgamechapter3a.mathgamechapter3a;   import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button;     public class MainActivity extends Activity implements View.OnClickListener{       @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         final Button buttonPlay =           (Button)findViewById(R.id.buttonPlay);         buttonPlay.setOnClickListener(this);     }       @Override     public void onClick(View view) {         Intent i;         i = new Intent(this, GameActivity.class);         startActivity(i);     }   } We have seen much of this code before, but let's just go over it a chunk at a time before moving on so that it is absolutely clear. The code works like this: package com.packtpub.mathgamechapter3a.mathgamechapter3a; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; You would probably remember that this first block of code defines what our package is called and makes available all the Android API stuff we need for Button, TextView, and Activity. From our MainActivity.java file, we have this: public class MainActivity extends Activity implements View.OnClickListener{ Our MainActivity declaration with our new bit of code implements View.OnClickListener that gives us the ability to detect button clicks. Next in our code is this: @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main); It is at the start of our onCreate method where we first ask the hidden code of onCreate to do its stuff using super.onCreate(savedInstanceState);. Then we set our UI to the screen with setContentView(R.layout.activity_main);. Next, we get a reference to our button with an ID of buttonPlay: Button buttonPlay = (Button)findViewById(R.id.buttonPlay); buttonPlay.setOnClickListener(this); Finally, our onClick method uses the Intent class to send the player to our GameActivity class and the related UI when the user clicks on the Play button: @Override     public void onClick(View view) {         Intent i;         i = new Intent(this, GameActivity.class);         startActivity(i);     } If you run the app, you will notice that we can now click on the Play button and our math game will ask us a question. Of course, we can't answer it yet. Although we have very briefly looked at how to deal with button presses, we need to learn more of Java in order to intelligently react to them. We will also reveal how to write code to handle presses from several buttons. This will be necessary to receive input from our multiple-choice-centric game_activity UI. Decisions in Java We can now summon enough of Java prowess to ask a question but a real math game must obviously do much more than this. We need to capture the player's answer, and we are nearly there with that—we can detect button presses. From there, we need to be able to decide whether their answer is right or wrong. Then, based on this decision, we have to choose an appropriate course of action. Let's leave the math game aside for now and look at how Java might help us by learning some more fundamentals and syntax of the Java language. More operators Let's look at some more operators: we can already add (+), take away (-), multiply (*), divide (/), assign (=), increment (++), compare (==), and decrement (--) with operators. Let's introduce some more super-useful operators, and then we will go straight to actually understanding how to use them in Java. Don't worry about memorizing every operator given here. Glance at them and their explanations. There, we will put some operators to use and they will become much clearer as we see a few examples of what they allow us to do. They are presented here in a list just to make the variety and scope of operators plain from the start. The list will also be more convenient to refer back to when not intermingled with the discussion about implementation that follows it. ==: This is a comparison operator we saw this very briefly before. It tests for equality and is either true or false. An expression like (10 == 9);, for example, is false. !: The logical NOT operator. The expression, ! (2+2==5).), is true because 2+2 is NOT 5. !=: This is another comparison operator which tests if something is NOT equal. For example, the expression, (10 != 9);), is true, that is, 10 is not equal to 9. >: This is another comparison operator which tests if something is greater than something else. The expression, (10 > 9);), is true. There are a few more comparison operators as well. <: You guessed it. This tests whether the value to the left is less than the value to the right or not. The expression, (10 < 9);, is false. >=: This operator tests whether one value is greater than or equal to the other, and if either is true, the result is true. For example, the expression, (10 >= 9);, is true. The expression, (10 >= 10);, is also true. <=: Like the preceding operator, this operator tests for two conditions but this time, less than and equal to. The expression, (10 <= 9);, is false. The expression, (10 <= 10);, is true. &&: This operator is known as logical AND. It tests two or more separate parts of an expression and all parts must be true in order for the result to be true. Logical AND is usually used in conjunction with the other operators to build more complex tests. The expression, ((10 > 9) && (10 < 11));, is true because both parts are true. The expression, ((10 > 9) && (10 < 9));, is false because only one part of the expression is true and the other is false. ||: This operator is called logical OR. It is just like logical AND except that only one of two or more parts of an expression need to be true for the expression to be true. Let's look at the last example we used but replace the && sign with ||. The expression, ((10 > 9) || (10 < 9));, is now true because one part of the expression is true. All of these operators are virtually useless without a way of properly using them to make real decisions that affect real variables and code. Let's look at how to make decisions in Java. Decision 1 – If they come over the bridge, shoot them As we saw, operators serve hardly any purpose on their own but it was probably useful to see just a part of the wide and varied range available to us. Now, when we look at putting the most common operator, ==, to use, we can start to see the powerful yet fine control that operators offer us. Let's make the previous examples less abstract using the Java if keyword, a few conditional operators, and a small story: in use with some code and a made up military situation that will hopefully make the following examples less abstract. The captain is dying and, knowing that his remaining subordinates are not very experienced, he decides to write a Java program to convey his last orders after he has died. The troops must hold one side of a bridge while awaiting reinforcements. The first command the captain wants to make sure his troops understand is this: If they come over the bridge, shoot them. So how do we simulate this situation in Java? We need a Boolean variable isComingOverBridge. The next bit of code assumes that the isComingOverBridge variable has been declared and initialized. We can then use it like this: if(isComingOverBridge){   //Shoot them } If the isComingOverBridge Boolean is true, the code inside the opening and closing curly braces will run. If not, the program continues after the if block without running it. Decision 2 – Else, do this The captain also wants to tell his troops what to do (stay put) if the enemy is not coming over the bridge. Now we introduce another Java keyword, else. When we want to explicitly do something and the if block does not evaluate to true, we can use else. For example, to tell the troops to stay put if the enemy is not coming over the bridge, we use else: if(isComingOverBridge){   //Shoot them }else{   //Hold position } The captain then realized that the problem wasn't as simple as he first thought. What if the enemy comes over the bridge and has more troops? His squad will be overrun. So, he came up with this code (we'll use some variables as well this time): boolean isComingOverTheBridge; int enemyTroops; int friendlyTroops; //Code that initializes the above variables one way or another   //Now the if if(isComingOverTheBridge && friendlyTroops > enemyTroops){   //shoot them }else if(isComingOverTheBridge && friendlyTroops < enemyTroops) {   //blow the bridge }else{   //Hold position } Finally, the captain's last concern was that if the enemy came over the bridge waving the white flag of surrender and were promptly slaughtered, then his men would end up as war criminals. The Java code needed was obvious. Using the wavingWhiteFlag Boolean variable he wrote this test: if (wavingWhiteFlag){   //Take prisoners } But where to put this code was less clear. In the end, the captain opted for the following nested solution and changing the test for wavingWhiteFlag to logical NOT, like this: if (!wavingWhiteFlag){//not surrendering so check everything else   if(isComingOverTheBridge && friendlyTroops > enemyTroops){     //shoot them   }else if(isComingOverTheBridge && friendlyTroops <   enemyTroops) {     //blow the bridge   } }else{//this is the else for our first if   //Take prisoners { //Holding position This demonstrates that we can nest if and else statements inside of one another to create even deeper decisions. We could go on making more and more complicated decisions but what we have seen is more than sufficient as an introduction. Take the time to reread this if anything is unclear. It is also important to point out that very often, there are two or more ways to arrive at the solution. The right way will usually be the way that solves the problem in the clearest and simplest manner. Switching to make decisions We have seen the vast and virtually limitless possibilities of combining the Java operators with if and else statements. But sometimes a decision in Java can be better made in other ways. When we have to make a decision based on a clear list of possibilities that doesn't involve complex combinations, then switch is usually the way to go. We start a switch decision like this: switch(argument){   } In the previous example, an argument could be an expression or a variable. Then within the curly braces, we can make decisions based on the argument with case and break elements: case x:   //code to for x   break;   case y:   //code for y   break; You can see that in the previous example, each case states a possible result and each break denotes the end of that case as well as the point at which no further case statements should be evaluated. The first break encountered takes us out of the switch block to proceed with the next line of code. We can also use default without a value to run some code if none of the case statements evaluate to true, like this: default://Look no value   //Do something here if no other case statements are true break; Supposing we are writing an old-fashioned text adventure game—the kind of game where the player types commands such as "Go East", "Go West", "Take Sword", and so on. In this case, switch could handle that situation like this example code and we could use default to handle the case of the player typing a command that is not specifically handled: //get input from user in a String variable called command switch(command){     case "Go East":":   //code to go east   break;     case "Go West":   //code to go west   break;   case "Take sword":   //code to take the sword   break;     //more possible cases     default:   //Sorry I don't understand your command   break;   } We will use switch so that our onClick method can handle the different multiple-choice buttons of our math game. Java has even more operators than we have covered here. We have looked at all the operators we are going to need in this book and probably the most used in general. If you want the complete lowdown on operators, take a look at the official Java documentation at http://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html. Math game – getting and checking the answer Here we will detect the right or wrong answer and provide a pop-up message to the player. Our Java is getting quite good now, so let's dive in and add these features. I will explain things as we go and then, as usual, dissect the code thoroughly at the end. The already completed code is in the download bundle, in the following files that correspond to the filenames we will create/autogenerate in Android Studio in a moment: Chapter 3/MathGameChapter3b/java/MainActivity.java Chapter 3/MathGameChapter3b/java/GameActivity.java Chapter 3/MathGameChapter3b/layout/activity_main.xml Chapter 3/MathGameChapter3b/layout/activity_game.xml As usual, I recommend following this tutorial step by step to see how we can create all of the code for ourselves. Open the GameActivity.java file visible in the editor window. Now we need to add the click detection functionality to our GameActivity, just as we did for our MainActivity. However, we will go a little further than the last time. So let's do it step by step as if it is totally new. Once again, we will give the buttons the ability to listen to the user clicking on them. Type this immediately after the last line of code we entered in the onCreate method but before the closing }. This time of course, we need to add some code to listen to three buttons: buttonObjectChoice1.setOnClickListener(this); buttonObjectChoice2.setOnClickListener(this); buttonObjectChoice3.setOnClickListener(this); Notice how the this keyword is highlighted in red indicating an error. Again, we need to make a modification to our code in order to allow the use of an interface, the special code element that allows us to add functionalities such as listening to button clicks. Edit the line as follows. When prompted to import another class, click on OK. Consider this line of code: public class GameActivity extends Activity { Change it to the following line: public class GameActivity extends Activity implements View.OnClickListener{   Now we have the entire preceding line underlined in red. This indicates an error but it is where we should be at this point. We mentioned that by adding implements View.OnClickListener, we have implemented an interface. We can think of this like a class that we can use, but with extra rules. One of the rules of the OnClickListener interface is that we must implement one of its methods, as you might remember. If we wish to use the useful functionality this interface provides (listening to button presses). Now we will add the onClick method. Type the following code. Notice the opening curly brace, {, and the closing curly brace, }. These denote the start and end of the method. Notice that the method is empty; it doesn't do anything but an empty method is enough to comply with the rules of the OnClickListener interface and the red line that indicated an error has gone. Make sure that you type the following code outside the closing curly brace (}) of the onCreate method but inside the closing curly brace of our MainActivity class: @Override     public void onClick(View view) {     } Notice that we have an empty line between the { and } braces of our onClick method. We can now put some code in here to make the buttons actually do something. Type the following in between { and } of onClick. This is where things get different from our code in MainActivity. We need to differentiate between the three possible buttons that could be pressed. We will do this with the switch statement that we discussed earlier. Look at the case criteria; they should look familiar. Here is the code that uses the switch statements: switch (view.getId()) {               case R.id.buttonChoice1:             //button 1 stuff goes here                 break;               case R.id.buttonChoice2:             //button 2 stuff goes here                 break;               case R.id.buttonChoice3:            //button 3 stuff goes here                 break;           }   Each case element handles a different button. For each button case, we need to get the value stored in the button that was just pressed and see if it matches our correctAnswer variable. If it does, we must tell the player they got it right, and if not, we must tell them they got it wrong. However, there is still one problem we have to solve. The onClick method is separate from the onCreate method and the Button objects. In fact, all the variables are declared in the onCreate method. If you try typing the code from step 9 now, you will get lots of errors. We need to make all the variables that we need in onClick available in onClick. To do this, we will move their declarations from above the onCreate method to just below the opening { of GameActivity. This means that these variables become variables of the GameActivity class and can be seen anywhere within GameActivity. Declare the following variables like this: int correctAnswer; Button buttonObjectChoice1; Button buttonObjectChoice2; Button buttonObjectChoice3; Now change the initialization of these variables within onCreate as follows. The actual parts of code that need to be changed are highlighted. The rest is shown for context: //Here we initialize all our variables int partA = 9; int partB = 9; correctAnswer = partA * partB; int wrongAnswer1 = correctAnswer - 1; int wrongAnswer2 = correctAnswer + 1; and TextView textObjectPartA =   (TextView)findViewById(R.id.textPartA);   TextView textObjectPartB =   (TextView)findViewById(R.id.textPartB);   buttonObjectChoice1 = (Button)findViewById(R.id.buttonChoice1);         buttonObjectChoice2 = (Button)findViewById(R.id.buttonChoice2);         buttonObjectChoice3 = (Button)findViewById(R.id.buttonChoice3);   Here is the top of our onClick method as well as the first case statement for our onClick method: @Override     public void onClick(View view) {         //declare a new int to be used in all the cases         int answerGiven=0;         switch (view.getId()) {               case R.id.buttonChoice1:             //initialize a new int with the value contained in buttonObjectChoice1             //Remember we put it there ourselves previously                 answerGiven = Integer.parseInt("" +                     buttonObjectChoice1.getText());                   //is it the right answer?                 if(answerGiven==correctAnswer) {//yay it's the right answer                     Toast.makeText(getApplicationContext(),                       "Well done!",                       Toast.LENGTH_LONG).show();                 }else{//uh oh!                     Toast.makeText(getApplicationContext(),"Sorry that's     wrong", Toast.LENGTH_LONG).show();                 }                 break;   Here are the rest of the case statements that do the same steps as the code in the previous step except handling the last two buttons. Enter the following code after the code entered in the previous step:   case R.id.buttonChoice2:                 //same as previous case but using the next button                 answerGiven = Integer.parseInt("" +                   buttonObjectChoice2.getText());                 if(answerGiven==correctAnswer) {                     Toast.makeText(getApplicationContext(), "Well done!", Toast.LENGTH_LONG).show();                 }else{                     Toast.makeText(getApplicationContext(),"Sorry that's wrong", Toast.LENGTH_LONG).show();                 }                 break;               case R.id.buttonChoice3:                 //same as previous case but using the next button                 answerGiven = Integer.parseInt("" +                     buttonObjectChoice3.getText());                 if(answerGiven==correctAnswer) {                     Toast.makeText(getApplicationContext(), "Well done!", Toast.LENGTH_LONG).show();                 }else{                     Toast.makeText(getApplicationContext(),"Sorry that's wrong", Toast.LENGTH_LONG).show();                 }                 break;           } Run the program, and then we will look at the code carefully, especially that odd-looking Toast thing. Here is what happens when we click on the leftmost button: This is how we did it: In steps 1 through 6, we set up handling for our multi-choice buttons, including adding the ability to listen to clicks using the onClick method and a switch block to handle decisions depending on the button pressed. In steps 7 and 8, we had to alter our code to make our variables available in the onClick method. We did this by making them member variables of our GameActivity class. When we make a variable a member of a class, we call it a field. In steps 9 and 10, we implemented the code that actually does the work in our switch statement in onClick. Let's take a line-by-line look at the code that runs when button1 is pressed. case R.id.buttonChoice1: First, the case statement is true when the button with an id of buttonChoice1 is pressed. Then the next line of code to execute is this: answerGiven = Integer.parseInt(""+ buttonObjectChoice1.getText()); The preceding line gets the value on the button using two methods. First, getText gets the number as a string and then Integer.parseInt converts it to an int. The value is stored in our answerGiven variable. The following code executes next: if(answerGiven==correctAnswer) {//yay it's the right answer   Toast.makeText(getApplicationContext(), "Well done!",     Toast.LENGTH_LONG).show(); }else{//uh oh!     Toast.makeText(getApplicationContext(),"Sorry that's wrong",       Toast.LENGTH_LONG).show();                 } The if statement tests to see if the answerGiven variable is the same as correctAnswer using the == operator. If so, the makeText method of the Toast object is used to display a congratulatory message. If the values of the two variables are not the same, the message displayed is a bit more negative one. The Toast line of code is possibly the most evil thing we have seen thus far. It looks exceptionally complicated and it does need a greater knowledge of Java than we have at the moment to understand. All we need to know for now is that we can use the code as it is and just change the message, and it is a great tool to announce something to the player. If you really want an explanation now, you can think of it like this: when we made button objects, we got to use all the button methods. But with Toast, we used the class directly to access its setText method without creating an object first. We can do this process when the class and its methods are designed to allow it. Finally, we break out of the whole switch statement as follows: break; Self-test questions Q1) What does this code do? // setContentView(R.layout.activity_main); Q2) Which of these lines causes an error? String a = "Hello"; String b = " Vinton Cerf"; int c = 55; a = a + b c = c + c + 10; a = a + c; c = c + a; Q3) We talked a lot about operators and how different operators can be used together to build complicated expressions. Expressions, at a glance, can sometimes make the code look complicated. However, when looked at closely, they are not as tough as they seem. Usually, it is just a case of splitting the expressions into smaller pieces to work out what is going on. Here is an expression that is more convoluted than anything else you will ever see in this book. As a challenge, can you work out: what will x be? int x = 10; int y = 9; boolean isTrueOrFalse = false; isTrueOrFalse = (((x <=y)||(x == 10))&&((!isTrueOrFalse) || (isTrueOrFalse))); Summary We went from knowing nothing about Java syntax to learning about comments, variables, operators, and decision making. As with any language, mastery of Java can be achieved by simply practicing, learning, and increasing our vocabulary. At this point, the temptation might be to hold back until mastery of the current Java syntax has been achieved, but the best way is to move on to new syntax at the same time as revisiting what we have already begun to learn. We will finally finish our math game by adding random questions of multiple difficulties as well as using more appropriate and random wrong answers for the multiple choice buttons. To enable us to do this, we will first learn some more new on Java and syntax. Resources for Article: Further resources on this subject: Asking Permission: Getting your head around Marshmallow's Runtime Permissions [article] Android and iOS Apps Testing at a Glance [article] Practical How-To Recipes for Android [article]
Read more
  • 0
  • 0
  • 8977
article-image-cocos2d-x-installation
Packt
05 Sep 2013
10 min read
Save for later

Cocos2d-x: Installation

Packt
05 Sep 2013
10 min read
(For more resources related to this topic, see here.) Download and installation All the examples in this article were developed on a Mac using Xcode. Although you can use Cocos2d-x to develop your games for other platforms, using different systems, the examples will focus on iOS and Mac. Xcode is free and can be downloaded from the Mac App store (https://developer.apple.com/xcode/index.php), but in order to test your code on an iOS device and publish your games, you will need a developer account with Apple, which will cost you USD 99 a year. You can find more information on their website: https://developer.apple.com/ So, assuming you have an internet connection, and that Xcode is ready to rock, let's begin! Time for action – downloading and installing Cocos2d-x We start by downloading the framework: Go to http://download.cocos2d-x.org/ and download the latest stable version of Cocos2d-x. For this article I'll be using version Cocos2d-2.0-x-2.0.4, which means the 2.0.4 C++ port of version 2.0 of Cocos2d. Uncompress the files somewhere on your machine. Open Terminal and type cd (that is cd and a space). Drag the uncompressed folder you just downloaded to the Terminal window. You should see the path to the folder added to the command line. Hit returnto go to that folder in Terminal. Now type: sudo ./install-templates-xcode.sh -u Hit return again and you're done. What just happened? You have successfully installed the Cocos2d-x templates in your machine. With these in place, you can select the type of Cocos2d-x application you wish to build inside Xcode, and the templates will take care of copying all the necessary files into your application. Next, open Xcode and select Create a new Xcode Project.You should see something like this: So let's build our first application. Hello-x World-x Let's create that old chestnut in computer programming: the hello world example. Time for action – creating an application Open Xcode and select File | New | Project... and follow these steps: In the dialogue box select cocos2d-x under the iOS menu and choose the cocos2dx template. Hit Next . Give the application a name, but not HelloWorld. I'll show you why in a second. You will be then asked to select a place to save the project and you are done. Once your application is ready, click Run to build it. After that, this is what you should see in the simulator: When you run a cocos2d-x application in Xcode it is quite common for the program to post some warnings regarding your code, or most likely the frameworks. These will mostly reference deprecated methods, or statements that do not precisely follow more recent, and stricter rules of the current SDK. But that's okay. These warnings, though certainly annoying, can be ignored. What just happened? You created your first Cocos2d-x application using the cocos2dx template, sometimes referred to as the basic template. The other template options include one with Box2D, one with Chipmunk (both related to physics simulation), one with JavaScript, and one with Lua. The last two options allow you to code some or all of your game using those script languages instead of the native C++; and they work just as you would expect a scripting language to work, meaning the commands written in either Javascript or Lua are actually replaced and interpreted as C++ commands by the compiler. Now if you look at the files created by the basic template you will see a HelloWorldScene class file. That's the reason I didn't want you to call your application HelloWorld, because I didn't want you to have the impression that the file name was based on your project name. It isn't. You will always get a HelloWorldScene file unless you change the template itself. Now let's go over the sample application and its files: The folder structure First you have the Resources folder, where you find the images used by the application. The ios folder has the necessary underlying connections between your app and iOS. For other platforms, you will have their necessary linkage files in separate folders targeting their respective platform (like an android folder the Android platform, for instance.) In the libs folder you have all the cocos2dx files, plus CocosDenshion files (for sound support) and a bunch of other extensions. Using a different template for your projects will result in a different folder structure here, based on what needs to be added to your project. So you will see a Box2D folder, for example, if you choose the Box2D template. In the Classes folder you have your application. In here, everything is written in C++ and this is the home for the part of your code that will hopefully not need to change, however many platforms you target with your application. Now let us go over the main classes of the basic application. The iOS linkage classes AppController and RootViewController are responsible for setting up OpenGL in iOS as well as telling the underlying operating system that your application is about to say Hello... To the World. These classes are written with a mix of Objective-C and C++, as all the nice brackets and the .mm extensions show. You will change very little if anything in these classes; and again that will reflect in changes to the way iOS handles your application. So other targets would require the same instructions or none at all depending on the target. In AppController for instance, I could add support for multitouch. And in RootViewController, I could limit the screen orientations supported by my application. The AppDelegate class This class marks the first time your C++ app will talk to the underlying OS. It attempts to map the main events that mobile devices wants to dispatch and listen to. From here on, all your application will be written in C++ (unless you need something else). In AppDelegate you should setup CCDirector (the cocos2d-x all powerful singleton manager object) to run your application just the way you want. You can: Get rid of the application status information Change the frame rate of your application Tell CCDirector where your high definition images are, and where your standard definition images are, as well as which to use You can change the overall scale of your application to suit different screens The AppDelegate class is also the best place to start any preloading process And, most importantly, it is here you tell the CCDirector object what CCScene to begin your application with Here too you will handle what happens to your application if the OS decides to kill it, push it aside, or hang it upside down to dry. All you need to do is place your logic inside the correct event handler: applicationDidEnterBackground or applicationWillEnterForeground. The HelloWorldScene class When you run the application you get a screen with the words Hello World and a bunch of numbers in one corner. These are the display stats you decided you wanted around in the AppDelegate class. The actual screen is created by the oddly named HelloWorldScene class. It is a Layer class that creates its own scene (don't worry if you don't know what a Layer class is, or a Scene class, you will soon enough). When it initializes, HelloWorldScene puts a button on screen that you can press to exit the application. The button is actually a Menu item, part of a Menu group consisting of one button, two image states for that button, and one callback event, triggered when the said button is pressed. The Menu group automatically handles touch events targeting its members, so you don't get to see any of that code floating about. There is also the necessary Label object to show the Hello World message and the background image. Who begets whom If you never worked with either Cocos2d or Cocos2d-x before, the way the initial scene() method is instantiated may lead to dizziness. To recap, in AppDelegate you have: CCScene *pScene = HelloWorld::scene(); pDirector->runWithScene(pScene); CCDirector needs a CCScene object to run, which you can think of as being your application, basically. CCScene needs something to show, which in this case is a CCLayer class. CCScene is then said to contain a CCLayer class. Here a CCScene object is created through a static method scene inside a CCLayer derived class. So the layer creates the scene, and the scene immediately adds the layer to itself. Huh? Relax. This incestuous-like instantiation will most likely only happen once, and you have nothing to do with it when it happens. So you can easily ignore all these funny goings-on and look the other way. I promise instantiations will be much easier after this first one. Further information Follow these steps to access one of the best sources for reference material on Cocos2d-x: its Test project. Time for action – running the test samples You open the test project just like you would do for any other Xcode project: Go inside the folder you downloaded for the framework, and navigate to samples/TestCpp/proj.ios/TestCpp.xcodeproj. Open that project in Xcode. When you run the project, you will see inside the simulator a long list of tests, all nicely organized by topic. Pick any one to review. Better yet, navigate to samples/TestCpp/Classes and if you have a program like TextWrangler or some equivalent, you can open that entire directory inside a Disk Browser window and have all that information ready for referencing right at your desktop. What just happened? With the test samples you can visualize most features in Cocos2d-x and see what they do, as well as some of the ways you can initialize and customize them. I will refer to the code found in the tests quite often. As usual with programming, there is always a different way to accomplish a given task, so sometimes after showing you one way, I'll refer to another one that you can find (and by then easily understand) inside the Test classes. The other tools Now comes the part where you may need to spend a bit more money to get some extremely helpful tools. In this articles examples I use four of them: A tool to help build sprite sheets: I'll use Texture Packer (http://www.codeandweb.com/texturepacker). There are other alternatives, like Zwoptex (http://zwopple.com/zwoptex/). And they usually offer some features for free. A tool to help build particle effects: I'll use Particle Designer (http://www.71squared.com/en/particledesigner). Depending on your operating system you may find free tools online for this. Cocos2d-x comes bundled with some common particle effects that you can customize. But to do it blindly is a process I do not recommend. A tool to help build bitmap fonts: I'll use Glyph Designer (http://www.71squared.com/en/glyphdesigner). But there are others: bmGlyph (which is not as expensive), FontBuilder (which is free). It is not extremely hard to build a Bitmap font by hand, not nearly as hard as building a particle effect from scratch, but doing it once is enough to convince you to get one of these tools fast. A tool to produce sound effects: No contest. cfxr for Mac or the original sfxr for Windows. Both are free (http://www.drpetter.se/project_sfxr.html and http://thirdcog.eu/apps/cfxr respectively). Summary You just learned how to install Cocos2d-x templates and create a basic application. You also learned enough of the structure of a basic Cocos2d-x application to get started to build your first game. Resources for Article: Further resources on this subject: Getting Started With Cocos2d [Article] Cocos2d: Working with Sprites [Article] Cocos2d for iPhone: Surfing Through Scenes [Article]
Read more
  • 0
  • 0
  • 8926

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

article-image-scaling-friendly-font-rendering-distance-fields
Packt
28 Oct 2014
8 min read
Save for later

Scaling friendly font rendering with distance fields

Packt
28 Oct 2014
8 min read
This article by David Saltares Márquez and Alberto Cejas Sánchez, the authors of Libgdx Cross-platform Game Development Cookbook, describes how we can generate a distance field font and render it in Libgdx. As a bitmap font is scaled up, it becomes blurry due to linear interpolation. It is possible to tell the underlying texture to use the nearest filter, but the result will be pixelated. Additionally, until now, if you wanted big and small pieces of text using the same font, you would have had to export it twice at different sizes. The output texture gets bigger rather quickly, and this is a memory problem. (For more resources related to this topic, see here.) Distance field fonts is a technique that enables us to scale monochromatic textures without losing out on quality, which is pretty amazing. It was first published by Valve (Half Life, Team Fortress…) in 2007. It involves an offline preprocessing step and a very simple fragment shader when rendering, but the results are great and there is very little performance penalty. You also get to use smaller textures! In this article, we will cover the entire process of how to generate a distance field font and how to render it in Libgdx. Getting ready For this, we will load the data/fonts/oswald-distance.fnt and data/fonts/oswald.fnt files. To generate the fonts, Hiero is needed, so download the latest Libgdx package from http://libgdx.badlogicgames.com/releases and unzip it. Make sure the samples projects are in your workspace. Please visit the link https://github.com/siondream/libgdx-cookbook to download the sample projects which you will need. How to do it… First, we need to generate a distance field font with Hiero. Then, a special fragment shader is required to finally render scaling-friendly text in Libgdx. Generating distance field fonts with Hiero Open up Hiero from the command line. Linux and Mac users only need to replace semicolons with colons and back slashes with forward slashes: java -cp gdx.jar;gdx-natives.jar;gdx-backend-lwjgl.jar;gdx-backend-lwjgl-natives.jar;extensions gdx-toolsgdx-tools.jar com.badlogic.gdx.tools.hiero.Hiero Select the font using either the System or File options. This time, you don't need a really big size; the point is to generate a small texture and still be able to render text at high resolutions, maintaining quality. We have chosen 32 this time. Remove the Color effect, and add a white Distance field effect. Set the Spread effect; the thicker the font, the bigger should be this value. For Oswald, 4.0 seems to be a sweet spot. To cater to the spread, you need to set a matching padding. Since this will make the characters render further apart, you need to counterbalance this by the setting the X and Y values to twice the negative padding. Finally, set the Scale to be the same as the font size. Hiero will struggle to render the charset, which is why we wait until the end to set this property. Generate the font by going to File | Save BMFont files (text).... The following is the Hiero UI showing a font texture with a Distance field effect applied to it: Distance field fonts shader We cannot use the distance field texture to render text for obvious reasons—it is blurry! A special shader is needed to get the information from the distance field and transform it into the final, smoothed result. The vertex shader found in data/fonts/font.vert is simple. The magic takes place in the fragment shader, found in data/fonts/font.frag and explained later. First, we sample the alpha value from the texture for the current fragment and call it distance. Then, we use the smoothstep() function to obtain the actual fragment alpha. If distance is between 0.5-smoothing and 0.5+smoothing, Hermite interpolation will be used. If the distance is greater than 0.5+smoothing, the function returns 1.0, and if the distance is smaller than 0.5-smoothing, it will return 0.0. The code is as follows: #ifdef GL_ES precision mediump float; precision mediump int; #endif   uniform sampler2D u_texture;   varying vec4 v_color; varying vec2 v_texCoord;   const float smoothing = 1.0/128.0;   void main() {    float distance = texture2D(u_texture, v_texCoord).a;    float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, distance);    gl_FragColor = vec4(v_color.rgb, alpha * v_color.a); } The smoothing constant determines how hard or soft the edges of the font will be. Feel free to play around with the value and render fonts at different sizes to see the results. You could also make it uniform and configure it from the code. Rendering distance field fonts in Libgdx Let's move on to DistanceFieldFontSample.java, where we have two BitmapFont instances: normalFont (pointing to data/fonts/oswald.fnt) and distanceShader (pointing to data/fonts/oswald-distance.fnt). This will help us illustrate the difference between the two approaches. Additionally, we have a ShaderProgram instance for our previously defined shader. In the create() method, we instantiate both the fonts and shader normally: normalFont = new BitmapFont(Gdx.files.internal("data/fonts/oswald.fnt")); normalFont.setColor(0.0f, 0.56f, 1.0f, 1.0f); normalFont.setScale(4.5f);   distanceFont = new BitmapFont(Gdx.files.internal("data/fonts/oswald-distance.fnt")); distanceFont.setColor(0.0f, 0.56f, 1.0f, 1.0f); distanceFont.setScale(4.5f);   fontShader = new ShaderProgram(Gdx.files.internal("data/fonts/font.vert"), Gdx.files.internal("data/fonts/font.frag"));   if (!fontShader.isCompiled()) {    Gdx.app.error(DistanceFieldFontSample.class.getSimpleName(), "Shader compilation failed:n" + fontShader.getLog()); } We need to make sure that the texture our distanceFont just loaded is using linear filtering: distanceFont.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear); Remember to free up resources in the dispose() method, and let's get on with render(). First, we render some text with the regular font using the default shader, and right after this, we do the same with the distance field font using our awesome shader: batch.begin(); batch.setShader(null); normalFont.draw(batch, "Distance field fonts!", 20.0f, VIRTUAL_HEIGHT - 50.0f);   batch.setShader(fontShader); distanceFont.draw(batch, "Distance field fonts!", 20.0f, VIRTUAL_HEIGHT - 250.0f); batch.end(); The results are pretty obvious; it is a huge win of memory and quality over a very small price of GPU time. Try increasing the font size even more and be amazed at the results! You might have to slightly tweak the smoothing constant in the shader code though: How it works… Let's explain the fundamentals behind this technique. However, for a thorough explanation, we recommend that you read the original paper by Chris Green from Valve (http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf). A distance field is a derived representation of a monochromatic texture. For each pixel in the output, the generator determines whether the corresponding one in the original is colored or not. Then, it examines its neighborhood to determine the 2D distance in pixels, to a pixel with the opposite state. Once the distance is calculated, it is mapped to a [0, 1] range, with 0 being the maximum negative distance and 1 being the maximum positive distance. A value of 0.5 indicates the exact edge of the shape. The following figure illustrates this process: Within Libgdx, the BitmapFont class uses SpriteBatch to render text normally, only this time, it is using a texture with a Distance field effect applied to it. The fragment shader is responsible for performing a smoothing pass. If the alpha value for this fragment is higher than 0.5, it can be considered as in; it will be out in any other case: This produces a clean result. There's more… We have applied distance fields to text, but we have also mentioned that it can work with monochromatic images. It is simple; you need to generate a low resolution distance field transform. Luckily enough, Libgdx comes with a tool that does just this. Open a command-line window, access your Libgdx package folder and enter the following command: java -cp gdx.jar;gdx-natives.jar;gdx-backend-lwjgl.jar;gdx-backend-lwjgl-natives.jar;extensionsgdx-tools gdx-tools.jar com.badlogic.gdx.tools.distancefield.DistanceFieldGenerator The distance field font generator takes the following parameters: --color: This parameter is in hexadecimal RGB format; the default is ffffff --downscale: This is the factor by which the original texture will be downscaled --spread: This is the edge scan distance, expressed in terms of the input Take a look at this example: java […] DistanceFieldGenerator --color ff0000 --downscale 32 --spread 128 texture.png texture-distance.png Alternatively, you can use the gdx-smart-font library to handle scaling. It is a simpler but a bit more limited solution (https://github.com/jrenner/gdx-smart-font). Summary In this article, we have covered the entire process of how to generate a distance field font and how to render it in Libgdx. Further resources on this subject: Cross-platform Development - Build Once, Deploy Anywhere [Article] Getting into the Store [Article] Adding Animations [Article]
Read more
  • 0
  • 0
  • 8868
article-image-article-breaking-ground-with-sandbox
Packt
12 Oct 2012
11 min read
Save for later

Breaking Ground with Sandbox

Packt
12 Oct 2012
11 min read
What makes a game? We saw that majority of the games created on the CryENGINE SDK have historically been first-person shooters containing a mix of sandbox and directed gameplay. If you have gone so far as to purchase a book on the use of the CryENGINE 3 SDK, then I am certain that you have had some kind of idea for a game, or even improvements to existing games, that you might want to make. It has been my experience professionally that should you have any of these ideas and want to share or sell them, the ideas that are presented in a playable format, even in early prototype form, are far more effective and convincing than any PowerPoint presentation or 100-page design document. Reducing, reusing, recycling Good practice when creating prototypes and smaller scale games, especially if you lack the expertise in creating certain assets and code, is to reduce, reuse, and recycle. To break down what I mean: Reduce the amount of new assets and new code you need to make Reuse existing assets and code in new and unique ways Recycle the sample assets and code provided, and then convert them for your own uses Developing out of the box As mentioned earlier, the CryENGINE 3 SDK has a huge amount of out-of-the-box features for creating games. Let's begin by following a few simple steps to make our first game world. Before proceeding with this example, it's important to understand the features it is displaying; the level we will have created by the end of this article will not be a full, playable game, but rather a unique creation of yours, which will be constructed using the first major features we will need in our game. It will provide an environment in to which we can design gameplay. With the ultimate goal of this article being to create our own level with the core features immediately available to us, we must keep in mind that these examples are orientated to compliment a first-person shooter and not other genres. The first-person shooter genre is quite well defined as new games come out every year within this genre. So, it should be fairly easy for any developer to follow these examples. In my career, I have seen that you can indeed accomplish a good cross section of different games with the CryENGINE 3 SDK. However, the third- and first-person genres are significantly easier to create, immediately with the example content and features available right out of the box. For the designers:This article is truly a must-have for designers working with the engine. Though, I would highly recommend that all users of sandbox know how to use these features, as they are the principal features typically used within most levels of the different types of games in the CryENGINE. Time for action - creating a new level Let's follow a few simple steps to create our own level: Start the Editor.exe application. Select File | New. This will present you with a New Level dialog box that allows you to do the adjustments of some principal properties of your masterpiece to come. The following screenshot shows the properties available in New Level: Name this New Level, as Book_Example_1. The name that you choose here will identify this level for loading later as well as creating a folder and .cry file of the same name. In the Terrain section of the dialog box, set Heightmap Resolution to 1024x1024 , and Meters Per Unit to 1. Click on OK and your New Level will begin to load. This should occur relatively fast, but will depend on your computer's specifications. You will know the level has been loaded when you see Ready in the status bar. You will also see an ocean stretching out infinitely and some terrain slightly underneath the water. Maneuver your camera so that you have a good, overall view of the map you will create, as seen in the following screenshot: (Move the mouse over the image to enlarge.) What just happened? Congratulations! You now have an empty level to mold and modify at your will. Before moving on, let's talk a little about the properties that we just set, as they are fundamental properties of the levels within CryENGINE. It is important to understand these, as depending on the type of game you are creating, you may need bigger or smaller maps, or you may not even need terrain at all. Using the right Heightmap Resolution When we created the New Level, we chose a Heightmap Resolution of 1024x1024. To explain this further, each pixel on the heightmap has a certain grey level. This pixel then gets applied to the terrain polygons, and depending on the level of grey, will move the polygon on the terrain to a certain height. This is called displacement. Heightmaps always have varying values from full white to full black, where full white is maximum displacement and full black is minimum or no displacement. The higher the resolution of the heightmap, the more the pixels that are available to represent different features on said heightmap. You can thus achieve more definition and a more accurate geometrical representation of your heightmap using higher resolutions. The settings can range from the smallest resolution of 128x128, all the way to the largest supported resolution of 8192x8192 . The following screenshot shows the difference between high resolution and low resolution heightmaps:   Scaling your level with Meters Per Unit If the Heightmap Resolution parameter is examined in terms of pixel size, then this dialog box can be viewed also as the Meters Per Pixel parameter . This means that each pixel of the heightmap will be represented by so many meters. For example, if a heightmap's resolution has 4 Meters Per Unit, then each pixel on the generated heightmap will measure to be 4 meters in length and width on the level. Even though Meters Per Unit can be used to increase the size of your level, it will decrease the fidelity of the heightmap. You will notice that attempting to smoothen out the terrain may be difficult, since there will be a wider, minimum triangle size set by this value. Keep in mind that you can adjust the unit size even after the map has been created. This is done through the terrain editor, which we will discuss shortly. Calculating the real-world size of the terrain The expected size of the terrain can easily be calculated before making the map, because the equation is not so complicated. The real-world size of the terrain can be calculated as: (Heightmap Resolution) x Meters Per Unit = Final Terrain Dimensions. For example: (128x128) x 2m = 256x256m (512x512) x 8m = 4096x4096m (1024x1024) x 2m = 2048x2048m Using or not using terrain In most cases, levels in CryENGINE will use some amount of the terrain. The terrain itself is a highly optimized system that has levels of dynamic tessellation, which adjusts the density of polygons depending on the distance from the camera to the player. Dynamic tessellation is used to make the more defined areas of the terrain closer to the camera and the less defined ones further away, as the amount of terrain polygons on the screen will have a significant impact on the performance of the level. In some cases, however, the terrain can be expensive in terms of performance, and if the game is made in an environment like space or interior corridors and rooms, then it might make sense to disable the terrain. Disabling the terrain in these cases will save an immense amount of memory, and speed up level loading and runtime performance. In this particular example, we will use the terrain, but should you wish to disable it, simply go to the second tab in the RollupBar (usually called the environment tab) and set the ShowTerrainSurface parameter to false , as shown in the following screenshot:   Time for action - creating your own heightmap You must have created a new map to follow this example. Having sufficiently beaten the terrain system to death through explanation, let's get on with what we are most interested in, which is creating our own heightmap to use for our game: As discussed in the previous example, you should now see a flat plane of terrain slightly submerged beneath the ocean. At the top of the Sandbox interface in the main toolbar, you will find a menu selection called Terrain; open this. The following screenshot shows the options available in the Terrain menu. As we want to adjust the terrain, we will select the Edit Terrain option. This will open the Terrain Editor window, which is shown in the following screenshot: You can zoom in and pan this window to further inspect areas within the map. Click-and-drag using the right mouse button to pan the view and use the mouse wheel to zoom in and zoom out. The Terrain Editor window has a multitude of options, which can be used to manipulate the heightmap of your level. Before we start painting anything, we should first set the maximum height of the map to something more manageable: Click on Modify. Click on Set Max Height. Set your Max Terrain Height to 256. Note that the terrain height is measured in meters.     Having now set the Max Height parameter, we are ready to paint! Using a second monitor: This is a good time to take advantage of a second monitor should you have one, as you can leave the perspective view on your primary monitor and view the changes made in the Terrain Editor on your second monitor, in real time. On the right-hand side of the Terrain Editor , you will see a rollout menu named Terrain Brush. We will first use this to flatten a section of the level. Change the Brush Settings to Flatten, and set the following values: Outside Radius = 100 Inside Radius = 100 Hardness = 1 Height = 20     NOTE: You can sample the terrain height in the Terrain Editor or the view port using the shortcut Control when the flatten brush is selected. Now paint over the top half of the map. This will flatten the entire upper half of the terrain to 20 meters in height. You will end up with the following screenshot, where the dark portion represents the terrain, and since it is relatively low compared to our max height, it will appear black: Note that, by default, the water is set to a height of 16 meters. Since we flattened our terrain to a height of 20 meters, we have a 4-meter difference from the terrain to the water in the center of the map. In the perspective viewport, this will look like a steep cliff going into the water. At the location where the terrain meets the water, it would make sense to turn this into a beach, as it's the most natural way to combine terrain and water. To do this, we will smoothen the hard edge of the terrain along the water. As this is to become our beach area, let's now use the smooth tools to make it passable by the player: Change the Type of brush to Smooth and set the following parameters: Outside Radius = 50 Hardness = 1 I find it significantly easier to gauge the effects of the smooth brush in the perspective viewport. Paint the southern edge of the terrain, which will become our beach. It might be difficult to view the effects of the smooth brush simply in the terrain editor, so I recommend using the perspective viewport to paint your beach. Now that we have what will be our beach, let's sculpt some background terrain. Select the Rise/Lower brush and set the following parameters: Outside Radius = 75 Inside Radius = 50 Hardness = 0.8 Height = 1 Before painting, set the Noise Settings for the brush; to do so, check Enable Noise to true. Also set: Scale = 5 Frequency = 25 Paint the outer edges of the terrain while keeping an eye on the perspective viewport at the actual height of the mountain type structure that this creates. You can see the results in the Terrain Editor and perspective view, as seen in the following screenshots: It is a good time to use the shortcut to switch to smooth brush while painting the terrain. While in perspective view, switch to the smooth brush using the Shift shortcut. A good technique is to use the Rise/Lower brush and only click a few times, and then use Shift to switch to the smooth brush and do this multiple times on the same area. This will give you some nice terrain variation, which will serve us nicely when we go to texture it. Don't forget the player's perspective: Remember to switch to game mode periodically to inspect your terrain from the players level. It is often the case that we get caught up in the appearance of a map by looking at it from our point of view while building it, rather than from the point of view of the player, which is paramount for our game to be enjoyable to anyone playing it. Save this map as Book_Example_1_no_color.cry.
Read more
  • 0
  • 0
  • 8818

article-image-lighting-outdoor-scene-blender
Packt
19 Oct 2010
7 min read
Save for later

Lighting an Outdoor Scene in Blender

Packt
19 Oct 2010
7 min read
  Blender 2.5 Lighting and Rendering Bring your 3D world to life with lighting, compositing, and rendering Render spectacular scenes with realistic lighting in any 3D application using interior and exterior lighting techniques Give an amazing look to 3D scenes by applying light rigs and shadow effects Apply color effects to your scene by changing the World and Lamp color values A step-by-step guide with practical examples that help add dimensionality to your scene        Getting the right files Before we get started, we need a scene to work with. There are three scenes provided for our use—an outdoor scene, an indoor scene, and a hybrid scene that incorporates elements that are found both inside as well as outside. All these files can be downloaded from http://www.cgshark.com/lightingand-rendering/ The file we are going to use for this scene is called exterior.blend. This scene contains a tricycle, which we will light as if it were a product being promoted for a company. To download the files for this tutorial, visit http://www.cgshark.com/lighting-and-rendering/ and select exterior.blend. Blender render settings In computer graphics, a two-dimensional image is created from three-dimensional data through a computational process known as rendering. It's important to understand how to customize Blender's internal renderer settings to produce a final result that's optimized for our project, be it a single image or a full-length film. With the settings Blender provides us, we can set frame rates for animation, image quality, image resolution, and many other essential parts needed to produce that optimized final result. The Scene menu We can access these render settings through the Scene menu. Here, we can adjust a myriad of settings. For the sake of these projects, we are only going to be concerned with: Which window Blender will render our image in How render layers are set up Image dimensions Output location and file type Render settings The first settings we see when we look at the Scene menu are the Render settings. Here, we can tell Blender to render the current frame or an animation using the render buttons. We can also choose what type of window we want Blender to render our image in using the Display options. The first option (and the one chosen by default) is Full Screen. This renders our image in a window that overlaps the three-dimensional window in our scene. To restore the three-dimensional view, select the Back to Previous button at the top of the window. The next option is the Image Editor that Blender uses both for rendering as well as UV editing. This is especially useful when using the Compositor, allowing us to see our result alongside our composite node setup. By default, Blender replaces the three-dimensional window with the Image Editor. The last option is the option that Blender has used, by default, since day one—New Window. This means that Blender will render the image in a newly created window, separate from the rest of the program's interface. For the sake of these projects, we're going to keep this setting at the default setting—Full Screen. Dimensions settings These are some of the most important settings that we can set when dealing with optimizing our project output. We can set the image size, frame rate, frame range, and aspect ratio of our render. Luckily for us, Blender provides us with preset render settings, common in the film industry: HDTV 1080P HDTV 720P TV NTSC TV PAL TV PAL 16:9 Because we want to keep our render times relatively low for our projects, we're going to set our preset dimensions to TV NTSC, which results in an image 720 pixels wide by 480 pixels high. If you're interested in learning more about how the other formats behave, feel free to visit http://en.wikipedia.org/wiki/Display_resolution. Output settings These settings are an important factor when determining how we want our final product to be viewed. Blender provides us with numerous image and video types to choose from. When rendering an animation or image sequence, it's always easier to manually set the folder we want Blender to save to. We can tell Blender where we want it to save by establishing the path in the output settings. By default on Macintosh, Blender saves to the /tmp/ folder. Now that we understand how Blender's renderer works, we can start working with our scene! Establishing a workflow The key to constantly producing high-quality work is to establish a well-tested and efficient workflow. Everybody's workflow is different, but we are going to follow this series of steps: Evaluate what the scene we are lighting will require. Plan how we want to lay out the lamps in our scene. Set lamp positions, intensities, colors, and shadows, if applicable. Add materials and textures. Tweak until we're satisfied. Evaluating our scene Before we even begin to approach a computer, we need to think about our scene from a conceptual perspective. This is important, because knowing everything about our scene and the story that's taking place will help us produce a more realistic result. To help kick start this process, we can ask ourselves a series of questions that will get us thinking about what's happening in our scene. These questions can pertain to an entire array of possibilities and conditions, including: Weather What is the weather like on this particular day? What was it like the day before or the day after? Is it cloudy, sunny, or overcast? Did it rain or snow? Source of light Where is the light coming from? Is it in front of, to the side, or even behind the object? Remember, light is reflected and refracted until all energy is absorbed; this not only affects the color of the light, but the quality as well. Do we need to add additional light sources to simulate this effect? Scale of light sources What is the scale of our light sources in relation to our three-dimensional scene? Believe it or not, this factor carries a lot of weight when it comes to the quality of the final render. If any lights feel out of place, it could potentially affect the believability of the final product. The goal of these questions is to prove to ourselves that the scene we're lighting has the potential to exist in real life. It's much harder, if not impossible, to light a scene if we don't know how it could possibly act in the real world. Let's take a look at these questions. What is the weather like? In our case, we're not concerned with anything too challenging, weather wise. The goal of this tutorial is to depict our tricycle in an environment that reflects the effects of a sunny, cloudless day. To achieve this, we are going to use lights with blue and yellow hues for simulating the effect the sun and sky will have on our tricycle. What are the sources of our light and where are they coming from in relation to our scene? In a real situation, the sun would provide most of the light, so we'll need a key light that simulates how the sun works. In our case, we can use a Sun lamp. The key to positioning light sources within a three-dimensional scene is to find a compromise between achieving the desired mood of the image and effectively illuminating the object being presented. What is the scale of our light sources? The sun is rather large, but because of the nature of the Sun lamp in Blender, we don't have to worry about the scale of the lamp in our three-dimensional scene. Sometimes—more commonly when working with indoor scenes, such as the scene we'll approach later—certain light sources need to be of certain sizes in relation to our scene, otherwise the final result will feel unnatural. Although we will be using a realistic approach to materials, textures, and lighting, we are going to present this scene as a product visualization. This means that we won't explicitly show a ground plane, allowing the viewer to focus on the product being presented, in this case, our tricycle.
Read more
  • 0
  • 0
  • 8794
Modal Close icon
Modal Close icon