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-resource-manager
Packt
03 Sep 2013
11 min read
Save for later

Resource Manager

Packt
03 Sep 2013
11 min read
(For more resources related to this topic, see here.) Resource definitions In order to be able to define resources, we need to create a module that will be in charge of handling this. The main idea is that before calling a certain asset through ResourceManager, it has to be defined inResourceDefinitions. In this way, ResourceManager will always have access to some metadata it needs to create the asset (filenames, sizes, volumes, and so on). In order to identify the asset types (sounds, images, tiles, and fonts), we will define some constants (note that the number values of these constants are arbitrary; you could use whatever you want here). Let’s call them RESOURCE_TYPE_[type] (feel free to use another convention if you want to). To make things easier, just follow this convention for now since it’s the one we’ll use in the rest of the book. You should enter them in main.lua as follows: RESOURCE_TYPE_IMAGE = 0RESOURCE_TYPE_TILED_IMAGE = 1RESOURCE_TYPE_FONT = 2RESOURCE_TYPE_SOUND = 3 If you want to understand the actual reason behind these resource type constants, take a look at the load function of our ResourceManager entity in the next section. We need to create a file named resource_definitions.lua and add some simple methods that will handle it. Add the following line to it: module ( “ResourceDefinitions”, package.seeall ) The preceding line indicates that all of the code in the file should be treated as a module function, being accessed through ResourceDefinitions in the code. This is one of many Lua patterns used to create modules. If you’re not used to the Lua’s module function, you can read about it in the modules tutorial at http://lua-users.org/wiki/ModulesTutorial. Next, we will create a table that contains these definitions: local definitions = {} This will be used internally and is not accessible through the module API, so we create it using the keyword local. Now, we need to create the setter, getter, and unload methods for the definitions. The setter method (called set) stores the definition parameter (a table) in the definitions table, using the name parameter (a string) as the key, as follows: function ResourceDefinitions:set(name, definition) definitions[name] = definitionend The getter method (called get, duh!) retrieves the definition that was previously stored (by use of ResourceDefinitions:set ()) using the name parameter as the key of the definitions table, as follows: function ResourceDefinitions:get(name) return definitions[name]end The final method that we’re creating is remove. We use it to clear the memory space used by the definition. In order to achieve this we assign nil to an entry in the definitions table indexed by the name parameter as follows: function ResourceDefinitions:remove (name) definitions[name] = nilend In this way, we remove the reference to the object, allowing the memory to be released by the garbage collector. This may seem useless here, but it’s a good example of how you should manage your objects to be removed from memory by the garbage collector. And besides this, we don’t know information comes in a resource definition; it may be huge, we just don’t know. This is all we need for the resource definitions. We’re making use of the dynamism that Lua provides. See how easy it was to create a repository for definitions that is abstracted from the content of each definition. We’ll define different fields for each asset type, and we don’t need to define them beforehand as we probably would have needed to do in C++. Resource manager We will now create our resource manager. This module will be in charge of creating and storing our decks and assets in general. We’ll retrieve the assets with one single command, and they’ll come from the cache or get created using the definition. We need to create a file named resource_manager.lua and add the following line to it: module ( “ResourceManager”, package.seeall ) This is the same as in the resource definitions; we’re creating a module that will be accessed using ResourceManager. ASSETS_PATH = ‘assets/’ We now create the ASSETS_PATH constant. This is the path where we will store our assets. You could have many paths for different kinds of assets, but in order to keep things simple, we’ll keep all of them in one single directory in this example. Using this constant will allow us to use just the filename instead of having to write the whole path when creating the actual resource definitions, saving us some phalanx injuries! local cache = {} Again, we’re creating a cache table as a local variable. This will be the variable that will store our initialized assets. Now we should take care of implementing the important functionality. In order to make this more readable, I’ll be using methods that we define in the following pages. So, I recommend that you read the whole section before trying to run what we code now. The full source code can be downloaded from the book’s website, featuring inline comments. In the book, we removed the comments for brevity’s sake. Getter The first thing we will implement is our getter method since it’s simple enough: function ResourceManager:get ( name ) if (not self:loaded ( name )) then self:load ( name ) end return cache[name] end This method receives a name parameter that is the identifier of the resource we’re working with. On the first line, we call loaded (a method that we will define soon) to see if the resource identified by name was already loaded. If it was, we just need to return the cached value, but if it was not we need to load it, and that’s what we do in the if statement. We use the internalload method (which we will define later as well) to take care of the loading. We will make this load method store the loaded object in the cache table. So after loading it, the only thing we have to do is return the object contained in the cache table indexed by name. One of the auxiliary functions that we use here is loaded. Let’s implement it since it’s really easy to do so: function ResourceManager:loaded ( name ) return cache[name] ~= nil end What we do here is check whether the cache table indexed by the name parameter is not equal to nil. If cache has an object under that key, this will return true, and that’s what we were looking for to decide whether the object represented by the name parameter was already loaded. Loader load and its auxiliary functions are the most important methods of this module. They’ll be slightly more complex than what we’ve done so far since they make the magic happen. Pay special attention to this section. It’s not particularly hard, but it might get confusing. Like the previous methods, this one receives just the name parameter that represents the asset we’re loading as follows: function ResourceManager:load ( name ) First of all, we retrieve the definition for the resource associated to name. We make a call to the get method from ResourceDefinitions, which we defined earlier as follows: local resourceDefinition = ResourceDefinitions:get( name ) If the resource definition does not exist (because we forgot to define it before), we print an error to the screen, as follows: if not resourceDefinition then print(“ERROR: Missing resource definition for “ .. name ) If the resource definition was retrieved successfully, we create a variable that will hold the resource and (pay attention) we call the correct load auxiliary function, depending on the asset type. else local resource Remember the RESOURCE_TYPE_[type] constants that we created in the ResourceDefinitions module? This is the reason for their existence. Thanks to the creation of the RESOURCE_TYPE_[type] constants, we now know how to load the resources correctly. When we define a resource, we must include a type key with one of the resource types. We’ll insist on this soon. What we do now is call the correct load method for images, tiled images, fonts, and sounds, using the value stored in resourceDefinition.type as follows: if (resourceDefinition.type == RESOURCE_TYPE_IMAGE) then resource = self:loadImage ( resourceDefinition ) elseif (resourceDefinition.type == RESOURCE_TYPE_TILED_IMAGE) then resource = self:loadTiledImage ( resourceDefinition ) elseif (resourceDefinition.type == RESOURCE_TYPE_FONT) then resource = self:loadFont ( resourceDefinition ) elseif (resourceDefinition.type == RESOURCE_TYPE_SOUND) then resource = self:loadSound ( resourceDefinition ) end After loading the current resource, we store it in the cache table, in an entry specified by the name parameter, as follows: -- store the resource under the name on cache cache[name] = resource endend Now, let’s take a look at all of the different load methods. The expected definitions are explained before the actual functions so you have a reference when reading them. Images Loading images is something that we’ve already done, so this is going to look somewhat familiar. In this book, we’ll have two ways of defining images. Let’s take a look at them: {type = RESOURCE_TYPE_IMAGEfileName = “tile_back.png”,width = 62,height = 62,} As you may have guessed, the type key is the one used in the load function. In this case, we need to make it of type RESOURCE_TYPE_IMAGE. Here we are defining an image that has specific width and height values, and that is located at assets/title_back.png. Remember that we will use ASSET_PATH in order to avoid writing assets/ a zillion times. That’s why we’re not writing it on the definition. Another useful definition is: {type = RESOURCE_TYPE_IMAGEfileName = “tile_back.png”,coords = { -10, -10, 10, 10 }} This is handy when you want a specific rectangle inside a bigger image. You can use the cords attribute to define this rectangle. For example, we get a square with 20 pixel long sides centered in the image by specifying coords = { -10, -10, 10, 10 }. Now, let’s take a look at the actual loadImage method to see how this all falls into place: function ResourceManager:loadImage ( definition ) local image First of all, we use the same technique of defining an empty variable that will hold our image: local filePath = ASSETS_PATH .. definition.fileName We create the actual full path by appending the value of fileName in the definition to the value of the ASSETS_PATH constant. if checks whether the coords attribute is defined: if definition.coords thenimage = self:loadGfxQuad2D ( filePath, definition.coords ) Then, we use another auxiliary function called loadGfxQuad2D. This will be in charge of creating the actual image. The reason why we’re using another auxiliary function is that the code used to create the image is the same for both definition styles, but the data in the definition needs to be processed differently. In this case, we just pass the coordinates of the rectangle. else local halfWidth = definition.width / 2 local halfHeight = definition.height / 2 image = self:loadGfxQuad2D(filePath, {-halfWidth, -halfHeight, halfWidth, halfHeight} ) If there were no coords attribute, we’d assume the image is defined using width and height. So what we do is to define a rectangle that covers the whole width and height for the image. We do this by calculating halfWidth and halfHeight and then passing these values to theloadGfxQuad2D method. Remember the discussion about the texture coordinates in Moai SDK; this is the reason why we need to divide the dimensions by 2 and pass them as negative and positive parameters for the rectangle. This allows it to be centered on (0, 0). After loading the image, we return it so it can be stored in the cache by the load method: end return imageend Now the last method we need to write is loadGfxQuad2D. This method is basically to display an image as follows: function ResourceManager:loadGfxQuad2D ( filePath, coords ) local image = MOAIGfxQuad2D.new () image:setTexture ( filePath ) image:setRect ( unpack(cords) ) return imageend Lua’s unpack method is a nice tool that allows you to pass a table as separate parameters. You can use it to split a table into multiple variables as well: x, y = unpack ( position_table )   What we do here is instantiate the MOAIGfxQuad2D class, set the texture we defined in the previous function, and use the coordinates we constructed to set the rectangle this image will use from the original texture. Then we return it so loadImage can use it. Well! That was it for images. It may look complicated at first, but it’s not that complex. The rest of the assets will be simpler than this, so if you understood this one, the rest will be a piece of cake.
Read more
  • 0
  • 0
  • 2046

article-image-papervision3d-external-models-part-2
Packt
17 Nov 2009
7 min read
Save for later

Papervision3D External Models: Part 2

Packt
17 Nov 2009
7 min read
Creating and loading models using SketchUp SketchUp is a free 3D modeling program, which was acquired by Google in 2006. It has been designed as a 3D modeling program that's easier to use than other 3D modeling programs. A key to its success is its easy learning curve compared to other 3D tools. Mac OS X, Windows XP, and Windows Vista operating systems are supported by this program that can be downloaded from http://sketchup.google.com/. Although there is a commercial SketchUp Pro version available, the free version works fine in conjunction with Papervision3D. An interesting feature for non-3D modelers is the integration with Google's 3D Warehouse. This makes it possible to search for models that have been contributed by other SketchUp users. These models are free of any rights and can be used in commercial (Papervision3D) projects. Exporting a model from Google's 3D Warehouse for Papervision3D There are several ways to load a model, coming from Google 3D Warehouse, into Papervision3D. One of them is by downloading a SketchUp file and exporting it to a format Papervision3D works with. This approach will be explained. The strength of Google 3D Warehouse is also its weakness. Anybody with a Google account can add models to the warehouse. This results in a variety of quality of the models. Some are very optimized and work fluently, whereas others reveal problems when you try to make them work in Papervision3D. Or they may not work at all, as they're made of too many polygons to run in Papervision3D. Take this into account while searching for a model in the 3D warehouse. For our example we're going to export a picnic table that was found on Google 3D Warehouse. Start Sketch Up. Choose a template when prompted. This example uses Simple Template – Meters, although there shouldn't be a problem with using one of the other templates. Go to File | 3D Warehouse | Get models to open 3D Warehouse inside SketchUp. Enter a keyword to search for. In this example that will be picnic table. Select a model of your choice. Keep in mind that it has to be low poly, which is something you usually find out by trial and error. Click on Download Model, to import the model into SketchUp and click OK when asked if you want to load the model directly into your Google SketchUp model. Place the model at the origin of the scene. To follow these steps, it doesn't have to be the exact origin, approximately is good enough. By default, a 2D character called Sang appears in the scene, which you do not necessarily have to remove; it will be ignored during export. Because the search returned a lot of picnic tables varying in quality, there is a SketchUp file (can be downloaded from http://www.packtpub.com/files/code/5722_Code.zip). This file has a picnic table already placed on the origin. Of course, you could also choose another picnic table, or any other object of your choice. Leave the model as it is and export it. Go to File | Export | 3D Model. Export it using the Google Earth (*.kmz) format and save it in your assets folder. T he file format we're exporting to originally was meant to display 3D objects in Google Earth. The file ends with .kmz as its extension, and is actually a ZIP archive that contains a COLLADA file and the textures. In the early days of Papervision3D, it was a common trick to create a model using SketchUp and then get the COLLADA file out of the exported Google Earth file, as the Google Earth KMZ file format wasn't still supported then. Importing a Google Earth model into Papervision3D Now that we have successfully exported a model from SketchUp, we will import it into Papervision3D. This doesn't really differ from loading a COLLADA or 3D Studio file. The class we use for parsing the created PicnicTable.kmz file is called KMZ and can be found in the parsers package. Add the following line to the import section of your document class: import org.papervision3d.objects.parsers.KMZ; Replace or comment the code that loads the animated COLLADA model and defines the animations from the previous example. In the init() method we can then instantiate the KMZ class, assign it to the model class property, and load the KMZ file. Make sure you have saved PicnicTable.kmz file into the assets folder of your project. model = new KMZ();model.addEventListener(FileLoadEvent.LOAD_COMPLETE,modelLoaded);KMZ(model).load("assets/PicnicTable.kmz"); ExternalModelsExample That looks familiar, right? Now let's publish the project and your model should appear on the screen. Notice that in many cases, the downloaded and exported model from Google 3D Warehouse might appear very small on your screen in Papervision3D. This is because they are modeled with other metric units than we use in Papervision3D. Our example application places the camera at a 1000 units away from the origin of the scene. Many 3D Warehouse models are made using units that define meters or feet, which makes sense if you were to translate them to real-world units. When a model is, for example, 1 meter wide in SketchUp, this equals to 1 unit in Papervision3D. As you can imagine, a 1 unit wide object in Papervision3D will barely be visible when placing the camera at a distance of 1000. To solve this you could use one of the following options: Use other units in Papervision3D and place your camera at a distance of 5 instead of 1000. Usually you can do this at the beginning of your project, but not while the project is already in progress, as this might involve a lot of changes in your code due to other objects, animations, and calculations that are made with a different scale. Scale your model inside SketchUp to a value that matches the units as you use them in Papervision3D. When the first option can't be realized, this option is recommended. Scale the loaded model in Papervision3D by changing the scale property of your model. model.scale = 20; Although this is an option that works, it's not recommended. Papervision3D has some issues with scaled 3D objects at the time of writing. It is a good convention to use the same units in Papervision3D and your 3D modeling tool. If you want to learn more about modeling with SketchUp, visit the support page on the SketchUp web site http://sketchup.google.com/support. You'll find help resources such as video tutorials and a help forum. Creating and loading models using Blender Blender is an open source, platform-independent 3D modeling tool, which was first released in 1998 as a shareware program. It went open source in 2002. Its features are similar to those in commercial tools such as 3ds Max, Maya, and Cinema4D. However, it has a reputation of having a difficult learning curve compared to other 3D modeling programs. Blender is strongly based on usage of keyboard shortcuts and not menus, which makes it hard for new users to find the options they're looking for. In the last few years, more menu-driven interfaces have been added. It's not in the scope of this article to teach you everything about the modeling tools that can be used with Papervision3D. This also counts for Blender. There are many resources such as online tutorials and books that cover how to work with Blender. A link to the Blender installer download can be found on its web site: www.blender.org.
Read more
  • 0
  • 0
  • 2020

article-image-using-animated-pieces-board-based-game-xna-40
Packt
30 Sep 2010
14 min read
Save for later

Using Animated Pieces in a Board-based Game with XNA 4.0

Packt
30 Sep 2010
14 min read
  XNA 4.0 Game Development by Example: Beginner's Guide Create your own exciting games with Microsoft XNA 4.0 Dive headfirst into game creation with XNA Four different styles of games comprising a puzzler, a space shooter, a multi-axis shoot 'em up, and a jump-and-run platformer Games that gradually increase in complexity to cover a wide variety of game development techniques Focuses entirely on developing games with the free version of XNA Packed with many suggestions for expanding your finished game that will make you think critically, technically, and creatively Fresh writing filled with many fun examples that introduce you to game programming concepts and implementation with XNA 4.0 A practical beginner's guide with a fast-paced but friendly and engaging approach towards game development Read more about this book (For more resources on XNA 4.0, see here.) Animated pieces We will define three different types of animated pieces: rotating, falling, and fading. The animation for each of these types will be accomplished by altering the parameters of the SpriteBatch.Draw() call. Classes for animated pieces In order to represent the three types of animated pieces, we will create three new classes. Each of these classes will inherit from the GamePiece class, meaning they will contain all of the methods and members of the GamePiece class, but add additional information to support the animation. Child classesChild classes inherit all of their parent's members and methods. The RotatingPiece class can refer to the pieceType and suffix of the piece without recreating them within RotatingPiece itself. Additionally, child classes can extend the functionality of their base class, adding new methods and properties or overriding old ones. In fact, Game1 itself is a child of the Micrsoft.Xna.Game class, which is why all of the methods we use (Update(), Draw(), LoadContent(), and so on) are declared as "override". Let's begin by creating the class we will use for rotating pieces. Time for action – rotating pieces Open your existing Flood Control project in Visual C# Express if it is not already active. Add a new class to the project called "RotatingPiece". Add "using Microsoft.Xna.Framework;" to the using area at the top of the class. Update the declaration of the class to read class RotatingPiece : GamePiece. Add the following declarations to the RotatingPiece class: public bool clockwise;public static float rotationRate = (MathHelper.PiOver2 / 10);private float rotationAmount = 0;public int rotationTicksRemaining = 10; Add a property to retrieve the current rotation amount: public float RotationAmount{ get { if (clockwise) return rotationAmount; else return (MathHelper.Pi*2) - rotationAmount; }} Add a constructor for the RotatingPiece class: public RotatingPiece(string pieceType, bool clockwise) : base(pieceType){ this.clockwise = clockwise;} Add a method to update the piece: public void UpdatePiece(){ rotationAmount += rotationRate; rotationTicksRemaining = (int)MathHelper.Max(0, rotationTicksRemaining-1);} What just happened? In step 2, we modified the declaration of the RotatingPiece class by adding : GamePiece to the end of it. This indicates to Visual C# that the RotatingPiece class is a child of the GamePiece class. The clockwise variable stores a "true" value if the piece will be rotating clockwise and "false" if the rotation is counter-clockwise. When a game piece is rotated, it will turn a total of 90 degrees (or pi/2 radians) over 10 animation frames. The MathHelper class provides a number of constants to represent commonly used numbers, with MathHelper.PiOver2 being equal to the number of radians in a 90 degree angle. We divide this constant by 10 and store the result as the rotationRate for use later. This number will be added to the rotationAmount float, which will be referenced when the animated piece is drawn. Working with radians All angular math is handled in radians from XNA's point of view. A complete (360 degree) circle contains 2*pi radians. In other words, one radian is equal to about 57.29 degrees. We tend to relate to circles more often in terms of degrees (a right angle being 90 degrees, for example), so if you prefer to work with degrees, you can use the MathHelper.ToRadians() method to convert your values when supplying them to XNA classes and methods. The final declaration, rotationTicksRemaining, is reduced by one each time the piece is updated. When this counter reaches zero, the piece has finished animating. When the piece is drawn, the RotationAmount property is referenced by a spriteBatch. Draw() call and returns either the rotationAmount property (in the case of a clockwise rotation) or 2*pi (a full circle) minus the rotationAmount if the rotation is counter-clockwise. The constructor in step 7 illustrates how the parameters passed to a constructor can be forwarded to the class' parent constructor via the :base specification. Since the GamePiece class has a constructor that accepts a piece type, we can pass that information along to its constructor while using the second parameter (clockwise) to update the clockwise member that does not exist in the GamePiece class. In this case, since both the clockwise member and the clockwise parameter have identical names, we specify this.clockwise to refer to the clockwise member of the RotatingPiece class. Simply clockwise in this scope refers only to the parameter passed to the constructor. this notationYou can see that it is perfectly valid C# code to have method parameter names that match the names of class variables, thus potentially hiding the class variables from being used in the method (since referring to the name inside the method will be assumed to refer to the parameter). To ensure that you can always access your class variables even when a parameter name conflicts, you can preface the variable name with this. when referring to the class variable. this. indicates to C# that the variable you want to use is part of the class, and not a local method parameter. Lastly, the UpdatePiece() method simply increases the rotationAmount member while decreasing the rotationTicksRemaining counter (using MathHelper.Max() to ensure that the value does not fall below zero). Time for action – falling pieces Add a new class to the Flood Control project called "FallingPiece". Add using Microsoft.Xna.Framework; to the using area at the top of the class. Update the declaration of the class to read class FallingPiece : GamePiece Add the following declarations to the FallingPiece class: public int VerticalOffset;public static int fallRate = 5; Add a constructor for the FallingPiece class: public FallingPiece(string pieceType, int verticalOffset) : base(pieceType){ VerticalOffset = verticalOffset;} Add a method to update the piece: public void UpdatePiece(){ VerticalOffset = (int)MathHelper.Max( 0, VerticalOffset - fallRate);} What just happened? Simpler than a RotatingPiece, a FallingPiece is also a child of the GamePiece class. A falling piece has an offset (how high above its final destination it is currently located) and a falling speed (the number of pixels it will move per update). As with a RotatingPiece, the constructor passes the pieceType parameter to its base class constructor and uses the verticalOffset parameter to set the VerticalOffset member. Note that the capitalization on these two items differs. Since VerticalOffset is declared as public and therefore capitalized by common C# convention, there is no need to use the "this" notation, since the two variables technically have different names. Lastly, the UpdatePiece() method subtracts fallRate from VerticalOffset, again using the MathHelper.Max() method to ensure the offset does not fall below zero. Time for action – fading pieces Add a new class to the Flood Control project called "FadingPiece". Add using Microsoft.Xna.Framework; to the using area at the top of the class. Update the declaration of the class to read class FadingPiece : GamePiece Add the following declarations to the FadingPiece class: public float alphaLevel = 1.0f;public static float alphaChangeRate = 0.02f; Add a constructor for the FadingPiece class: public FadingPiece(string pieceType, string suffix) : base(pieceType, suffix){} Add a method to update the piece: public void UpdatePiece(){alphaLevel = MathHelper.Max( 0, alphaLevel - alphaChangeRate);} What just happened? The simplest of our animated pieces, the FadingPiece only requires an alpha value (which always starts at 1.0f, or fully opaque) and a rate of change. The FadingPiece constructor simply passes the parameters along to the base constructor. When a FadingPiece is updated, alphaLevel is reduced by alphaChangeRate, making the piece more transparent. Managing animated pieces Now that we can create animated pieces, it will be the responsibility of the GameBoard class to keep track of them. In order to do that, we will define a Dictionary object for each type of piece. A Dictionary is a collection object similar to a List, except that instead of being organized by an index number, a dictionary consists of a set of key and value pairs. In an array or a List, you might access an entity by referencing its index as in dataValues[2] = 12; With a Dictionary, the index is replaced with your desired key type. Most commonly this will be a string value. This way, you can do something like fruitColors["Apple"]="red"; Time for action – updating GameBoard to support animated pieces In the declarations section of the GameBoard class, add three dictionaries: public Dictionary<string, FallingPiece> fallingPieces = new Dictionary<string, FallingPiece>();public Dictionary<string, RotatingPiece> rotatingPieces = new Dictionary<string, RotatingPiece>();public Dictionary<string, FadingPiece> fadingPieces = new Dictionary<string, FadingPiece>(); Add methods to the GameBoard class to create new falling piece entries in the dictionaries: public void AddFallingPiece(int X, int Y, string PieceName, int VerticalOffset){ fallingPieces[X.ToString() + "_" + Y.ToString()] = new FallingPiece(PieceName, VerticalOffset);}public void AddRotatingPiece(int X, int Y,string PieceName, bool Clockwise){ rotatingPieces[X.ToString() + "_" + Y.ToString()] = new RotatingPiece(PieceName, Clockwise);}public void AddFadingPiece(int X, int Y, string PieceName){ fadingPieces[X.ToString() + "_" + Y.ToString()] = new FadingPiece(PieceName,"W");} Add the ArePiecesAnimating() method to the GameBoard class: { if ((fallingPieces.Count == 0) && (rotatingPieces.Count == 0) && (fadingPieces.Count == 0)) { return false; } else { return true; }} Add the UpdateFadingPieces() method to the GameBoard class: private void UpdateFadingPieces(){ Queue<string> RemoveKeys = new Queue<string>(); foreach (string thisKey in fadingPieces.Keys) { fadingPieces[thisKey].UpdatePiece(); if (fadingPieces[thisKey].alphaLevel == 0.0f) RemoveKeys.Enqueue(thisKey.ToString()); } while (RemoveKeys.Count > 0) fadingPieces.Remove(RemoveKeys.Dequeue());} Add the UpdateFallingPieces() method to the GameBoard class: private void UpdateFallingPieces(){ Queue<string> RemoveKeys = new Queue<string>(); foreach (string thisKey in fallingPieces.Keys) { fallingPieces[thisKey].UpdatePiece(); if (fallingPieces[thisKey].VerticalOffset == 0) RemoveKeys.Enqueue(thisKey.ToString()); } while (RemoveKeys.Count > 0) fallingPieces.Remove(RemoveKeys.Dequeue());} Add the UpdateRotatingPieces() method to the GameBoard class: private void UpdateRotatingPieces(){ Queue<string> RemoveKeys = new Queue<string>(); foreach (string thisKey in rotatingPieces.Keys) { rotatingPieces[thisKey].UpdatePiece(); if (rotatingPieces[thisKey].rotationTicksRemaining == 0) RemoveKeys.Enqueue(thisKey.ToString()); } while (RemoveKeys.Count > 0) rotatingPieces.Remove(RemoveKeys.Dequeue());} Add the UpdateAnimatedPieces() method to the GameBoard class: public void UpdateAnimatedPieces(){ if (fadingPieces.Count == 0) { UpdateFallingPieces(); UpdateRotatingPieces(); } else { UpdateFadingPieces(); }} What just happened? After declaring the three Dictionary objects, we have three methods used by the GameBoard class to create them when necessary. In each case, the key is built in the form "X_Y", so an animated piece in column 5 on row 4 will have a key of "5_4". Each of the three Add... methods simply pass the parameters along to the constructor for the appropriate piece types after determining the key to use. When we begin drawing the animated pieces, we want to be sure that animations finish playing before responding to other input or taking other game actions (like creating new pieces). The ArePiecesAnimating() method returns "true" if any of the Dictionary objects contain entries. If they do, we will not process any more input or fill empty holes on the game board until they have completed. The UpdateAnimatedPieces() method will be called from the game's Update() method and is responsible for calling the three different update methods above (UpdateFadingPiece(), UpdateFallingPiece(), and UpdateRotatingPiece()) for any animated pieces currently on the board. The first line in each of these methods declares a Queue object called RemoveKeys. We will need this because C# does not allow you to modify a Dictionary (or List, or any of the similar "generic collection" objects) while a foreach loop is processing them. A Queue is yet another generic collection object that works like a line at the bank. People stand in a line and await their turn to be served. When a bank teller is available, the first person in the line transacts his/her business and leaves. The next person then steps forward. This type of processing is known as FIFO, or First In, First Out. Using the Enqueue() and Dequeue() methods of the Queue class, objects can be added to the Queue (Enqueue()) where they await processing. When we want to deal with an object, we Dequeue() the oldest object in the Queue and handle it. Dequeue() returns the first object waiting to be processed, which is the oldest object added to the Queue. Collection classes C# provides a number of different "collection" classes, such as the Dictionary, Queue, List, and Stack objects. Each of these objects provides different ways to organize and reference the data in them. For information on the various collection classes and when to use each type, see the following MSDN entry: http://msdn.microsoft.com/en-us/library/6tc79sx1(VS.80).aspx Each of the update methods loops through all of the keys in its own Dictionary and in turn calls the UpdatePiece() method for each key. Each piece is then checked to see if its animation has completed. If it has, its key is added to the RemoveKeys queue. After all of the pieces in the Dictionary have been processed, any keys that were added to RemoveKeys are then removed from the Dictionary, eliminating those animated pieces. If there are any FadingPieces currently active, those are the only animated pieces that UpdateAnimatedPieces() will update. When a row is completed, the scoring tiles fade out, the tiles above them fall into place, and new tiles fall in from above. We want all of the fading to finish before the other tiles start falling (or it would look strange as the new tiles pass through the fading old tiles). Fading pieces In the discussion of UpdateAnimatedPieces(), we stated that fading pieces are added to the board whenever the player completes a scoring chain. Each piece in the chain is replaced with a fading piece. Time for action – generating fading pieces In the Game1 class, modify the CheckScoringChain() method by adding the following call inside the foreach loop before the square is set to "Empty": gameBoard.AddFadingPiece( (int)ScoringSquare.X, (int)ScoringSquare.Y, gameBoard.GetSquare( (int)ScoringSquare.X, (int)ScoringSquare.Y)); What just happened? Adding fading pieces is simply a matter of getting the square (before it is replaced with an empty square) and adding it to the FadingPieces dictionary. We need to use the (int) typecasts because the ScoringSquare variable is a Vector2 value, which stores its X and Y components as floats. Falling pieces Falling pieces are added to the game board in two possible locations: From the FillFromAbove() method when a piece is being moved from one location on the board to another, and in the GenerateNewPieces() method, when a new piece falls in from the top of the game board. Time for action – generating falling pieces Modify the FillFromAbove() method of the GameBoard class by adding a call to generate falling pieces right before the rowLookup = -1; line: AddFallingPiece(x, y, GetSquare(x, y), GamePiece.PieceHeight *(y-rowLookup)); Update the GenerateNewPieces() method by adding the following call right after the RandomPiece(x,y) line: AddFallingPiece(x, y, GetSquare(x, y), GamePiece.PieceHeight * GameBoardHeight); What just happened? When FillFromAbove() moves a piece downward, we now create an entry in the FallingPieces dictionary that is equivalent to the newly moved piece. The vertical offset is set to the height of a piece (40 pixels) times the number of board squares the piece was moved. For example, if the empty space was at location 5,5 on the board, and the piece above it (5,4) is being moved down one block, the animated piece is created at 5,5 with an offset of 40 pixels (5-4 = 1, times 40). When new pieces are generated for the board, they are added with an offset equal to the height (in pixels) of the game board, determined by multiplying the GamePiece.PieceHeight value by the GameBoardHeight. This means they will always start above the playing area and fall into it.
Read more
  • 0
  • 0
  • 2006
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-dragging-ccnode-cocos2d-swift
Packt
21 Jan 2015
6 min read
Save for later

Dragging a CCNode in Cocos2D-Swift

Packt
21 Jan 2015
6 min read
 In this article by Ben Trengrove, author of the book Cocos2D Game Development Essentials, we will see how can we update our sprite position according to the touch movement. (For more resources related to this topic, see here.) Very often in development with Cocos2d you will want the ability to drag a node around the screen. It is not a built in behavior but it can be easily coded. To do it you will need to track the touch information. Using this information you will move the sprite to the updated position anytime the touch moves. Lets get started. Add a new Boolean property to your private interface. @interface HelloWorldScene ()@property (nonatomic, assign) BOOL dragging;@end Now, add the following code to the touchBegan method. -(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {  CGPoint touchLoc = [touch locationInNode:self];  if (CGRectContainsPoint(_sprite.boundingBox, touchLoc)) {    self.dragging = YES;    NSLog(@"Start dragging");  }} Add a touchMoved method with the following code. - (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event {  CGPoint touchLoc = [touch locationInNode:self];  if (self.dragging) {    _sprite.position = touchLoc;  }} What is being done in these methods is first you check to see if the initial touch was inside the sprite. If it was, we set a Boolean to say that the user is dragging the node. They have in effect picked up the node. Next in the touchMoved method, it is as simple as if the user did touch down on the node and move, set the new position of the node to the touch location. Next we just have to implement the letting go of the sprite. This is done in touchEnded. Implement the touchEnded method as follows. - (void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event {  self.dragging = NO;} Now, if you build and run the app you will be able to drag around the sprite. There is one small problem however, if you don't grab the sprite in its center you will see that the node snaps its center to the touch. What you really want to happen is just move from where on the node it was touched. You will make this adjustment now. To make this fix you are going to have to calculate the offset on the initial touch from the nodes center point. This will be stored and applied to the final position of the node in touchMoved. Add another property to your private interface. @property (nonatomic, assign) CGPoint dragOffset; Modify your touchBegan method to the following: -(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {  CGPoint touchLoc = [touch locationInNode:self];  CGPoint touchOffset = [touch locationInNode:_sprite];  if (CGRectContainsPoint(_sprite.boundingBox, touchLoc)) {    self.dragging = YES;    NSLog(@"Start dragging");    self.dragOffset = touchOffset;  }} Notice that using the locationinnode method, you can calculate the position of the touch relative to the node. This information is only useful if the touch was indeed inside of the node so you only store it if that is the case. Now, modify your touchMoved method to the following: - (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event {  CGPoint touchLoc = [touch locationInNode:self];  //Check if we are already dragging  if (self.dragging) {    CGPoint offsetPosition = ccpSub(touchLoc, self.dragOffset);//Calculate an offset to account for the anchor point        CGPoint anchorPointOffset = CGPointMake(_sprite.anchorPoint.x * _sprite.boundingBox.size.width, _sprite.anchorPoint.y * _sprite.boundingBox.size.height);//Add the offset and anchor point adjustment together to get the final position    CGPoint positionWithAnchorPoint = ccpAdd(offsetPosition, anchorPointOffset);    _sprite.position = positionWithAnchorPoint;  }} The offset position is subtracted from the touch location using the Cocos2d convenience function ccpSub. CcpSub subtracts a point from another point. Using the anchor point and size of the sprite, an adjustment is calculated to account for different anchor points. Once these two points have been calculated, they are added together to create a final sprite position. Build and run the app now, you will now have a very natural dragging mechanic. For reference, here is the complete scene. @interface HelloWorldScene ()@property (nonatomic, assign) BOOL dragging;@property (nonatomic, assign) CGPoint dragOffset;@end- (id)init{  // Apple recommend assigning self with supers return value  self = [super init];  if (!self) return(nil);  // Enable touch handling on scene node  self.userInteractionEnabled = YES;  // Create a colored background (Dark Grey)  CCNodeColor *background = [CCNodeColor nodeWithColor:[CCColor colorWithRed:0.2f green:0.2f blue:0.2f alpha:1.0f]];  [self addChild:background];  // Add a sprite  _sprite = [CCSprite spriteWithImageNamed:@"Icon-72.png"];  _sprite.position  = ccp(self.contentSize.width/2,self.contentSize.height/2);  _sprite.anchorPoint = ccp(0.5, 0.5);  [self addChild:_sprite];  // Create a back button  CCButton *backButton = [CCButton buttonWithTitle:@"[ Menu ]" fontName:@"Verdana-Bold" fontSize:18.0f];  backButton.positionType = CCPositionTypeNormalized;  backButton.position = ccp(0.85f, 0.95f); // Top Right of screen  [backButton setTarget:self selector:@selector(onBackClicked:)];  [self addChild:backButton];  // donereturn self;}// -----------------------------------------------------------------------#pragma mark - Touch Handler// ------------------------------------------------------------------------(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {  CGPoint touchLoc = [touch locationInNode:self];  CGPoint touchOffset = [touch locationInNode:_sprite];  if (CGRectContainsPoint(_sprite.boundingBox, touchLoc)) {    self.dragging = YES;    NSLog(@"Start dragging");    self.dragOffset = touchOffset;  }}- (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event {  CGPoint touchLoc = [touch locationInNode:self];  if (self.dragging) {    CGPoint offsetPosition = ccpSub(touchLoc, self.dragOffset);    CGPoint anchorPointOffset = CGPointMake(_sprite.anchorPoint.x * _sprite.boundingBox.size.width, _sprite.anchorPoint.y * _sprite.boundingBox.size.height);    CGPoint positionWithAnchorPoint = ccpAdd(offsetPosition, anchorPointOffset);    _sprite.position = positionWithAnchorPoint;  }}- (void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event {  self.dragging = NO;} Summary In this article, we saw how to update your sprite position according to the touch movement. Resources for Article: Further resources on this subject: Why should I make cross-platform games? [article] Animations in Cocos2d-x [article] Moving the Space Pod Using Touch [article]
Read more
  • 0
  • 0
  • 1985

article-image-building-events
Packt
22 Oct 2013
8 min read
Save for later

Building Events

Packt
22 Oct 2013
8 min read
(For more resources related to this topic, see here.) Building a collision event system In a game such as Angry Birds, we would want to know when a breakable object such as a pig or piece of wood has collided with something, so that we can determine the amount of damage that was dealt, and whether or not the object should be destroyed, which in turn spawns some particle effects and increments the player's score. It's the game logic's job to distinguish between the objects, but it's the physics engine's responsibility to send these events in the first place and then we can extract this information from Bullet through its persistent manifolds. Continue from here using the Chapter6.1_ CollisionEvents project files. Explaining the persistent manifolds Persistent manifolds are the objects that store information between pairs of objects that pass the broad phase. If we remember our physics engine theory the broad phase returns a shortlist of the object pairs that might be touching, but are not necessarily touching. They could still be a short distance apart from one another, so the existence of a manifold does not imply a collision. Once you have the manifolds, there's still a little more work to do to verify if there is a collision between the object pair. One of the most common mistakes made with the Bullet physics engine is to assume that the existence of a manifold is enough to signal a collision. This results in detecting collision events a couple of frames too early (while the objects are still approaching one another) and detecting separation events too late (once they've separated far enough away that they no longer pass the broad phase). This often results in a desire to blame Bullet for being sluggish, when the fault lies with the user's original assumptions. Be warned! Manifolds reside within the collision dispatcher, and Bullet keeps the same manifolds in memory for as long as the same object pairs keep passing the broad phase. This is useful if you want to keep querying the same contact information between pairs of objects over time. This is where the persistent part comes in, which serves to optimize the memory allocation process by minimizing how often the manifolds are created and destroyed. Bullet is absolutely riddled with subtle optimizations and this is just one of them. This is all the more reason to use a known good physics solution like Bullet, instead of trying to take on the world and building your own! The manifold class in question is btPersistentManifold and we can gain access to the manifold list through the collision dispatcher's getNumManifolds() and getManifoldByIndexInternal() functions. Each manifold contains a handful of different functions and member variables to make use of, but the ones we're most interested in for now are getBody0(), getBody1(), and getNumContacts(). These functions return the two bodies in the object pair that passed the broad phase, and the number of contacts detected between them. We will use these functions to verify if a collision has actually taken place, and send the involved objects through an event. Managing the collision event There are essentially two ways to handle collision events: either send an event every update while two objects are touching (and continuously while they're still touching), or send events both when the objects collide and when the objects separate. In almost all cases it is wiser to pick the latter option, since it is simply an optimized version of the first. If we know when the objects start and stop touching, then we can assume that the objects are still touching between those two moments in time. So long as the system also informs us of peculiar cases in separation (such as if one object is destroyed, or teleports away while they're still touching), then we have everything we need for a collision event system. Bullet strives to be feature-rich, but also flexible, allowing us to build custom solutions to problems such as this; so this feature is not built into Bullet by default. In other words, we will need to build this logic ourselves. Our goals are simple; determine if a pair of objects have either collided or separated during the step, and if so, broadcast the corresponding event. The basic process is as follows: For each manifold, check if the two objects are touching (the number of contact points will be greater than zero). If so, add the pair to a list of pairs that we found in this step. If the same pair was not detected during the previous step, broadcast a collision event. Once we've finished checking the manifolds, create another list of collision objects that contains only the missing collision pairs between the previous step and this step. For each pair that is missing, broadcast a separation event. Overwrite the list of collision pairs from the previous step, with the list we created for this step. There are several STL (Standard Template Library) objects and functions we can use to make these steps easier. An std::pair can be used to store the objects in pairs, and can be stored within an std::set. These sets let us perform rapid comparisons between two sets using a helpful function, std::set_difference(). This function tells us the elements that are present in the first set, but not in the second. The following diagram shows how std::set_difference returns only objects pairs that are present in the first set, but missing from the second set. Note that it does not return new object pairs from the second set. The most important function introduced in this article's source code isCheckForCollisionEvents(). The code may look a little intimidating at first, but it simply implements the steps listed previously. The comments should help us to identify each step. When we detect a collision or separation, we will want some way to inform the game logic of it. These two functions will do the job nicely: virtual void CollisionEvent(btRigidBody* pBody0, btRigidBody * pBody1); virtual void SeparationEvent(btRigidBody * pBody0, btRigidBody * pBody1); In order to test this feature, we introduce the following code to turn colliding objects white (and similar code to turn separating objects black): void BulletOpenGLApplication::CollisionEvent(const btCollisionObject * pBody0, const btCollisionObject * pBody1) { GameObject* pObj0 = FindGameObject((btRigidBody*)pBody0); pObj0->SetColor(btVector3(1.0,1.0,1.0)); GameObject* pObj1 = FindGameObject((btRigidBody*)pBody1); pObj1->SetColor(btVector3(1.0,1.0,1.0)); } Note that these color changing commands are commented out in future project code. When we launch the application, we should expect colliding and separating objects to change to the colors give in CollisionEvent(). Colliding objects should turn white, and separated objects should turn black. But, when objects have finished moving, we observe something that might seem a little counterintuitive. The following screenshot shows the two objects colored differently once they come to rest: But, if we think about the order of events for a moment, it begins to make sense: When the first box collides with the ground plane, this turns both objects (the box and the ground plane) white The second box then collides with the first turning the second box white, while the first box stays white. Next, the second box separates from the first box, meaning both objects turn black. Finally, the second box collides with the ground plane, turning the box white once again. What was the last color that the first box turned to? The answer is black, because the last event it was involved in was a separation with the second box. But, how can the box be black if it's touching something? This is an intentional design consequence of this particular style of collision event management; one where we only recognize the collision and separation events. If we wanted objects to remember that they're still touching something, we would have to introduce some internal method of counting how many objects they're still in contact with, and incrementing/decrementing the count each time a collision or separation event comes along. This naturally consumes a little memory and processing time, but it's certainly far more optimized than the alternative of spamming a new collision event every step while two objects are still touching. We want to avoid wasting CPU cycles telling ourselves information that we already know. The CollisionEvent() and SeparationEvent() functions can be used by a game logic to determine if, when, and how two objects have collided. Since they hand over the rigid bodies involved in the collision, we can determine all kinds of important physics information, such as the points of contact (where they hit), and the difference in velocity/impulse force of the two bodies (how hard they hit). From there we can construct pretty much whatever physics collision-related game logic we desire. Try picking up, or introducing more objects with the left/right mouse buttons, causing further separations and collisions until you get a feel for how this system works. Summary Very little game logic can be built around a physics engine without a collision event system, so we made Bullet broadcast collision and separation events to our application so that it can be used by our game logic. This works by checking the list of manifolds, and creating logic that keeps track of important changes in these data structures. Resources for Article: Further resources on this subject: Flash Game Development: Making of Astro-PANIC! [Article] 2D game development with Monkey [Article] Developing Flood Control using XNA Game Development [Article]
Read more
  • 0
  • 0
  • 1943

article-image-preparing-your-scene
Packt
21 Mar 2014
10 min read
Save for later

Preparing Your Scene

Packt
21 Mar 2014
10 min read
(For more resources related to this topic, see here.) Element scenes in After Effects Element 3D scenes, unfortunately, are currently limited to five object groups. An object group is a collection of geometrical objects that is animated together. Under some circumstances, you may have multiple objects in a group, but not in this example. As a result, much of this article is devoted to tricks to overcome the limitations in Element 3D. We're going to cover the following topics: Saving your objects to your library so that they may be used in other instances of the Element plugin Setting up your objects in the After Effects interface and generating null objects to animate them easily Setting up your camera and lights in the scene and creating a new null object to create a focal point for the depth of field Let's get started! Saving your objects First things first: Let's create a folder in your Model Browser window. Navigate to your default objects directory for Element 3D (Documents/VideoCopilot/Models). Create a folder called BookModels inside that directory. Now, open your AEX project and go into the Scene Setup window in Element 3D. Your folder won't show up in the Model Browser (because there are no objects in that folder just yet). Let's save the lampshade glass to the presets now. The following screenshot shows the interface for saving your objects: Right-click on the lampshade glass object and select Save Model Preset. Now, you will see the directory we created. Unfortunately, there is no way to create a new folder from within this window yet. (Hence the need to navigate to the directory using your Windows Explorer (or Finder on Mac) to create the directory first.) Save all of your objects to this directory. Save them as shown in the following list: lampshade(glass) Lamp(base) Table WineBottle PepperShaker SaltShaker Note that we created a pepper shaker (in addition to our salt shaker). This was done by saving the salt shaker, then changing the texture do something darker, and saving it again as a pepper shaker. This can be great help when creating slight variants in your objects. The following screenshot shows how your Model Browser window should look: Preparing our scene Now that we have all our objects saved as presets, we can start populating our scene. Start off by deleting all your objects from the scene (click on the small X on the right side of the object bar in the Scene window). Don't worry. This is why we set them up as presets. Now we can bring them back in the right order. Setting up the lamp Let's start with the lamp. We'll be putting the lamp back in its lampshade and adding bulbs to the sockets. To do this, let's execute the following steps: In your Model Browser window, click on your lamp, then your lampshade, navigate to your starter pack, and click on bulb_on. Then in your Scene window, put the lamp in group 1, the shade in group 2, and the bulbs in group 3, as shown in the following screenshot: But wait. There's more! We have one bulb for four light sockets (it's a pretty huge bulb). That's okay! Element 3D wasn't really designed as a full 3D animation package. This is probably why it still has some of the limitations it does. Instead, it was designed as a way to use 3D objects as particles and replicate them in interesting ways. We'll get more into these features in the advanced sections, but it's time to get your feet wet by replicating the bulbs. Replicating the bulbs The shade and lamp should appear perfectly together (as if they were modeled together). The bulb is a stock asset of Element 3D, so it must be adjusted using the following steps: First, let's create a camera (so that we can accurately look around). Create a camera layer called Camera 1 using the stock's 50 mm preset. Remember, you can move your camera around using the camera tool (press C on your keyboard, then the mouse buttons will let you move your view around). Position your camera over the lamp so that we can see through the lampshades. Now, open the effect controls again for the ElementWineAndLamp layer. We don't need to touch anything in group 1 or 2 (that is, our lampshade and lamp). Since we created a symmetrical lamp, all we need to do is position one bulb and replicate it (like an array). Positioning and replicating an object Open Group 3 and then the Particle Replicator option. If we set the replicator shape to a plane, we will be able to replicate them all symmetrically on a plane. Pretty self explanatory, right? Let's set that first, as setting it later will change the coordinate system for our object, and as a result, it will look like it rests. Down the list, a little away from the Replicator Shape parameter are our Position XY, Position Z, and Scale Shape parameters. Mess with these to get your bulb directly in one of the bulb sockets. Then, you can turn the Particle Count parameter up to 4, and you'll end up with four bulbs positioned directly in the sockets. If they're not lined up, you're not out of luck! Directly under the Scale Shape parameter is Scale XYZ. It's important to note that Scale XYZ does not scale the object. Instead, it scales the replicator shape. There is no way in Element 3D to scale objects on their individual axis as of yet. Just remember, Scale Shape scales the geometry and Scale XYZ scales the replicator. You can also use the Particle Size parameter inside Particle Look to adjust object sizes. If you used your own lamp for this exercise, play around; you'll get it. If you downloaded the sample lamp, this screenshot shows the correct settings to get the bulbs into place. Don't worry if they're not exact. They aren't the hero objects in this animation. They're just there in case we see them either through the lampshades, or catch a glimpse. The result and setting should look similar to what is shown in the following screenshot: Lighting the lamp Luckily, lighting our scene is relatively easy, there's a lamp! Let's create an ambient light first. If our scene is going to be believable, we're going to need a blue light for ambient lighting. Create a new light with the type as Ambient, a light blue color, and an intensity of 10 %. Why so low? We want dramatic lighting in this scene. We'll keep the details without losing contrast with a very low ambient light. Call this light Ambient Light. The settings should look similar to what is shown in the following screenshot: So, why did we make it blue? Well, for a lamp to be on, it's probably night. If you look at any film footage, night is really denoted by shades of blue. Therefore, the light of night is blue. The same reason our lamp bulbs will have a bit of orangey-yellow to them. They're incandescent bulbs, which have a very warm look (also called color temperature). If you're going to be an animation god, you have to pay attention to everything around you: timings, lighting, and colors (actual colors and not just perceived colors). With all this in mind, let's create our first (of four) bulb lights by performing the following steps: Create a point light that is slightly orangey-yellow (be subtle) with an intensity of 100 percent. Don't worry just yet about any shadows or falloffs. These will actually be controlled within Element 3D. Name this light Light 1. The settings should look similar to what is shown in the following screenshot: Now, position this light directly on your first light bulb. Make sure you rotate your camera to ensure that it's placed properly. Once that's done, you can simply duplicate the light (Ctrl + D) and position the next on another bulb, either on the x or the z axis, and eventually have four lights (one on each bulb) in addition to your ambient light. The result should look like the following screenshot. Remember, lighting is the key to stunning looks. Always take the time to light your scenes well. The following screenshot shows the location of the lights directly on the bulbs: We'll finalize our lighting's falloff and the illusion of shadows in a bit. For now, let's move on. Adding the table and wine bottle Obviously, we're not going to be able to add the wine bottle, salt and pepper shakers, and the table to one layer of Element in AEX. Hence, we have the naming convention calling out the lamp and the bottle. We're going to break up our other objects across other layers within AEX. We'll get to that, but don't worry, there's a trick to do this. First, let's set up this layer. Place the table and move it (and scale it) using the techniques you already learned for positioning the first lamp. Position this table under the lamp. Do not move the lamp. Then, do the same with the wine bottle, placing it on top of the table. Use group 4 for the table and group 5 for the bottle. So far, your scene should look similar to what is shown in the following screenshot: Finishing the initial setup Now we have all of the five groups used up, but before we can add more objects (in other layers), we want to finalize our render settings (so we don't have to remember them and keep adjusting more Element layers). This will make all our layers look the same for rendering and give the illusion they were all on the same layer. Faking shadows Again, there is no ray tracing in Element 3D, and because the lights are native to AEX and they only project shadows on objects, AEX can see for itself that using AEX's shadow engine isn't an option. So we have to fake them. This can be done with something called ambient occlusion (AO). When the light goes into a corner, it has a hard time bouncing out again. This means that by nature, corners are darker than non-corners. This principal is called ambient occlusion. Element 3D does have this feature. So, by turning the AO way up, we can fake shadows. Some surfaces receive shadows better than others, so we'll have to play around a bit. Start by going into your E3D effect controls. Under Render Settings, you'll see Ambient Occlusion. Tick the Enable AO checkbox. Also, turn the intensity up to 10 and crank up the samples to 50. The sample amount is simple: the larger the number, the smoother the AO. However, the smoother the AO, the longer the render time. Crank up the AO Radius option to 3.2 and the AO Gamma option to 3.2. Watch what happens on your Preview window while you do this. Your settings should look similar to what is shown in the following screenshot: Metals don't receive shadows very well, so let's go turn it down on the gold. Go back into your scene settings, and on the gold shader (down in the advanced section), turn down your AO Amount option to 0.42. The final result should look similar to what is shown in the following screenshot: Light falloff When a candle is very bright at its flame but against a far wall, the wall is lit dimly. This effect is true with light bulbs as well. This principle is called Light Falloff. To add some realism and dramatic lighting to our scene, let's adjust this setting. Inside Render Settings in the Element effect controls, you'll see Lighting. Turn the Light Falloff setting up to 2.49. The larger this number, the more severe the falloff. The result should look similar to what is shown in the following screenshot:
Read more
  • 0
  • 0
  • 1892
article-image-game-publishing
Packt
23 Sep 2013
14 min read
Save for later

Game Publishing

Packt
23 Sep 2013
14 min read
(For more resources related to this topic, see here.) Manifest file Many application details are specified in the manifest file. Thus, we will modify it to set the correct application name, description, as well as choose suitable tile images. we can adjust these settings on the Application UI page in the Manifest Designer. Basic configuration As presented in the following screenshot, set the Display Name field to Space Aim 3D, and adjust the Description field as well. What is more, we should choose the suitable App Icon, which is an image with size 100 x 100 pixels. It represents our application, thus we should provide the game with a proper icon that the user can easily recognize. In the exemplary game, the icon shows the planet and a few asteroids, which are the main elements in the game. What is more, the image contains the game title. It is important, because we clear the content of the Tile Title setting. Thus the user will not see additional small text with a name of the application, after pinning the tile to the Start screen. Tiles Apart from some basic settings, we can also choose tile images. They will be shown on the default tile when the player taps the game to the Start screen. we can also create secondary tiles, which could navigate to particular locations inside the application. However, such a solution is not shown in this article. The Windows Phone 8 platform supports three kinds of tile templates: flip, iconic, and cycle. They differ by a way of presenting content. Of course, we can select a suitable one also in the Manifest Designer by choosing an option from the Tile Template list. The flip tile template is the default option, which allows we to present two sides of the tile and flip between them. Thus, we can present the game logo on the front side and some more information on the other side. The tile flips automatically. we can specify settings regarding the background images, titles shown on the front and back sides, the content presented on the other side, as well as the number displayed on the tile. The iconic tile template shows the content in a bit different way. Here, a small icon is used to present the application, together with an optional number, for example, regarding received messages in our project. Of course, we can set some properties, including the tile title, the image, as well as the background color. The cycle tile template is the last available type, which makes it possible to present up to nine images that are changed automatically. Thus, it can be a suitable way to present a tile for the application which works with many images. In case of the cycle tile template, we can also adjust a few settings, such as the title or the shown images. Tiles can be available in three sizes: small (159 x 159 pixels), medium (336 x 336), and large (691 x 336). The small and medium ones are mandatory, while the large one is optional. With the usage of the Manifest Designer, it is very easy to prepare the basic version of the default tile for the application, just by selecting a suitable template type and choosing images which should be shown on the tile, depending on its size. we can also enable or disable the support for a large tile. After adjusting the tiles, we may receive a result as shown in the following screenshot: As we can see, the tile is presented in three various sizes: small, medium, and large. What is important, we do not just rescale the image, but provide three separate images in particular sizes, which look good regardless of the tile size. Here, we can also find the Space Aim 3D shortcut in the application list. However, we can expect that it appears in the Games group instead. Fortunately, we do not need to worry about it, because after downloading the game from the store, its shortcut will be shown in the proper place. As we could remember, the flip data template, which we chose, can present two sides of the tile, but now we do not see this effect. Thus, in the following part of this section we will learn how to configure the default tile to support flipping and how to set the background image and the content at the back. To do so, we should open the WMAppManifest.xml file, but not in the Manifest Designer. we need to choose the View Code option from the context menu of this file. What is interesting, the XML code contains information that we had set in a graphical way. Thus, it can be an additional way of adjusting some settings and adding more complex features, which are not supported directly by the Manifest Designer. The TemplateFlip node, specified in the WMAppManifest.xml file, is as follows: <TemplateFlip> <SmallImageURI (...)> (...) </SmallImageURI> <Count>0</Count> <BackgroundImageURI (...)> (...) </BackgroundImageURI> <Title></Title> <BackContent>Let's avoid asteroids and reach the target planet!</BackContent> <BackBackgroundImageURI></BackBackgroundImageURI> <BackTitle>Space Aim 3D</BackTitle> <LargeBackgroundImageURI (...)> (...) </LargeBackgroundImageURI> <LargeBackContent>Let's avoid asteroids, reach the target planet, and see players in the vicinity!</LargeBackContent> <LargeBackBackgroundImageURI(...)></LargeBackBackgroundImageURI> <DeviceLockImageURI></DeviceLockImageURI> <HasLarge>True</HasLarge> </TemplateFlip> Here, we specify the BackContent, BackTitle, and LargeBackContent elements to adjust a way of presenting the back side of the tile. The first setting is a string which will be displayed on the other side of the medium-sized tile. The second setting is the title shown on the back side (regardless of the tile size), while the other is a string shown on the large tile. When we deploy the application to the emulator or the phone, and tap it to the start screen, we should see that the tile flips automatically, and it should present some additional content on the back side. What is more, it differs depending on the tile size, as shown in the following screenshots: The Windows Phone 8 platform also supports live tiles, which present the content received from the Internet and are updated automatically. Such a kind of tiles can use either the push notifications or the local notifications. Remaining settings we have completed the basic configuration of the application, as well as learned how to set up the default tile. However, a few other modifications are necessary on the Packaging tab. Here, we specify the author and publisher data, as well as a version of the application. Other tabs (Capabilities and Requirements) remain unchanged, because we made the required modifications earlier. Rating by the users While designing the web screen, we created the button which should allow the user to rate the game. we can easily implement this functionality with the MarketplaceReviewTask class, which is another launcher used in the exemplary game. we need to modify the Rate method of the webViewModel class, as shown in the following code snippet: private void Rate() { MarketplaceReviewTask task = new MarketplaceReviewTask(); task.Show(); } Here, we create a new instance of the MarketplaceReviewTask class (from the Microsoft.Phone.Tasks namespace) and call the Show method. When this part of code is executed, the review page is opened, where the user can rate and review the current application. Release version The development environment for programming the Windows Phone 8 applications is equipped with many advanced features regarding their debugging. Such functionalities may require a bit different form of the code, which can be executed slower, but provide the developers with additional possibilities during development. For this reason, it is important to prepare the release version (retail) of the game before publishing. we can easily generate the release version of the .xap file (with the data of wer application) using the IDE. To do it, we should change two options located next to the green triangle, and a selection of the emulator or device, as shown in the following screenshot. Here, we should indicate that we want to use the Release mode, as well as the ARM platform. Then, we should select Build and Rebuild Solution options to generate the suitable version of the .xap file. Now, we can proceed to the process of testing our game and preparing for submission to the store! Store Test Kit Some testing operations are simplified by the Store Test Kit, which allows to perform a set of automatic and manual tests. They can be used to verify many requirements that should be met to accept the application in the store. we can open the tool by choosing the Open Store Test Kit option from the context menu of the SpaceAim3D project (not solution), or by choosing the Open Store Test Kit entry from the Project menu. The tool contains three pages: Application Details, Automated Tests, and Manual Tests. we will learn how to use all of them in this section. Application details In the first page we should specify some details regarding the application, including the store tile image with size 300 x 300 pixels. Apart from it, we need to choose a set of screenshots for each supported screen resolution. It is important to add at least one screenshot and not more than eight. Each of them should be provided in proper resolution, that is, 480 x 800 (WVGA), 768 x 1280 (WXGA), and 720 x 1280 (720P). The Application Details page is shown in the following screenshot: In case of applications running in the landscape mode (as in case of wer game), we should provide images that are not rotated, that is, as captured in the emulator. we can make screenshots either in the emulator (using the Screenshot tab in the Additional Tools window) or on the phone. In the latter case, we should press the Start and power buttons. When a screenshot is taken correctly, a shutter sound is played and the image is saved in a suitable album. The Application Package field is a read-only textbox, where a path to the .xap file is shown. It is worth mentioning that it indicates the Release version for ARM, thus it is exactly the same version as we generated earlier. Automated tests The second page is named Automated Tests. By clicking on the Run Tests button, we run a basic verification of wer project regarding the submission requirements, for example, whether the .xap file size is correct, as well as we provide the suitable icons and screenshots. If all tests are passed, we will receive a result as shown in the following screenshot. Otherwise, some additional notes are shown. In such a situation, we need to fix errors and run the tests once again. It is important to note that passing all automated tests does not mean that the application does not contain any errors which could prevent the project from being accepted in the store. Manual tests Additional verification can be performed manually, by using the Manual Tests tab in the Store Test Kit. Here, we have a list of test cases and their descriptions. we should follow them and manually indicate whether the test is passed or failed as shown in the following screenshot: Simulation Dashboard The exemplary game may be used in various conditions, often significantly different than our testing environment. For instance, the player can have a limited access to the Internet or switched off the location services. For this reason, we should try to test our project in many situations. Such a process can be simplified by the Simulation Dashboard tool. we can open it by choosing the Simulation Dashboard option from the Tools menu. Its window is shown in the following screenshot: This tool makes it possible to simulate some specific network conditions, by choosing a proper network speed and signal strength. we can choose 2G, 3G, 4G, Wi-Fi, and No Network options as the speed, as well as Good, Average, and Poor as the Signal Strength. Thus, we can check how our application behaves if we need to download some data from the Internet using the slower network connection, or even whether the game responds correctly in case of no network access. Apart from the network simulation, the Simulation Dashboard allows we to lock and unlock the screen just by choosing a suitable option from the Lock Screen group. The last supported testing feature is named Reminders. Just after pressing the Trigger Reminder button, the reminder is shown in the emulator. Thus, we can easily check how wer application reacts in such a situation. Windows Phone Application Analysis Apart from testing the project in real-world conditions, it is important to check its performance and try to eliminate problems in this area. Fortunately, the Windows Phone Application Analysis tool is integrated with the IDE, and we can use it to measure the performance of various parts of our game. The tool can be started by selecting the Start Windows Phone Application Analysis option from the Debug menu. After launching, we should have the Execution option selected, thus we can click on the Start Session (App will start) element. It automatically starts our game in the emulator, if it is selected as the target. Then, a process of collecting data is started, and we can use the application in various ways to check the performance of its several areas. For instance, we can start by spending some time on the Menu screen, then we launch the game and play ten levels, return to the menu, and open additional screens (such as Ranks, Map, or World). When we want to finish collecting performance data, we should click on the End Session (App will exit) option. Then, the process of collecting data stops and the report is created, which can take some time. The .sap file is generated for each analysis session. It is saved in the directory of the managed project, and can be later opened and analyzed using the IDE. The Windows Phone Application Analysis tool presents a lot of important data related to the game performance, including both the managed and the native parts. The graph, presented in the main part of the window, shows the CPU usage at a particular time, as shown in the following screenshot. we can easily analyze which part of our application causes performance issues, and we may try to improve them. As we can see in the preceding screenshot, the performance results for the Space Aim 3D game are expectable and reasonable. In case of the Menu screen, the CPU usage is very low. It grows significantly for the Game page, but here we need to perform a lot of operations, for example, related to rendering many objects in the 3D game world. What is interesting, the CPU usage grows slightly while consecutive levels, but also on the tenth level, the game works smoothly both in the emulator and on the phone. As soon as we exit to the main menu, the CPU usage decreases almost immediately. The remaining part is related to opening the following game screens: Ranks, Map, World, and others. The possibilities of the Windows Phone Application Analysis are not limited only to drawing the graph of the CPU usage. we can also see the Hot Path information, which lets us know what part of code uses the most processing power. In our case, it is the region that renders 3D objects on the screen, using Direct3D. What is more, we can click on a name of the particular function to open another view, which shows more details. By using the tool, we can even analyze the performance of particular lines of code, as shown in the following screenshot: Available features make it significantly easier to find a bottleneck causing performance problems. By running the Windows Phone Application Analysis tool multiple times, while making modifications in the code, we can see how wer changes reflect in the performance.
Read more
  • 0
  • 0
  • 1856

article-image-introduction-editing-operators-blender-sequel
Packt
29 Mar 2011
3 min read
Save for later

Introduction to the Editing Operators in Blender: A Sequel

Packt
29 Mar 2011
3 min read
  Blender 2.5 Materials and Textures Cookbook Over 80 great recipes to create life-like Blender objects         Read more about this book       (For more resources on Blender, see here.) Well, ready to go with a simple mouth structure, right? Then select the two lower vertex as in picture below. We'll extrude them in Z axis and then again in Y axis to finish scaling. First, just select the two lower vertices. (See illustration 12) Illustration 12: Modeling Stage After all past actions you should have something similar to this picture. Use the known select commands to select the two lower vertices. For manipulating the 3D View and checking your model at any perspective you want, use MMB (Middle Mouse Button). This will show you your model at any perspective allowing you to move around it. Also, you can probably notice that in the latest picture I have disabled the Transform Manipulator (those green, red and blue arrows). In the modeling stage they are not primordial to use if you get used to commands, but they will help you in other areas such as animation. Transform Manipulator function is to help in the Location, Rotation and Scale procedure while manipulating data (objects, meshes, vertex, edges, etc). You can disable/enable Transform Manipulator from the buttons close the Orientations (Global by default). Other way to do that is with Ctrl + Space bar Key. (See illustration 13) Illustration 13: Transform Manipulator Buttons You can manage the transformation mode with different manipulators. From left to side buttons are: Enable/Disable manipulators view, Translate manipulator mode, Rotate manipulator mode, and Scale manipulator mode. Extrude now the selected bit of mesh in the Z Axis. Then go to E Key and Z Axis after that to just extrude in the right axis. Make it small because this will be the start point to extrude the lower side of the mouth and the jaw. When you do that, extrude again two times in the Y Axis in negative normals, in other words pointing to the direction we started modeling. So, E Key and Y Axis (negative normals) and repeat it again. Make those extrudes to coincide with upper edges. After that, just scale a little bit with S Key. (See illustration 14) Illustration 14: Modeling Stage After all past actions you should have something similar to this picture. After some steps, we have something similar (more or less) to our model nose and mouth and you have learned all basics commands for successful and creative modeling. Just all you need to know at this point to develop interesting characters in your projects. Let's go to complete the jaw now. Select vertices as they are in the illustration below (See illustration 15). Go to Right View by pressing 3 Key in Numpad and extrude them to Z Axis, so press E Key to extrude and Z to tell Blender the right direction to extrude (doing so is useful to extrude in straight line according with axis, but is not required always, that just depends on the model). After that you should rotate the selected elements, so press R Key to rotate and you should have something similar to the illustration 18. (See illustration 18) Illustration 15: Modeling Stage After all past actions you should have something similar to this picture. Select these four vertices to extrude the mouth and the jaw. Illustration 16: Modeling Stage After all past actions you should have something similar to this picture. Previous selection after the rotation action.
Read more
  • 0
  • 0
  • 1840

Packt
18 Mar 2014
10 min read
Save for later

Introducing a new game type – top-down

Packt
18 Mar 2014
10 min read
(For more resources related to this topic, see here.) We want to start a new little game prototype. Let's create a little top-down shooter. For that reason, create a new application. For now, let's choose one of my favorite resolutions for retro games: 480 x 320. Here are the steps to create a basic top-down character: Click on Application in the workspace toolbar, choose Window in the properties, and select 480 x 320. You'll be asked to apply these settings. Your application's resolution has been set. Now set the size of your frame to 800 x 600 to create more space for your game objects. Create an active object, center hotspot, and action point. Set the movement of your object to Eight Directions, and change the properties to your desired values. You have just created a basic top-down character that you can steer with the arrow keys (or later with the touch joystick if you want to create a mobile game). Of course, you can also visit the Clickteam forum and search for a 360 degree movement example to get a more advanced movement system. In our case, the built-in eight-directions movement will do a great job though. Create one more Active object. This will be your bullet. Change the graphics if you want to. Let's create a simple shooting event: Repeat while "button 1" is pressed – Launch bullet in the direction "player" with a speed of 100 Too many bullets are created while pressing button 1 now. You can trigger the bullets every 10 seconds with conditions from the timer. Just add the following condition to the existing button condition: Every 10 seconds This event will create one bullet every 10 seconds while button 1 is pressed. One more thing you could do is center the display at your player's position to activate scrolling just like in the platformer example: Enemy movements As a next step, we want to create some basic enemy movements. Remember, this is just a prototype, so we don't really care about graphics. We just want to test different movements and events to get basic knowledge about Fusion. Path movement The simplest method to make your characters move might be on a path. Create an active object (an enemy), and set its movement to Path. Now hit the Edit button and place at least one node for a path. Also activate Reverse at End, and loop the movement in the Path Movement Setup. No matter what game type you are creating, the path movement can be used for a simple platformer enemy as well as for top-down spaceships. Bouncing ball movement The bouncing ball movement can be used for a million situations. The name that gives basic movement though would be another simple motion. Create an active object and set the movement to Bouncing Ball. Change the movement properties to whatever fits your game dynamic. We want our object to bounce whenever it leaves the game area. You will only need one event to trigger this situation. Start a new condition: navigate to Set Position | Test position of "enemy". Hit all the arrows pointing outside the frame area in the pop ( ) up that appears. This will create the condition Enemy leaves the play area. Now select your enemy object and create the action—navigate to Movement | Bounce: Just before your enemy object leaves the game area, it will bounce to a random direction within the frame. It will move forever within your game—until you blow it to pieces of course. Direction detection – a basic AI You can easily modify your bouncing ball enemy to follow your player object wherever it might go and create your first little Artificial Intelligence (AI). That actually sounds pretty cool, doesn't it? Use the bouncing ball movement for a new active object again. Set Speed to a low value such as 8. Go to the event editor and create the condition Always. Select your enemy object to create the action—navigate to Direction | Look in the direction of…— ( ) and select your player object. You should get this event: Always – Look at (0,0) from "player" The following screenshot shows the creation of the preceding event: Your enemy moves with a constant speed of 8 towards the player now! This might be the most simple AI you can create with Fusion. The great thing is it totally works for simple enemies! You can always dig deeper and create a more powerful movement system, but sometimes less is more. Especially when things need to be done quickly, you will be very happy about the built-in movements in Fusion! There are many, many, many different ways to work with the built-in movements in Fusion. There are just as many ways to create AI's for enemies and characters. You will get behind it step by step with every new game project! Alterable values At the moment, you have your frantic shooting player and a screen full of swirling enemy squares. Now we want to let them interact with each other. In the next steps, you will learn how to use alterable values, which are internal counters you can change and make calculations with. In your special case, you will use those values for the health of your enemies, but they can actually be used for pretty much any situation where you need to set values. Some examples are as follows: Reloading the shield of a spaceship Money, gold, or credits of a character Number of bullets for a weapon Health, energy, or life bars The easiest way to describe alterable values is with a simple example. We will give one of your enemies five health points, which is pretty nice of us. Select the enemy with path movement and go to the Values tab in the objects properties. Hit the New button to create Alterable Value A. Double-click on Alterable Value A name it PlayerHealth, and set the value to 5: Naming alterable values is not necessary but is highly recommended. Each object has up to 26 values (from A to Z) by the way. The following screenshot shows the naming of alterable values: Now open the event editor to create the interaction of your bullet and your enemy. You will need a simple event that reduces the alterable value HealthBar by 1 whenever it gets hit by one of your bullets: Collision between "enemy" and "bullet" - Sub 1 from "HealthBar" Additionally, let this condition destroy your bullet. The plan is to destroy the enemy object after it gets hit four times. To do so, test whether the alterable value HealthBar is lower or equal to 0 to destroy the enemy: HealthBar <= 0 – Destroy "enemy" This event will destroy your enemy when the alterable value hits a value lower or equal to 1. This is just one of countless possibilities for alterable values. As you can already see, alterable values will be your best friends from this very day! Interface, counters, and health bars We could talk a million years about good or bad interface design. The way your interface might look is just the beginning. Movement, speed, transparency, position, and size are just a few values you really have to think of with every new game. Some games completely go without an interface, which can create an even more immersive gaming experience. In other words, try to plan your interface precisely before you start to create an energy bar. We will work with the Counter game object to create a basic energy bar. The Counter object stores the numbers in your application and is used for objects such as fuel displays, clocks, speedometers, and health bars. The Counter object can be displayed as an animation, a bar, or a simple number. You can also hide the counter if you want to use it for calculations only. The following is the screenshot of the Counter object: Create a new counter object within your frame. The counter Type in the Properties settings is set to Numbers by default. Change the type to Horizontal bar. This will turn the counter to a small black rectangle. Change the color and size of this bar if you want. Now set the Initial Value to 10, the Minimum Value to 0, and the Maximum Value to 10. This means that the counter will start with a value of 10 when the frame (game) starts. Assuming that the bar represents your player's health, values will be subtracted or added. The value of this counter cannot go lower than 0 or higher than 10, as we have set the minimum and the maximum values! Now take a look at the RunTime Options tab. The option Follow the frame is important for every game. It is deactivated by default. That means that the counter will always stay in the same position no matter where your player character moves to. You can also put the counter (and the whole interface) in to a new layer of course. Open the event editor and create an event that will subtract 1 from the Counter object HealthBar (very similar to your enemy): Collision between "player" and "enemy" - Sub 1 from "HealthBar" Also add a limiting condition to deactivate multiple collisions at one time. You'll find this condition under Special | Limit conditions | Only one action when event loops. The only thing that is left is an event to destroy your object. Just test whether the health counter is lower or equal to 1 to destroy the player object: HealthBar <= 1 – Destroy "player" The following screenshot shows the event created: So, this is basically how you can use the counter object. As you can imagine, there are a lot of other situations where this object comes in very handy. You could, for example, create one more counter and leave it as a number. This could be your ammo counter! Set both Maximum and Initial Value to 20 and the Minimum Value to 0. In the event editor, subtract 1 from the counter whenever your player character fires a bullet. Add the following condition to your existing shooting condition: Counter > 0 Now your player will only shoot bullets when the counter is greater than 0. Of course, you have to add ammo packs to your game now. This is something you can find out on your own. Just use what you have learned so far. Going further Now think of the options you already have! You could add a destroy animation for your player. Let some simple particles bounce when your bullet hits the enemy or an obstacle. Go for some more advanced methods and change the animation of your player to a hurt state when he gets hit by an enemy. Maybe add some new events to your player. The player might be invincible for a while after he gets hurt, for example! Also think of your previous platformer prototype. Create a counter and add 1 every time you destroy one of the red crates! Talking about the red ones: why not set a path movement to the red crates? This would turn them from static boxes to patrolling, evil, explosive crates! Summary Resolutions are something you will think about before you start your next prototype. You created a new game setup to test some more advanced properties. Within this prototype, you also created and placed your first interface and turned it into a basic energy bar for your player. Alterable values will also be very useful from now on. Resources for Article: Further resources on this subject: Getting Started with Fusion Applications [Article] Dynamic Flash Charts - FusionCharts style [Article] Fine Tune the View layer of your Fusion Web Application [Article]
Read more
  • 0
  • 0
  • 1713
article-image-building-your-first-application-papervision3d-part-1
Packt
26 Oct 2009
7 min read
Save for later

Building your First Application with Papervision3D: Part 1

Packt
26 Oct 2009
7 min read
This article covers the following: Introduction to classes and object-oriented programming Working with the document class/main application file Introduction to classes and object-oriented programming In this article we will learn to write our own classes that can be used in Flash, along with Flex Builder and Flash Builder. If you're a developer using the Flash IDE, it might be the first time you'll write your own classes. Don't worry about the difficulty level if you are new to classes. Learning how to program  in an OOP way using Papervision3D is a good way to become more familiar with classes and it  might motivate you to learn more about this subject. You will not be the first who learned to program in an OOP way, as a side effect of learning an external library such as Papervision3D is. So what are classes? In fact, they are nothing more than a set of functions (methods) and variables (properties) grouped together in a single file, which is known as the class definition. A class forms the blueprint for new objects that you create. Sounds a bit vague? What if you were told that you've probably already used classes? Each object you create from the Flash API is based on classes. For example, Sprites, MovieClips, and TextFields are objects that you have probably used in your code before. In fact, these objects are classes. The blueprints for these objects and their classes are already incorporated in Flash. First, have a look at how you can use them to create a new Sprite object: var mySprite:Sprite = new Sprite(); Looks familiar—right? By doing this, you create a new copy of the Sprite class as an object called mySprite. This is called instantiation of an object. There's no difference between instantiating built-in classes or instantiating custom written classes. Papervision3D is a set of custom classes. var myObject3D:DisplayObject3D = new DisplayObject3D(); So, although you know how to use classes, creating your own classes might be new to you. Creating a custom class An ActionScript class is basically a text-based file with an .as extension stored somewhere on your computer, containing ActionScript code. This code works as the previously mentioned blueprint for an object. Let's see what that blueprint looks like: package { public class ExampleClass { public var myName:String = "Paul"; public function ExampleClass() { } public function returnMyName():String { return "My name is" + myName; } }} On the first line, you'll find the package statement, followed by an opening curly bracket and ended with a closing curly bracket at the bottom of the class. Packages are a way to group classes together and represent the folder in which you saved the file. Imagine you have created a folder called myPackage inside the same folder where you've saved an FLA or inside a defined source folder. In order to have access to the folder and its classes, you will need to define the package using the folder's name as shown next: package myPackage { ...} This works the same way for subfolders. Let's imagine a folder called subPackage has been added to the imaginary folder myPackage. The package definition for classes inside this subfolder should then look like this: package myPackage.subPackage { ...} If you don't create a special folder to group your classes, you can use the so-called default package instead of defining a name. All the examples in this article will use default packages. However, for real projects it's good practice to set up a structure in order to organize your files. After the package definition, you'll find the class definition, which looks as follows: public class ExampleClass{ ...} The name of the class must be the same name as the class file. In this example, the file needs to be saved as ExampleClass.as. Besides the fact that working packages is a good way to organize your files, they can also be used to uniquely identify each class in a project. At the top of the class definition you'll see the word public, which is a keyword defining that the class is accessible to all other code in the project. This keyword is called, an access modifier. The defined name of the class will be used to instantiate new copies of the class. Instantiating this class could be done like this: var classExample:ExampleClass = new ExampleClass(); Inside the class, a string variable is defined in pretty much the same way as you would when working with timeline scripting. public var myName:String = "Paul"; The definition of a variable inside a class is called as a class property. You can add as many properties to a class as you want. Each definition starts off with an access modifier. In this case the access modifier is set to public, meaning that it is both readable and writeable by code located outside the class: var classExample:ExampleClass = new ExampleClass();classExample.myName = "Jeff"; As you can see, this creates an instance of ExampleClass. We also changed the myName property from Paul to Jeff. When a property is defined as public, this is allowed to happen. In case you want access to this property inside the class itself, you can mark the property as private: private var myName:String = "Paul"; Executing the previous code to change myName from Paul to Jeff will result in a compile-time error. In the next lines we see the creation of a new function called ExampleClass in the class. Functions that have the same name as the name of the class are known as constructors. public function ExampleClass(){} Constructors always have a public access modifier and are called automatically each time the class is instantiated. This means that the code inside this function will be executed automatically. All other function definitions inside the class are called methods. public function returnMyName():String{ return "My name is" + myName;} At the end of this method definition, you'll notice a colon followed by a data type. This defines the type of object the method returns. When you work with functions on the timeline in Flash, you can define this as well, but it's not required to do so. The method returnMyName() is defined to return a string. In case you do not want to return any data type, you can define this by using a void as the return type: public function returnNothing():void{ //Do not return something} We need to define return type only for methods and not for constructors. Classes, as well as properties and methods, have access modifiers that define from where each of these objects can be accessed. So far we've seen that a public keyword allows code outside the class to access a property, or call a method inside the class. When you allow access from other code, you need to be aware that this code can mess up your class. You can prevent this by using the private keyword, which makes the property or method only accessible inside your class. Two other access modifiers that are often used are internal and protected. Classes inside the same package can access internal methods or properties and protected methods can only be used inside a related subclass. Subclasses are a part of inheritance, which will be explained in a bit. As long as you have not planned to give access to scripts outside your class, it's a good practice to mark all properties and methods of a class as private by default. When defining a property or method, you should always ask yourself whether you want them to be accessed from outside your class.
Read more
  • 0
  • 0
  • 1549
Modal Close icon
Modal Close icon