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 - 2D Game Development

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

Components in Unity

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

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

Data-driven Design

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

Packt
07 Jul 2014
12 min read
Save for later

HTML5 Game Development – A Ball-shooting Machine with Physics Engine

Packt
07 Jul 2014
12 min read
(For more resources related to this topic, see here.) Mission briefing In this article, we focus on the physics engine. We will build a basketball court where the player needs to shoot the ball in to the hoop. A player shoots the ball by keeping the mouse button pressed and releasing it. The direction is visualized by an arrow and the power is proportional to the duration of the mouse press and hold event. There are obstacles present between the ball and the hoop. The player either avoids the obstacles or makes use of them to put the ball into the hoop. Finally, we use CreateJS to visualize the physics world into the canvas. You may visit http://makzan.net/html5-games/ball-shooting-machine/ to play a dummy game in order to have a better understanding of what we will be building throughout this article. The following screenshot shows a player shooting the ball towards the hoop, with a power indicator: Why is it awesome? When we build games without a physics engine, we create our own game loop and reposition each game object in every frame. For instance, if we move a character to the right, we manage the position and movement speed ourselves. Imagine that we are coding a ball-throwing logic now. We need to keep track of several variables. We have to calculate the x and y velocity based on the time and force applied. We also need to take the gravity into account; not to mention the different angles and materials we need to consider while calculating the bounce between the two objects. Now, let's think of a physical world. We just defined how objects interact and all the collisions that happen automatically. It is similar to a real-world game; we focus on defining the rule and the world will handle everything else. Take basketball as an example. We define the height of the hoop, size of the ball, and distance of the three-point line. Then, the players just need to throw the ball. We never worry about the flying parabola and the bouncing on the board. Our space takes care of them by using the physics laws. This is exactly what happens in the simulated physics world; it allows us to apply the physics properties to game objects. The objects are affected by the gravity and we can apply forces to them, making them collide with each other. With the help of the physics engine, we can focus on defining the game-play rules and the relationship between the objects. Without the need to worry about collision and movement, we can save time to explore different game plays. We then elaborate and develop the setup further, as we like, among the prototypes. We define the position of the hoop and the ball. Then, we apply an impulse force to the ball in the x and y dimensions. The engine will handle all the things in between. Finally, we get an event trigger if the ball passes through the hoop. It is worth noting that some blockbuster games are also made with a physics engine. This includes games such as Angry Birds, Cut the Rope, and Where's My Water. Your Hotshot objectives We will divide the article into the following eight tasks: Creating the simulated physics world Shooting a ball Handling collision detection Defining levels Launching a bar with power Adding a cross obstacle Visualizing graphics Choosing a level Mission checklist We create a project folder that contains the index.html file and the scripts and styles folders. Inside the scripts folder, we create three files: physics.js, view.js, and game.js. The physics.js file is the most important file in this article. It contains all the logic related to the physics world including creating level objects, spawning dynamic balls, applying force to the objects, and handling collision. The view.js file is a helper for the view logic including the scoreboard and the ball-shooting indicator. The game.js file, as usual, is the entry point of the game. It also manages the levels and coordinates between the physics world and view. Preparing the vendor files We also need a vendors folder that holds the third-party libraries. This includes the CreateJS suite—EaselJS, MovieClip, TweenJS, PreloadJS—and Box2D. Box2D is the physics engine that we are going to use in this article. We need to download the engine code from https://code.google.com/p/box2dweb/. It is a port version from ActionScript to JavaScript. We need the Box2dWeb-2.1.a.3.min.js file or its nonminified version for debugging. We put this file in the vendors folder. Box2D is an open source physics-simulation engine that was created by Erin Catto. It was originally written in C++. Later, it was ported to ActionScript because of the popularity of Flash games, and then it was ported to JavaScript. There are different versions of ports. The one we are using is called Box2DWeb, which was ported from ActionScript's version Box2D 2.1. Using an old version may cause issues. Also, it will be difficult to find help online because most developers have switched to 2.1. Creating a simulated physics world Our first task is to create a simulated physics world and put two objects inside it. Prepare for lift off In the index.html file, the core part is the game section. We have two canvas elements in this game. The debug-canvas element is for the Box2D engine and canvas is for the CreateJS library: <section id="game" class="row"> <canvas id="debug-canvas" width="480" height="360"></canvas> <canvas id="canvas" width="480" height="360"></canvas> </section> We prepare a dedicated file for all the physics-related logic. We prepare the physics.js file with the following code: ;(function(game, cjs, b2d){ // code here later }).call(this, game, createjs, Box2D); Engage thrusters The following steps create the physics world as the foundation of the game: The Box2D classes are put in different modules. We will need to reference some common classes as we go along. We use the following code to create an alias for these Box2D classes: // alias var b2Vec2 = Box2D.Common.Math.b2Vec2 , b2AABB = Box2D.Collision.b2AABB , b2BodyDef = Box2D.Dynamics.b2BodyDef , b2Body = Box2D.Dynamics.b2Body , b2FixtureDef = Box2D.Dynamics.b2FixtureDef , b2Fixture = Box2D.Dynamics.b2Fixture , b2World = Box2D.Dynamics.b2World , b2MassData = Box2D.Collision.Shapes.b2MassData , b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape , b2CircleShape = Box2D.Collision.Shapes.b2CircleShape , b2DebugDraw = Box2D.Dynamics.b2DebugDraw , b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef , b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef ; We prepare a variable that states how many pixels define 1 meter in the physics world. We also define a Boolean to determine if we need to draw the debug draw: var pxPerMeter = 30; // 30 pixels = 1 meter. Box3D uses meters and we use pixels. var shouldDrawDebug = false; All the physics methods will be put into the game.physics object. We create this literal object before we code our logics: var physics = game.physics = {}; The first method in the physics object creates the world: physics.createWorld = function() { var gravity = new b2Vec2(0, 9.8); this.world = new b2World(gravity, /*allow sleep= */ true); // create two temoporary bodies var bodyDef = new b2BodyDef; var fixDef = new b2FixtureDef; bodyDef.type = b2Body.b2_staticBody; bodyDef.position.x = 100/pxPerMeter; bodyDef.position.y = 100/pxPerMeter; fixDef.shape = new b2PolygonShape(); fixDef.shape.SetAsBox(20/pxPerMeter, 20/pxPerMeter); this.world.CreateBody(bodyDef).CreateFixture(fixDef); bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.x = 200/pxPerMeter; bodyDef.position.y = 100/pxPerMeter; this.world.CreateBody(bodyDef).CreateFixture(fixDef); // end of temporary code } The update method is the game loop's tick event for the physics engine. It calculates the world step and refreshes debug draw. The world step upgrades the physics world. We'll discuss it later: physics.update = function() { this.world.Step(1/60, 10, 10); if (shouldDrawDebug) { this.world.DrawDebugData(); } this.world.ClearForces(); }; Before we can refresh the debug draw, we need to set it up. We pass a canvas reference to the Box2D debug draw instance and configure the drawing settings: physics.showDebugDraw = function() { shouldDrawDebug = true; //set up debug draw var debugDraw = new b2DebugDraw(); debugDraw.SetSprite(document.getElementById("debug-canvas").getContext("2d")); debugDraw.SetDrawScale(pxPerMeter); debugDraw.SetFillAlpha(0.3); debugDraw.SetLineThickness(1.0); debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit); this.world.SetDebugDraw(debugDraw); }; Let's move to the game.js file. We define the game-starting logic that sets up the EaselJS stage and Ticker. It creates the world and sets up the debug draw. The tick method calls the physics.update method: ;(function(game, cjs){ game.start = function() { cjs.EventDispatcher.initialize(game); // allow the game object to listen and dispatch custom events. game.canvas = document.getElementById('canvas'); game.stage = new cjs.Stage(game.canvas); cjs.Ticker.setFPS(60); cjs.Ticker.addEventListener('tick', game.stage); // add game.stage to ticker make the stage.update call automatically. cjs.Ticker.addEventListener('tick', game.tick); // gameloop game.physics.createWorld(); game.physics.showDebugDraw(); }; game.tick = function(){ if (cjs.Ticker.getPaused()) { return; } // run when not paused game.physics.update(); }; game.start(); }).call(this, game, createjs); After these steps, we should have a result as shown in the following screenshot. It is a physics world with two bodies. One body stays in position and the other one falls to the bottom. Objective complete – mini debriefing We have defined our first physical world with one static object and one dynamic object that falls to the bottom. A static object is an object that is not affected by gravity and any other forces. On the other hand, a dynamic object is affected by all the forces. Defining gravity In reality, we have gravity on every planet. It's the same in the Box2D world. We need to define gravity for the world. This is a ball-shooting game, so we will follow the rules of gravity on Earth. We use 0 for the x-axis and 9.8 for the y-axis. It is worth noting that we do not need to use the 9.8 value. For instance, we can set a smaller gravity value to simulate other planets in space—maybe even the moon; or, we can set the gravity to zero to create a top-down view of the ice hockey game, where we apply force to the puck and benefit from the collision. Debug draw The physics engine focuses purely on the mathematical calculation. It doesn't care about how the world will be presented finally, but it does provide a visual method in order to make the debugging easier. This debug draw is very useful before we use our graphics to represent the world. We won't use the debug draw in production. Actually, we can decide how we want to visualize this physics world. We have learned two ways to visualize the game. The first way is by using the DOM objects and the second one is by using the canvas drawing method. We will visualize the world with our graphics in later tasks. Understanding body definition and fixture definition In order to define objects in the physics world, we need two definitions: a body definition and fixture definition. The body is in charge of the physical properties, such as its position in the world, taking and applying force, moving speed, and the angular speed when rotating. We use fixtures to handle the shape of the object. The fixture definition also defines the properties on how the object interacts with others while colliding, such as friction and restitution. Defining shapes Shapes are defined in a fixture. The two most common shapes in Box2D are rectangle and circle. We define a rectangle with the SetAsBox function by providing half of its width and height. Also, the circle shape is defined by the radius. It is worth noting that the position of the body is at the center of the shape. It is different from EaselJS in that the default origin point is set at the top-left corner. Pixels per meter When we define the dimension and location of the body, we use meter as a unit. That's because Box2D uses metric for calculation to make the physics behavior realistic. But we usually calculate in pixels on the screen. So, we need to convert between pixels on the screen and meters in the physics world. That's why we need the pxPerMeter variable here. The value of this variable might change from project to project. The update method In the game tick, we update the physics world. The first thing we need to do is take the world to the next step. Box2D calculates objects based on steps. It is the same as we see in the physical world when a second is passed. If a ball is falling, at any fixed time, the ball is static with the property of the falling velocity. In the next millisecond, or nanosecond, the ball falls to a new position. This is exactly how steps work in the Box2D world. In every single step, the objects are static with their physics properties. When we go a step further, Box2D takes the properties into consideration and applies them to the objects. This step takes three arguments. The first argument is the time passed since the last step. Normally, it follows the frame-per-second parameter that we set for the game. The second and the third arguments are the iteration of velocity and position. This is the maximum iterations Box2D tries when resolving a collision. Usually, we set them to a low value. The reason we clear the force is because the force will be applied indefinitely if we do not clear it. That means the object keeps receiving the force on each frame until we clear it. Normally, clearing forces on every frame will make the objects more manageable. Classified intel We often need to represent a 2D vector in the physics world. Box2D uses b2vec for this purpose. Similar to the b2vec function, we use quite a lot of Box2D functions and classes. They are modularized into namespaces. We need to alias the most common classes to make our code shorter.
Read more
  • 0
  • 0
  • 5564

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

Customizing an Avatar in Flash Multiplayer Virtual Worlds

Packt
27 Aug 2010
5 min read
(For more resources on Flash, see here.) Customizing your avatar A Flash virtual world is a social community in which players interact with each other and have their own identity. Virtual world usually lets a user decide the avatar's appearance by choosing the combination of different styles and colors. Customizing different styles Each part of the avatar will have different styles and shapes to form different combinations of the appearance of the avatar. Thanks to the timeline and movie clip features in Flash, we can put different styles of each part within the movie clip. For example, the following screenshot shows the head movie clip with different head styles placed frame by frame and we can use gotoAndStop to display the style we want. Customizing the color ActionScript supports changing the color transform for a given movie clip. It supports not only color tint but also applying color filter and detailed RGB transformation. We will use the simple color tint to change the color of the avatar. As the color transform is applying to the whole movie clip, we cannot simply tint the avatar movie clip because that will make the whole avatar tint to one solid color. In order to tint a partial part of the movie clip, we specifically create a movie clip in each part and name it color_area. We later program the ActionScript to change all movie clip names with color_area to the customized color. Adding customization to avatar class We are going to change the style and color by ActionScript in avatar class. We need to import the ColorTransform class in flash.geom package to change the color with ActionScript. import flash.geom.ColorTransform; We need several instance variables to hold the styles and color state. public const totalStyles:Number = 3;public var currentColor:Number = 0x704F4C;public var currentStyle:Number = 1; We wrap the whole block of color transform code into one function. The color transform adds RGB color transformation to the target movie clip. We only use colorTransform to tint the color here but it also supports percentage transform that adds partial color to the target movie clip. We will apply the color transform to the color area inside the head of the avatar in 4 directions. public function changeColor(newColor:Number = 0x000000):void { currentColor = newColor; for each(var avatar:MovieClip in _directionArray){ var avatarColor:ColorTransform = new ColorTransform(); avatarColor.color = newColor; avatar.head.color_area.transform.colorTransform = avatarColor; } } We modified the color by using color transform and used timeline to style the avatar style. Every frame in the head movie clip represents a style with its color tint area. We display the new style by changing the current frame of the avatar movie clip. It is also necessary to change the color again after switching the style because every style contains its own color area. public function changeStyle(styleNumber:int):void { for each(var avatar:MovieClip in _directionArray){ /* display the giving style in all parts of avatar*/ avatar.head.gotoAndStop(styleNumber); avatar.body.gotoAndStop(styleNumber); avatar.lefthand.gotoAndStop(styleNumber); avatar.righthand.gotoAndStop(styleNumber); /* need to apply the color again after changing the style */ var avatarColor:ColorTransform = new ColorTransform(); avatarColor.color = currentColor; avatar.head.color_area.transform.colorTransform = avatarColor; } currentStyle = styleNumber; } The purpose of the avatar class is to control the appearance of the avatar. We just implemented the direction, color, and style switching methods and it is now ready for customization panel to use. Designing a customization panel Avatars in virtual worlds and games often provide players with different kinds of customization. Some games allow users to customize the whole body with lots of options while some games may only provide two to three basic customizations. The layout design of the customization panel is often based on the number of options. There are two common customization panel layouts in the market. One layout displays arrows for a user to select next and previous styles. The other one displays a thumbnail view of the options within the same category. The arrows selection layout is suitable for an avatar that contains limited parts for customization. There may be only two to four categories and not many options in each category. Players can easily loop through different style combinations and choose their favorite one using this layout. The following avatar customization screenshot from the 2D Online RPG called Dragon Fable uses the arrows selection layout: The thumbnail view layout is suitable for avatars that can be highly customized. There are often many categories to customize and each category provides a lot of options for players to choose. Some virtual worlds even provide micro modification so that players can adjust details on the chosen style such as the distance between the eyes. Players do not need to iterate the large amount of styles and can quickly choose a style option among them with the thumbnail view. The following screenshot is an online Mii editor. Mii is the avatar system in the Nintendo Wii console. This is an online clone of the Mii avatar customization. It allows a large amount of avatar customization by the thumbnail view layout with extended features such as scaling and moving the elements.
Read more
  • 0
  • 0
  • 5545

article-image-creating-games-cocos2d-x-easy-and-100-percent-free-0
Packt
01 Apr 2015
5 min read
Save for later

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

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

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

Designing an Avatar in Flash Multiplayer Virtual Worlds

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

Ordering the Buildings in Flash Virtual Worlds

Packt
16 Aug 2010
2 min read
(For more resources on Flash, see here.) Ordering the buildings The buildings are not well placed on the map. They overlap with each other in a very strange way. That is because we are now viewing the 3D isometric world in 2D screen with wrong ordering. When we view the 3D perspective in the following way, the closer objects should block the view of the objects behind. The buildings in the preceding image do not obey this real-world rule and cause some strange overlapping. We are going to solve this problem in the next section. Ordering the movie clips in Flash In Flash, every movie clip has its own depth. The depth is called z-order or the z-index of the movie clip. A movie clip with bigger z-order number is higher and covers the others with lower z-order when overlapping. By swapping their z-order, we can rearrange the movie clips on how they overlap and create the correct ordering of isometric buildings. Determining an object's location and view According to our tile-based isometric map, the object that locates in larger number of the x and y axis is in front of the object that locates in smaller number of the x and y axis. We can thus compare the isometric x and y coordinate to determine which object is in front. There is a special case when all shapes of the buildings occupy square shapes. In this situation, the order of the movie clip's z order can be easily determined by comparing their y position.
Read more
  • 0
  • 0
  • 5437

article-image-sparrow-ios-game-framework-basics-our-game
Packt
25 Jun 2014
10 min read
Save for later

Sparrow iOS Game Framework - The Basics of Our Game

Packt
25 Jun 2014
10 min read
(For more resources related to this topic, see here.) Taking care of cross-device compatibility When developing an iOS game, we need to know which device to target. Besides the obvious technical differences between all of the iOS devices, there are two factors we need to actively take care of: screen size and texture size limit. Let's take a closer look at how to deal with the texture size limit and screen sizes. Understanding the texture size limit Every graphics card has a limit for the maximum size texture it can display. If a texture is bigger than the texture size limit, it can't be loaded and will appear black on the screen. A texture size limit has power-of-two dimensions and is a square such as 1024 pixels in width and in height or 2048 x 2048 pixels. When loading a texture, they don't need to have power-of-two dimensions. In fact, the texture does not have to be a square. However, it is a best practice for a texture to have power-of-two dimensions. This limit holds for big images as well as a bunch of small images packed into a big image. The latter is commonly referred to as a sprite sheet. Take a look at the following sample sprite sheet to see how it's structured: How to deal with different screen sizes While the screen size is always measured in pixels, the iOS coordinate system is measured in points. The screen size of an iPhone 3GS is 320 x 480 pixels and also 320 x 480 points. On an iPhone 4, the screen size is 640 x 960 pixels, but is still 320 by 480 points. So, in this case, each point represents four pixels: two in width and two in height. A 100-point wide rectangle will be 200 pixels wide on an iPhone 4 and 100 pixels on an iPhone 3GS. It works similarly for the devices with large display screens, such as the iPhone 5. Instead of 480 points, it's 568 points. Scaling the viewport Let's explain the term viewport first: the viewport is the visible portion of the complete screen area. We need to be clear about which devices we want our game to run on. We take the biggest resolution that we want to support and scale it down to a smaller resolution. This is the easiest option, but it might not lead to the best results; touch areas and the user interface scale down as well. Apple recommends for touch areas to be at least a 40-point square; so, depending on the user interface, some elements might get scaled down so much that they get harder to touch. Take a look at the following screenshot, where we choose the iPad Retina resolution (2048 x 1536 pixels) as our biggest resolution and scale down all display objects on the screen for the iPad resolution (1024 x 768 pixels): Scaling is a popular option for non-iOS environments, especially for PC and Mac games that support resolutions from 1024 x 600 pixels to full HD. Sparrow and the iOS SDK provide some mechanisms that will facilitate handling Retina and non-Retina iPad devices without the need to scale the whole viewport. Black borders Some games in the past have been designed for a 4:3 resolution display but then made to run on a widescreen device that had more screen space. So, the option was to either scale a 4:3 resolution to widescreen, which will distort the whole screen, or put some black borders on either side of the screen to maintain the original scale factor. Showing black borders is something that is now considered as bad practice, especially when there are so many games out there which scale quite well across different screen sizes and platforms. Showing non-interactive screen space If our pirate game is a multiplayer, we may have a player on an iPad and another on an iPhone 5. So, the player with the iPad has a bigger screen and more screen space to maneuver their ship. The worst case will be if the player with the iPad is able to move their ship outside the visual range for the iPhone player to see, which will result in a serious advantage for the iPad player. Luckily for us, we don't require competitive multiplayer functionality. Still, we need to keep a consistent screen space for players to move their ship in for game balance purposes. We wouldn't want to tie the difficulty level to the device someone is playing on. Let's compare the previous screenshot to the black border example. Instead of the ugly black borders, we just show more of the background. In some cases, it's also possible to move some user interface elements to the areas which are not visible on other devices. However, we will need to consider whether we want to keep the same user experience across devices and whether moving these elements will result in a disadvantage for users who don't have this extra screen space on their devices. Rearranging screen elements Rearranging screen elements is probably the most time-intensive and sophisticated way of solving this issue. In this example, we have a big user interface at the top of the screen in the portrait mode. Now, if we were to leave it like this in the landscape mode, the top of the screen will be just the user interface, leaving very little room for the game itself. In this case, we have to be deliberate about what kind of elements we need to see on the screen and which elements are using up too much screen estate. Screen real estate (or screen estate) is the amount of space available on a display for an application or a game to provide output. We will then have to reposition them, cut them up in to smaller pieces, or both. The most prominent example of this technique is Candy Crush (a popular trending game) by King. While this concept applies particularly to device rotation, this does not mean that it can't be used for universal applications. Choosing the best option None of these options are mutually exclusive. For our purposes, we are going to show non-interactive screen space, and if things get complicated, we might also resort to rearranging screen elements depending on our needs. Differences between various devices Let's take a look at the differences in the screen size and the texture size limit between the different iOS devices: Device Screen size (in pixels) Texture size limit (in pixels) iPhone 3GS 480 x 360 2048 x 2048 iPhone 4 (including iPhone 4S) and iPod Touch 4th generation 960 x 640 2048 x 2048 iPhone 5 (including iPhone 5C and iPhone 5S) and iPod Touch 5th generation 1136 x 640 2048 x 2048 iPad 2 1024 x 768 2048 x 2048 iPad (3rd and 4th generations) and iPad Air 2048 x 1536 4096 x 4096 iPad Mini 1024 x 768 4096 x 4096 Utilizing the iOS SDK Both the iOS SDK and Sparrow can aid us in creating a universal application. Universal application is the term for apps that target more than one device, especially for an app that targets the iPhone and iPad device family. The iOS SDK provides a handy mechanism for loading files for specific devices. Let's say we are developing an iPhone application and we have an image that's called my_amazing_image.png. If we load this image on our devices, it will get loaded—no questions asked. However, if it's not a universal application, we can only scale the application using the regular scale button on iPad and iPhone Retina devices. This button appears on the bottom-right of the screen. If we want to target iPad, we have two options: The first option is to load the image as is. The device will scale the image. Depending on the image quality, the scaled image may look bad. In this case, we also need to consider that the device's CPU will do all the scaling work, which might result in some slowdown depending on the app's complexity. The second option is to add an extra image for iPad devices. This one will use the ~ipad suffix, for example, my_amazing_image~ipad.png. When loading the required image, we will still use the filename my_amazing_image.png. The iOS SDK will automatically detect the different sizes of the image supplied and use the correct size for the device. Beginning with Xcode 5 and iOS 7, it is possible to use asset catalogs. Asset catalogs can contain a variety of images grouped into image sets. Image sets contain all the images for the targeted devices. These asset catalogs don't require files with suffixes any more. These can only be used for splash images and application icons. We can't use asset catalogs for textures we load with Sparrow though. The following table shows which suffix is needed for which device: Device Retina File suffix iPhone 3GS No None iPhone 4 (including iPhone 4S) and iPod Touch (4th generation) Yes @2x @2x~iphone iPhone 5 (including iPhone 5C and iPhone 5S) and iPod Touch (5th generation) Yes -568h@2x iPad 2 No ~ipad iPad (3rd and 4th generations) and iPad Air Yes @2x~ipad iPad Mini No ~ipad How does this affect the graphics we wish to display? The non-Retina image will be 128 pixels in width and 128 pixels in height. The Retina image, the one with the @2x suffix, will be exactly double the size of the non-Retina image, that is, 256 pixels in width and 256 pixels in height. Retina and iPad support in Sparrow Sparrow supports all the filename suffixes shown in the previous table, and there is a special case for iPad devices, which we will take a closer look at now. When we take a look at AppDelegate.m in our game's source, note the following line: [_viewController startWithRoot:[Game class] supportHighResolutions:YES doubleOnPad:YES]; The first parameter, supportHighResolutions, tells the application to load Retina images (with the @2x suffix) if they are available. The doubleOnPad parameter is the interesting one. If this is set to true, Sparrow will use the @2x images for iPad devices. So, we don't need to create a separate set of images for iPad, but we can use the Retina iPhone images for the iPad application. In this case, the width and height are 512 and 384 points respectively. If we are targeting iPad Retina devices, Sparrow introduces the @4x suffix, which requires larger images and leaves the coordinate system at 512 x 384 points. App icons and splash images If we are talking about images of different sizes for the actual game content, app icons and splash images are also required to be in different sizes. Splash images (also referred to as launch images) are the images that show up while the application loads. The iOS naming scheme applies for these images as well, so for Retina iPhone devices such as iPhone 4, we will name an image as Default@2x.png, and for iPhone 5 devices, we will name an image as Default-568h@2x.png. For the correct size of app icons, take a look at the following table: Device Retina App icon size iPhone 3GS No 57 x 57 pixels iPhone 4 (including iPhone 4S) and iPod Touch 4th generation Yes 120 x 120 pixels iPhone 5 (including iPhone 5C and iPhone 5S) and iPod Touch 5th generation Yes 120 x 120 pixels iPad 2 No 76 x 76 pixels iPad (3rd and 4th generation) and iPad Air Yes 152 x 152 pixels iPad Mini No 76 x 76 pixels The bottom line The more devices we want to support, the more graphics we need, which directly increases the application file size, of course. Adding iPad support to our application is not a simple task, but Sparrow does some groundwork. One thing we should keep in mind though: if we are only targeting iOS 7.0 and higher, we don't need to include non-Retina iPhone images any more. Using @2x and @4x will be enough in this case, as support for non-Retina devices will soon end. Summary This article deals with setting up our game to work on iPhone, iPod Touch, and iPad in the same manner. Resources for Article: Further resources on this subject: Mobile Game Design [article] Bootstrap 3.0 is Mobile First [article] New iPad Features in iOS 6 [article]
Read more
  • 0
  • 0
  • 5386

article-image-flash-10-multiplayer-game-lobby-and-new-game-screen-implementation
Packt
27 Jul 2010
5 min read
Save for later

Flash 10 Multiplayer Game: The Lobby and New Game Screen Implementation

Packt
27 Jul 2010
5 min read
(For more resources on Flash and Games, see here.) The lobby screen implementation In this section, we will learn how to implement the room display within the lobby. Lobby screen in Hello World Upon login, the first thing the player needs to do is enter the lobby. Once the player has logged into the server successfully, the default behavior of the PulseGame in PulseUI is to call enterLobby API. The following is the implementation within PulseGame: protected function postInit():void { m_netClient.enterLobby();} Once the player has successfully entered the lobby, the client will start listening to all the room updates that happen in the lobby. These updates include any newly created room, any updates to the room objects, for example, any changes to the player count of a game room, host change, etc. Customizing lobby screen In the PulseUI, the lobby screen is the immediate screen that gets displayed after a successful login. The lobby screen is drawn over whatever the outline object has drawn onto the screen. The following is added to the screen when the lobby screen is shown to the player: Search lobby UI Available game rooms Game room scroll buttons Buttons for creating a new game room Navigation buttons to top ten and register screens When the lobby is called to hide, the lobby UI elements are taken off the screen to make way for the incoming screen. For our initial game prototype, we don't need to make any changes. The PulseUI framework already offers all of the essential set of functionalities of a lobby for any kind of multiplayer game. However, the one place you may want to add more details is in what gets display for each room within the lobby. Customizing game room display The room display is controlled by the class RoomsDisplay, an instance of which is contained in GameLobbyScreen. The RoomsDisplay contains a number of RoomDisplay object instances, one for each room being displayed. In order to modify what gets displayed in each room display, we do it inside of the class that is subclassed from RoomDisplay. The following figure shows the containment of the Pulse layer classes and shows what we need to subclass in order to modify the room display: In all cases, we would subclass (MyGame) the PulseGame. In order to have our own subclass of lobby screen, we first need to create class (MyGameLobbyScreen) inherited from GameLobbyScreen. In addition, we also need to override the method initLobbyScreen as shown below: protected override function initLobbyScreen():void { m_gameLobbyScreen = new MyGameLobbyScreen();} In order to provide our own RoomsDisplay, we need to create a subclass (MyRoomsDisplay) inherited from RoomsDisplay class and we need to override the method where it creates the RoomsDisplay in GameLobbyScreen as shown below: protected function createRoomsDisplay():void { m_roomsDisplay = new MyRoomsDisplay();} Finally, we do similar subclassing for MyRoomDisplay and override the method that creates the RoomDisplay in MyRoomsDisplay as follows: protected override function createRoomDisplay (room:GameRoomClient):RoomDisplay { return new MyRoomDisplay(room);} Now that we have hooked up to create our own implementation of RoomDisplay, we are free to add any additional information we like. In order to add additional sprites, we now simply need to override the init method of GameRoom and provide our additional sprites. Filtering rooms to display The choice is up to the game developer to either display all the rooms currently created or just the ones that are available to join. We may override the method shouldShowRoom method in the subclass of RoomsDisplay (MyRoomsDisplay) to change the default behavior. The default behavior is to show rooms that are only available to join as well as rooms that allow players to join even after the game has started. Following is the default method implementation: protected function shouldShowRoom(room:GameRoomClient):Boolean { var show:Boolean; show = (room.getRoomType() == GameConstants.ROOM_ALLOW_POST_START); if(show == true) return true; else { return (room.getRoomStatus() == GameConstants.ROOM_STATE_WAITING); }} Lobby and room-related API Upon successful logging, all game implementation must call the enterLobby method. public function enterLobby(gameLobbyId:String = "DefaultLobby"):void You may pass a null string in case you only wish to have one default lobby. The following notification will be received again by the client whether the request to enter a lobby was successful or not. At this point, the game screen should switch to the lobby screen. function onEnteredLobby(error:int):void If entering a lobby was successful, then the client will start to receive a bunch of onNewGameRoom notifications, one for each room that was found active in the entered lobby. The implementation should draw the corresponding game room with the details on the lobby screen. function onNewGameRoom(room:GameRoomClient):void The client may also receive other lobby-related notifications such as onUpdateGameRoom for any room updates and onRemoveGameRoom for any room objects that no longer exist in lobby. function onUpdateGameRoom(room:GameRoomClient):voidfunction onRemoveGameRoom(room:GameRoomClient):void If the player wishes to join an existing game room in the lobby, you simply call joinGameRoom and pass the corresponding room object. public function joinGameRoom(gameRoom:GameRoomClient):void In response to a join request, the server notifies the requesting client of whether the action was successful or failed via the game client callback method. function onJoinedGameRoom(gameRoomId:int, error:int):void A player already in a game room may leave the room and go back to the lobby, by calling the following API: public function leaveGameRoom():void Note that if the player successfully left the room, the calling game client will receive the notification via the following callback API: function onLeaveGameRoom(error:int):void
Read more
  • 0
  • 0
  • 4931

article-image-flash-multiplayer-virtual-world-setting-smartfoxserver-third-party-http-and-database-s
Packt
17 Aug 2010
7 min read
Save for later

Flash Multiplayer Virtual World: Setting up SmartFoxServer with Third-party HTTP and Database Server

Packt
17 Aug 2010
7 min read
(For more resources on Flash, see here.) We are going to download and install Apache and MySQL server package. These kinds of server package features have easy install that auto-configures most of the server settings. It will also install some essential tool for beginners to manage the server easily, such as GUI server administration panel. Installing WAMP on Windows WampServer is an open source HTTP and database server solution on Windows. WAMP stands for Windows, Apache, MySQL, and PHP package. Go to http://www.wampserver.com/en/download.php. Click on Download WampServer to download the installer. Run the installer with all default settings. The server is configured and ready. The WampServer can run by launching from Start | Programs | WampServer | Start WampServer. It will be in the task bar and the server management operation can be found by clicking the WampServer icon. We can start the server by putting the server online in the menu. Installing MAMP on Mac OSX Similar to WampServer, MAMP is the one package web server solution that stands for Mac, Apache, MySQL, and PHP package. The MAMP package can be downloaded at http://www.mamp.info/. Download the MAMP package from the official website. Double-click on the downloaded MAMP dmg file to mount it. Drag the MAMP folder into the Applications folder. To run the MAMP server, go to Applications | MAMP and double-click on the MAMP.app. Installing LAMP on Linux As the same naming convention, the "L" stands for Linux here. Different Linux distributions use different ways to install applications. There may not be a oneclick install method on some Linux branch which requires us to install the Apache and MySQL individually. Some Linux may provide graphic user interface to install LAMP by just selecting it in the applications list. We will use Ubuntu to demonstrate the installation of LAMP. Launch terminal from Applications | Accessories | Terminal. Type following command to install LAMP. sudo tasksel install lamp-server The installer will progress and configure different modules. A dialog will prompt several times asking for a new MySQL root password. You can set your own MySQL password, while in the example we will leave the root password blank. After the completion of the installation, the MySQL server is set up as service in the system. It runs automatically and we do not need to manually launch it to use it. Connecting SmartFoxServer and MySQL server SmartFoxServer is a Java application and Java database connection driver is needed to connect from SmartFoxServer to MySQL database. Downloading JDBC Driver for MySQL JDBC is a Java database connection driver that we need to establish connections between the Java-based SmartFoxServer and the MySQL server. The JDBC driver for MySQL is called Connector/J. We are going to install it to enable MySQL connection from SmartFoxServer. Go to http://dev.mysql.com/downloads/connector/j/5.1.html in web browser. Download the Platform Independent Zip Archive. It may ask you to log in to MySQL.com account. Click on No thanks, just take me to the downloads! to bypass the login step. Choose a mirror to download by clicking on HTTP. Setting up the JDBC driver The MySQL Java connector comes with a bunch of files. We only need two among them. Extract the mysql-connector-java-5.1.10.zip file to a temporary folder. Open the folder and find the mysql-connector-java-5.1.10-bin.jar file. Copy that jar file into SmartFoxServer installation directory | jre | lib | ext. Go into the src directory of the extracted directory and copy the org directory to SmartFoxServer installation directory | jre | lib | ext. Configuring the server settings The configuration file of SmartFoxServer is an XML file that allows us to configure many server settings. It can configure the initial zone or room creation, server address, admin authorization, value tuning for performance, and a lot more. We are going to set the database connection for testing our setup in this article (core settings are out of scope of this article). The configuration file is called config.xml and is located in the SmartFoxServer installation directory under the Server directory. Configuring MySQL server connection in SmartFoxServer Open the config.xml in your favorite text editor. Go to line 203 of the config.xml. This line should be within the structure of a Zone tag with name as dbZone. Change the lines 203-218 from the config.xml: Original code: <DatabaseManager active="false"> <Driver>sun.jdbc.odbc.JdbcOdbcDriver</Driver> <ConnectionString>jdbc:odbc:sfsTest</ConnectionString> <!-- Example connecting to MySQL <Driver>org.gjt.mm.mysql.Driver</Driver> <ConnectionString>jdbc:mysql://192.168.0.1:3306/sfsTest </ConnectionString> --> <UserName>yourname</UserName> <Password>yourpassword</Password> <TestSQL><![CDATA[SELECT COUNT(*) FROM contacts]]></TestSQL> Replace the code in lines 203-218 with the following code: <DatabaseManager active="true"> <Driver>org.gjt.mm.mysql.Driver</Driver> <ConnectionString>jdbc:mysql://127.0.0.1:3306/mysql </ConnectionString> <UserName>root</UserName> <Password></Password> <TestSQL><![CDATA[SELECT NOW()]]></TestSQL> The new setting activates the DatabaseManager and configures the JDBC driver to the MySQL connector that we just downloaded. We also changed the user name and password of the connection to the database to "root" and empty password. We will use the empty password through out the development process but it is strongly recommended to set your own database user password. There is a TestSQL setting where we can write a simple database query so that the SmartFoxServer will try to run it to test if the database connection is correct. As we have not created any new databases for the virtual world, we will test the database connection by querying the current server time. Restarting the server We’ve just set up the connection between SmartFoxServer and third-party database. It is time to test the new setting by restarting the SmartFoxServer. To stop the SmartFoxServer in Windows and Linux, press Ctrl + C. To stop it in Mac OS X, click on the Cancel button in the SmartFoxServer log window. There is a log that appears as usual after we start up the server again. It is important to check the log carefully every time the config.xml is changed. The logfile can provide details of any errors that occur when it tries to load the configure file. For example, if we configure the database connection just now but forget to activate the DatabaseManager, the server will start up correctly. Then you may spend a lot of time debugging why the database connection is not working until you find that the DatabaseManager is not active at all. This happened to me several times while I was developing my first flash virtual world. If the server is running with the new database connection settings, the following lines will be appearing in the log. There can be different database manager settings for each zone. When checking the log, we should be aware which zone the log is referring to. We are configuring the database manager of dbZone zone now. DB Manager Activated ( org.gjt.mm.mysql.Driver ) Zone: dbZone If we forget to activate the DatabaseManager, we will not see the DB Manager Activated wording. Instead, the following message may appear in the log: DB Manager is not active in this Zone! Moreover, if the SmartFoxServer faces some fatal error on start up, it will terminate itself with more detailed error logs. The following lines are an example for error logs that appear when the MySQL connector file is missing: Can’t load db driver: org.gjt.mm.mysql.Driver [ Servre ] > DbManager could not retrive a connection. Java.sql.SQLException: Configuration file not found DbManagerException: The Test SQL statement failed! Please check your configuration. These lines state that the testing SQL failed to run, which we just set to test the connection. It also describes what exception has caused this error to help the debugging.
Read more
  • 0
  • 0
  • 4918
article-image-flash-10-multiplayer-game-game-interface-design
Packt
28 Jul 2010
10 min read
Save for later

Flash 10 Multiplayer Game: Game Interface Design

Packt
28 Jul 2010
10 min read
(For more resources on Flash and Games, see here.) Overview of Pulse library components The Pulse package includes two components Pulse.swc and PulseUI.swc.The Pulse.swc offers the API required for you to build a multiplayer game. While PulseUI offers the game screen management, both aid in the rapid development of your game. The Pulse.swc is required in order to communicate with the server and with other clients. The usage of PulseUI, on the other hand, is optional. It is recommended to use the PulseUI since it allows you to focus only on the game implementation and leaves the standard feature set of a multiplayer game to be taken care of by the PulseUI package. Once you have the implementation of your game done and working well, you can then replace the PulseUI package with something of your own that is more suited to your game. The following is a block diagram that shows the dependencies among different components: The Pulse API design The interaction of the game client with the server and other clients happens primarily via two classes that expose the Pulse features: GameClient GameClientCallback The GameClient is primarily used to send request to the server while creating a room, joining a room, or sending a message to other players such as chat or game state updates during game play. The GameClientCallback is an AS3 interface class for which one of the classes within the GameClient must implement. All notifications from the server are processed by the Pulse layer and corresponding notifications are called on the implementation of the callback class—for example, when a create room request was successful or when a chat message was received, etc. Creating the Hello World sample Let us now explore the Hello World sample that is included in the Pulse package. The Hello World sample and the rest of the samples rely heavily on the game screen management framework package, PulseUI, which is also included in the Pulse package along with the source code. In this article, we will focus on the code contained in the Hello World sample and how the sample makes use of the PulseUI. In order to explore the Hello World sample, we first need to create a project in Flash Builder—all the required source files already exists in the sample folders. The Hello World sample does the following: create a room or join an existing game room, then add, remove, or modify a game state—the changes done on one client instance are then reflected on all other clients that are in the same room. Think it is too much for a Hello World sample? It is not! These are just the basic functionalities for any multiplayer game. Moreover, we don't need to write the code for every bit of functionality because we heavily rely on Pulse SDK to do all the dirty work. Setting up the project Fire up the Flash Builder 4 IDE and let us start by creating an ActionScript project called Hello World: From the main menu, navigate to File | New | ActionScript Project. You will see the following screenshot. Enter a project name HelloWorld or any other name of your choice. Since we already have the entire source required for Hello World from the Pulse package, click on Next to specify that the source folder is already on the disk. This will bring up the following screen where we choose the Hello World src folder as shown. Note that the screenshot shows that Pulse was installed under F:Gamantra. This path may be different on your computer. Once we have chosen the source folder, we still need to choose the main source folder and main application file. Unfortunately, in order to do this, we need to navigate a bug in Flash Builder 4. You need to click on the Back button and then again on the Next button, bringing us back to where we were. We now click on the Browse button, as shown in the screenshot, and choose the [source path] src and click on OK. Next we choose the main application file—this determines the main class file that the execution will start with. We need to tell the Flash Builder to use the Pulse libraries for this project. In the Flash world, the library files come with an extension .swc, which stands for shockwave component. Once you make it available to your project, you can start using the classes and functions that are exposed from within the library. In order to do so, choose the Library Path tab view and click on the Add SWC… button; navigate to the lib folder within the Pulse installation folder and choose Pulse.swc and once again do the same procedure for PulseUI.swc. Click on the Finish button. As a final step, before attempting to run the sample, we also need to set the stage size to 800 (width) by 600 (height). The PulseUI requires the stage size to be exactly this size. We may also set the background color of our choice as shown in the following screenshot: After this step, Flash Builder 4 should be able to crunch all the code in folders and report no problems. This will also create the swf files under the project folder within the workspace ready for you to take it for a spin. At this point, you may also use the debugger to step through the code. But make sure the Pulse server is running so that you may login and explore all the screens. The Hello World specification The Hello World client will be able to create a new HelloGameState and share it with other players, and any player may change the x and y and have that change reflected in every player's screen. Here is the final screen that we will end up with: The screenshot is that of the game screen. The circles are a visual representation of the game states, the position of the circle comes from the corresponding game states x and y values and so does the color from the color property. We will have two buttons: one to add new game states and another to remove them. To add a new circle (a game state), we click on the Add button. To remove an existing game state, we click on any of the circles and click on the Remove button. The selected circle appears to be raised like the one on the far right-hand side of the screenshot. We may also modify an existing game state by moving the circles by clicking and dragging them to a different position—doing that on one client, we can observe the change in every other player's screen as well. The schema file For any Pulse-based game development, we first start out with an XML-formatted schema file. Let's now explore the schema file for the Hello World sample. The game developer must create a schema file that specifies all the needed game states, avatars, and game room objects. After you have created the schema file, we then use a Pulse modeler tool to create the class files based on the schema to be used within the game. So first let's examine the schema file for the Hello World project: <ObjectSchema> <import> <client import="pulse.gsrc.client.*" /> </import> <class name="HelloGameState" parent="GameState" classId="601" > <public> <property index="0" name="x" count="1" type="int"/> <property index="1" name="y" count="1" type="int"/> <property index="2" name="color" count="1" type="int"/> </public> </class></ObjectSchema> Navigate to the project folder where you have created the project and create a file called GameSchema.xml with the above content. We will not go through the details of the XML file in greater detail since it is out of the scope of this article. For the Hello World sample, we will define a game state object that we can use to share game states among all the players within a game room. We will name the class as HelloGameState, but you are welcome to call it by any name or something that makes sense to your game. You may also define as many game state classes as you like. For the HelloGameState in the schema file, each game state instance will define three properties, namely, x, y, and color. Code generator In order to create the AS3 class files from the schema file, you need to run the batch file called PulseCodeGen.bat found in the $bin folder. It takes the following three parameters: Path to schema file Namespace Output directory In order to make our life easier, let us create a batch file that will call the PulseCodeGen and pass all the required parameters. The reason for creating the batch file is that you have to code generate every time you modify the schema file. As you progress through your game implementation, it is normal to add a new class or modify an existing one. The convenience batch file may look like what's shown next. Let's call it .init.bat and save it in the same root folder for the sample along with the schema file. @ECHO OFFIF EXIST .srchwgsrcclient del .srchwgsrcclient*.asCALL "%GAMANTRA%"binPulseCodeGen.bat .GameSchema.xml hw.gsrc .srchwgsrcIF NOT %ERRORLEVEL% == 0 GOTO ERRORECHO Success!GOTO END:ERRORECHO oops!:ENDpause The schema file parameter to the Pulse code generator is specified as .GameSchema.xml because the schema file and the batch file are in the same folder. The second parameter is the package name for the generated classes—in this example, it is specified to be hw.gsrc. You specify the directory that the generated classes will be saved to as the last parameter. Note that the code generator appends client to both the package and directory into which it will be saved. It is also important to match the package name and directory structure as required by the AS3 compiler. Upon running the code gen successfully, there is one AS3 class generated for each class in the schema file and two additional supporting class files. One is a factory class called GNetClientObjectFactory, which is responsible for creating new instances of generated classes, and the other is GNetMetaDataMgr, which aids in serializing and de-serializing the transmitting data over the network. The data carried is what resides in the instances of generated classes. You don't need to deal with these two extra classes first hand; it is mostly used by the underlying Pulse runtime system. As for the generated classes for what is defined in the schema file, the name of the class would be identical to what is specified in the schema file plus the suffix Client. In this example, there would be a class generated with the name HelloGameStateClient.as. Go ahead and try running the batch file called init.bat under $samplesHelloWorld. Project directory structure The Hello World that is part of the Pulse package is organized into the following directory structure: hw gsrc client rsrc ui The package hw being the root package contains the main class HelloWorld.as, and the gsrc as you see contains the generated class. The rsrc folder contains the skin files, which we will discuss in more detail later in this article. The skin files in Pulse consist of three PNG files that provide all the basic buttons and background for the game. You can simply replace these PNG files with your own set, provided the individual elements with the file are of the exact dimensions and are placed at the exact same positions.
Read more
  • 0
  • 1
  • 4777

article-image-flash-game-development-making-astro-panic
Packt
11 Apr 2011
10 min read
Save for later

Flash Game Development: Making of Astro-PANIC!

Packt
11 Apr 2011
10 min read
  Flash Game Development by Example Build 10 classic Flash games and learn game development along the way         Read more about this book       (For more resources on flash, see here.) Astro-PANIC! was released as an all machine language Commodore 64 game to be typed in the February 1984 issue of COMPUTE!'s Gazette magazine. At that time there wasn't any blog with source codes to download or copy/paste into your projects, so the only way to learn from other programmers was buying computer magazines and typing the example codes on your computer. Since I suppose you never played this game, I would recommend you play it a bit on http://www.freewebarcade.com/game/astro-panic/. Defining game design Here are the rules to design our Astro-PANIC! prototype: The player controls a spaceship with the mouse, being able to move it horizontally on the bottom of the screen. At each level, a given number of enemy spaceships appear and roam around the stage at a constant speed in a constant direction. Enemies cannot leave the stage, and they will bounce inside it as they touch stage edges. Enemies don't shoot, and the only way they will kill the player is by touching the spaceship. The player can only have one bullet on stage at any time, and hitting an enemy with the bullet will destroy it. Destroying all enemies means passing the level, and at each level the number of enemies and their speed increases. These are the basic rules. We'll add some minor improvements during the design of the game itself, but before we start drawing the graphics, keep in mind we'll design something with the look and feel of old coin operator monitors, with bright glowing graphics. Creating the game and drawing the graphics Create a new file (File | New). Then, from New Document window select Actionscript 3.0. Set its properties as width to 640 px, height to 480 px, background color to #000000 (black) and frame rate to 60. Also define the Document Class as Main and save the file as astro-panic.fla. Though 30 frames per second is the ideal choice for smooth animations, we will use 60 frames per second to create a very fast paced game. There are three actors in this game: the player-controlled spaceship, the bullet and the enemy. In astro-panic.fla, create three new Movie Clip symbols and call them spaceship_mc for the spaceship, bullet_mc for the bullet, and enemy_mc for the enemy. Set them all as exportable for ActionScript. Leave all other settings at their default values, just like you did in previous article on Tetris. From left to right: The spaceship (spaceship_mc), the bullet (bullet_mc), and the enemy (enemy_mc). I made all assets with the shape of a circle. The spaceship is half a circle with a radius of 30 pixels, the bullet is a circle with a 4 pixels radius, and the enemy is a circle with a radius of 25 pixels. All of them have the registration point in their centers, and enemy_mc has a dynamic text field in it called level. If you've already met dynamic text fields during the making of Minesweeper it won't be a problem to add it. At the moment I am writing a couple of zeros to test how the dynamic text field fits in the enemy shape. Now we are ready to code. Adding and controlling the spaceship As usual we know we are going to use classes to manage both enter frame and mouse click events, so we'll import all the required classes immediately. The spaceship is controlled with the mouse, but can only move along x-axis. Without closing astro_panic.fla, create a new file and from New Document window select ActionScript 3.0 Class. Save this file as Main.as in the same path you saved astro_panic.fla. Then write: package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; public class Main extends Sprite { private var spaceship:spaceship_mc; public function Main() { placeSpaceship(); addEventListener(Event.ENTER_FRAME,onEnterFrm); } private function placeSpaceship():void { spaceship=new spaceship_mc(); addChild(spaceship); spaceship.y=479; } private function onEnterFrm(e:Event):void { spaceship.x=mouseX; if (spaceship.x<30) { spaceship.x=30; } if (spaceship.x>610) { spaceship.x=610; } } }} At this time you should know everything about the concept behind this script. placeSpaceship is the function which constructs, adds to Display List and places the spaceship_mc DisplayObject called spaceship. In enter_frame function we just move the spaceship in the same position of the x-axis of the mouse. We don't want the spaceship to hide in a corner, so it won't be able to follow the axis of the mouse if it gets too close to stage edges. Test the movie, and move the mouse. Your spaceship will follow it, while being bound to the ground. Now we should give the spaceship an old arcade look. Adding a glow filter AS3 allows us to dynamically apply a wide range of filters to DisplayObjects on the fly. We'll add a glow filter to simulate old 'arcades' pixel luminosity. flash.filters.GlowFilter class lets us apply a glow effect to DisplayObjects. First, we need to import it. import flash.display.Sprite;import flash.events.Event;import flash.events.MouseEvent;import flash.filters.GlowFilter; At this time, we can simply create a new variable to construct a GlowFilter object. Change placeSpaceship this way: private function placeSpaceship():void { ... var glow_GlowFilter=new GlowFilter(0x00FFFF,1,6,6,2,2); spaceship.filters=new Array(glow);} We specify the color as 0x00FFFF (cyan to draw the spaceship), the alpha (1 = full opacity), and the amount of horizontal and vertical blur (both 6). I want you to notice that I used 6 for horizontal and vertical blur because I like the effect I achieve with such value. If you are planning to use a lot of filters, remember values that are a power of 2 (such as 4 and 8, but not 6) render more quickly than other values. The remaining two arguments are the strength, that determines the spread of the filter (if you use Photoshop, it's something like spread and size of the glow filter you can apply on layers) and the quality. Quality can range from 1 to 15 but values higher than 3 may affect performances and the same final effect can be set playing with blur. Finally the filter is added. spaceship.filters=new Array(glow); filters DisplayObject's property wants an array with all the filters you want to associate to the DisplayObject. In our case, we are adding only one filter but we have to include it in the array anyway. Test the movie and you will see your spaceship glow. In the previous picture, you can see the difference between the spaceship without and with the glow effect applied. Now your spaceship is ready to fire. Making spaceship fire Nobody would face an alien invasion with a harmless spaceship, so we are going to make it fire. We need to create a variable to manage bullet_mc DisplayObject and I have said the spaceship can fire only one bullet at a time, so we need another variable to tell us if the spaceship is already firing. If it's firing, it cannot fire. If it's not firing, it can fire. Add two new class level variables: private var spaceship:spaceship_mc;private var isFiring_Boolean=false;private var bullet:bullet_mc; isFiring is the Boolean variable that we'll use to determine if the spaceship is firing. false means it's not firing. bullet will represent the bullet itself. The player will be able to fire with mouse click, so a listener is needed in Main function: public function Main() { placeSpaceship(); addEventListener(Event.ENTER_FRAME,onEnterFrm); stage.addEventListener(MouseEvent.CLICK,onMouseCk);} Now every time the player clicks the mouse, onMouseCk function is called. This is the function: private function onMouseCk(e:MouseEvent):void { if (! isFiring) { placeBullet(); isFiring=true; }} It's very easy: if isFiring is false (the spaceship isn't already firing), placeBullet function is called to physically place a bullet then isFiring is set to true because now the spaceship is firing. The same placeBullet function isn't complex: private function placeBullet():void { bullet=new bullet_mc(); addChild(bullet); bullet.x=spaceship.x; bullet.y=430; var glow_GlowFilter=new GlowFilter(0xFF0000,1,6,6,2,2); bullet.filters=new Array(glow);} It's very similar to placeSpaceship function, the bullet is created, added to Display List, placed on screen, and a red glow effect is added. The only thing I would explain is the concept behind x and y properties: bullet.x=spaceship.x; Setting bullet's x property equal to spaceship's x property will place the bullet exactly where the spaceship is at the moment of firing. bullet.y=430; 430 is a good y value to make the bullet seem as it were just fired from the turret. Test the movie, and you will be able to fire a bullet with a mouse click. The bullet at the moment remains static in the point where we fired it. Making the bullet fly To make the bullet fly, we have to define its speed and move it upwards. Then we'll remove it once it leaves the stage and reset isFiring to false to let the player fire again. Add a constant to class level variables: private const BULLET_SPEED_uint=5;private var spaceship:spaceship_mc;private var isFiring_Boolean=false;private var bullet:bullet_mc; BULLET_SPEED is the amount of pixels the bullet will fly at each frame. We won't manage upgrades or power-ups, so we can say its value will never change. That's why it's defined as a constant. To manage bullet movement, we need to add some lines at the end of onEnterFrm function. You may wonder why we are managing both the spaceship and the bullet inside the same class rather than creating a separate class for each one. You'll discover it when you manage enemies' movement, later in this article. Meanwhile, add this code to onEnterFrm function. private function onEnterFrm(e:Event):void { ... if (isFiring) { bullet.y-=BULLET_SPEED; if (bullet.y<0) { removeChild(bullet); bullet=null; isFiring=false; } }} The new code is executed only if isFiring is true. We are sure we have a bullet on stage when isFiring is true. bullet.y-=BULLET_SPEED; Moves the bullet upward by BULLET_SPEED pixels. if (bullet.y<0) { ... } This if statement checks if y property is less than 0. This means the bullet flew off the screen. In this case we physically remove the bullet from the game with removeChild(bullet);bullet=null; and we give the player the capability of firing again with isFiring=false; Test the movie and fire, now your bullets will fly until they reach the top of the stage. Then you will be able to fire again. Since nobody wants to fire for the sake of firing, we'll add some enemies to shoot down.
Read more
  • 0
  • 0
  • 4272

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

Why should I make cross-platform games?

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

Subtitles – tracking the video progression

Packt
06 Jul 2015
10 min read
In this article by Roberto Ulloa, author of the book Kivy – Interactive Applications and Games in Python Second Edition, we will learn how to use the progression of a video to display subtitles at the right moment. (For more resources related to this topic, see here.) Let's add subtitles to our application. We will do this in four simple steps: Create a Subtitle widget (subtitle.kv) derived from the Label class that will display the subtitles Place a Subtitle instance (video.kv) on top of the video widget Create a Subtitles class (subtitles.py) that will read and parse a subtitle file Track the Video progression (video.py) to display the corresponding subtitle The Step 1 involves the creation of a new widget in the subtitle.kv file: 1. # File name: subtitle.kv 2. <Subtitle@Label>: 3.     halign: 'center' 4.     font_size: '20px' 5.     size: self.texture_size[0] + 20, self.texture_size[1] + 20 6.     y: 50 7.     bcolor: .1, .1, .1, 0 8.     canvas.before: 9.         Color: 10.            rgba: self.bcolor 11.         Rectangle: 12.             pos: self.pos 13.             size: self.size There are two interesting elements in this code. The first one is the definition of the size property (line 4). We define it as 20 pixels bigger than the texture_size width and height. The texture_size property indicates the size of the text determined by the font size and text, and we use it to adjust the Subtitles widget size to its content. The texture_size is a read-only property because its value is calculated and dependent on other parameters, such as font size and height for text display. This means that we will read from this property but not write on it. The second element is the creation of the bcolor property (line 7) to store a background color, and how the rgba color of the rectangle has been bound to it (line 10). The Label widget (like many other widgets) doesn't have a background color, and creating a rectangle is the usual way to create such features. We add the bcolor property in order to change the color of the rectangle from outside the instance. We cannot directly modify parameters of the vertex instructions; however, we can create properties that control parameters inside the vertex instructions. Let's move on to Step 2 mentioned earlier. We need to add a Subtitle instance to our current Video widget in the video.kv file: 14. # File name: video.kv 15. ... 16. #:set _default_surl      "http://www.ted.com/talks/subtitles/id/97/lang/en" 18. <Video>: 19.     surl: _default_surl 20.     slabel: _slabel 21.     ... 23.     Subtitle: 24.         id: _slabel 25.         x: (root.width - self.width)/2 We added another constant variable called _default_surl (line 16), which contains the link to the URL with the corresponding subtitle TED video file. We set this value to the surl property (line 19), which we just created to store the subtitles' URL. We added the slabel property (line 20), that references the Subtitle instance through its ID (line 24). Then we made sure that the subtitle is centered (line 25). In order to start Step 3 (parse the subtitle file), we need to take a look at the format of the TED subtitles: 26. { 27.     "captions": [{ 28.         "duration":1976, 29.         "content": "When you have 21 minutes to speak,", 30.         "startOfParagraph":true, 31.         "startTime":0, 32.     }, ... TED uses a very simple JSON format (https://en.wikipedia.org/wiki/JSON) with a list of captions. Each caption contains four keys but we will only use duration, content, and startTime. We need to parse this file, and luckily Kivy provides a UrlRequest class (line 34) that will do most of the work for us. Here is the code for subtitles.py that creates the Subtitles class: 33. # File name: subtitles.py 34. from kivy.network.urlrequest import UrlRequest 36. class Subtitles: 38.     def __init__(self, url): 39.         self.subtitles = [] 40.         req = UrlRequest(url, self.got_subtitles) 42.     def got_subtitles(self, req, results): 43.         self.subtitles = results['captions'] 45.     def next(self, secs): 46.         for sub in self.subtitles: 47.             ms = secs*1000 - 12000 48.             st = 'startTime' 49.             d = 'duration' 50.             if ms >= sub[st] and ms <= sub[st] + sub[d]: 51.                 return sub 52.         return None The constructor of the Subtitles class will receive a URL (line 38) as a parameter. Then, it will make the petition to instantiate the UrlRequest class (line 40). The first parameter of the class instantiation is the URL of the petition, and the second is the method that is called when the result of the petition is returned (downloaded). Once the request returns the result, the method got_subtitles is called(line 42). The UrlRequest extracts the JSON and places it in the second parameter of got_subtitles. All we had to do is put the captions in a class attribute, which we called subtitles (line 43). The next method (line 45) receives the seconds (secs) as a parameter and will traverse the loaded JSON dictionary in order to search for the corresponding subtitle that belongs to that time. As soon as it finds one, the method returns it. We subtracted 12000 microseconds (line 47, ms = secs*1000 - 12000) because the TED videos have an introduction of approximately 12 seconds before the talk starts. Everything is ready for Step 4, in which we put the pieces together in order to see the subtitles working. Here are the modifications to the header of the video.py file: 53. # File name: video.py 54. ... 55. from kivy.properties import StringProperty 56. ... 57. from kivy.lang import Builder 59. Builder.load_file('subtitle.kv') 61. class Video(KivyVideo): 62.     image = ObjectProperty(None) 63.     surl = StringProperty(None) We imported StringProperty and added the corresponding property (line 55). We will use this property by the end of this chapter when we we can switch TED talks from the GUI. For now, we will just use _default_surl defined in video.kv (line 63). We also loaded the subtitle.kv file (line 59). Now, let's analyze the rest of the changes to the video.py file: 64.     ... 65.     def on_source(self, instance, value): 66.         self.color = (0,0,0,0) 67.         self.subs = Subtitles(name, self.surl) 68.         self.sub = None 70.     def on_position(self, instance, value): 71.         next = self.subs.next(value) 72.         if next is None: 73.             self.clear_subtitle() 74.         else: 75.             sub = self.sub 76.             st = 'startTime' 77.             if sub is None or sub[st] != next[st]: 78.                 self.display_subtitle(next) 80.     def clear_subtitle(self): 81.         if self.slabel.text != "": 82.             self.sub = None 83.             self.slabel.text = "" 84.             self.slabel.bcolor = (0.1, 0.1, 0.1, 0) 86.     def display_subtitle(self, sub): 87.         self.sub = sub 88.         self.slabel.text = sub['content'] 89.         self.slabel.bcolor = (0.1, 0.1, 0.1, .8) 90. (...) We introduced a few code lines to the on_source method in order to initialize the subtitles attribute with a Subtitles instance (line 67) using the surl property and initialize the sub attribute that contains the currently displayed subtitle (line 68), if any. Now, let's study how we keep track of the progression to display the corresponding subtitle. When the video plays inside the Video widget, the on_position event is triggered every second. Therefore, we implemented the logic to display the subtitles in the on_position method (lines 70 to 78). Each time the on_position method is called (each second), we ask the Subtitles instance (line 71) for the next subtitle. If nothing is returned, we clear the subtitle with the clear_subtitle method (line 73). If there is already a subtitle in the current second (line 74), then we make sure that there is no subtitle being displayed, or that the returned subtitle is not the one that we already display (line 164). If the conditions are met, we display the subtitle using the display_subtitle method (line 78). Notice that the clear_subtitle (lines 80 to 84) and display_subtitle (lines 86 to 89) methods use the bcolor property in order to hide the subtitle. This is another trick to make a widget invisible without removing it from its parent. Let's take a look at the current result of our videos and subtitles in the following screenshot: Summary In this article, we discussed how to control a video and how to associate the subtitles element of the screen with it. We also discussed how the Video widget incorporates synchronization of subtitles that we receive in a JSON format file with the progression of the video and a responsive control bar. We learned how to control its progression and add subtitles to it. Resources for Article: Further resources on this subject: Moving Further with NumPy Modules [article] Learning Selenium Testing Tools with Python [article] Python functions – Avoid repeating code [article]
Read more
  • 0
  • 0
  • 4049

article-image-microsoft-xna-40-game-development-receiving-player-input
Packt
02 Jul 2012
11 min read
Save for later

Microsoft XNA 4.0 Game Development: Receiving Player Input

Packt
02 Jul 2012
11 min read
(For more resources on Microsoft XNA 4.0, see here.) Adding text fields I'm generally a big fan of having as few text fields in an application as possible, and this holds doubly true for a game but there are some occasions when receiving some sort of textual information from the player is required so in these regrettable occasions, a textbox or field may be an appropriate choice. Unfortunately, a premade textbox isn't always available to us on any given gaming project, so sometimes we must create our own. Getting ready This recipe only relies upon the presence of a single SpriteFont file referring to any font at any desired size. How to do it... To start adding textboxes to your own games: Add a SpriteFont to the solution named Text: <?xml version="1.0" encoding="utf-8"?><XnaContent >    <FontName>Segoe UI Mono</FontName>    <Size>28</Size>    <Spacing>0</Spacing>    <UseKerning>true</UseKerning>    <Style>Regular</Style>    <CharacterRegions>      <CharacterRegion>        <Start>&#32;</Start>        <End>&#126;</End>      </CharacterRegion>    </CharacterRegions>  </Asset></XnaContent> Begin a new class to contain the text field logic, and embed a static mapping of the keyboard keys to their respective text characters: class Textbox{           private static Dictionary<Keys, char> characterByKey; Create a static constructor to load the mapping of keys to characters: static Textbox(){    characterByKey = new Dictionary<Keys, char>()    {        {Keys.A, 'a'},        {Keys.B, 'b'},        {Keys.C, 'c'},        {Keys.D, 'd'},     {Keys.E, 'e'},        {Keys.F, 'f'},        {Keys.G, 'g'},        {Keys.H, 'h'},        {Keys.I, 'i'},        {Keys.J, 'j'},        {Keys.K, 'k'},        {Keys.L, 'l'},        {Keys.M, 'm'},        {Keys.N, 'n'},        {Keys.O, 'o'},        {Keys.P, 'p'},        {Keys.Q, 'q'},        {Keys.R, 'r'},        {Keys.S, 's'},        {Keys.T, 't'},        {Keys.U, 'u'},        {Keys.V, 'v'},        {Keys.W, 'w'},        {Keys.X, 'x'},        {Keys.Y, 'y'},        {Keys.Z, 'z'},        {Keys.D0, '0'},        {Keys.D1, '1'},        {Keys.D2, '2'},        {Keys.D3, '3'},        {Keys.D4, '4'},        {Keys.D5, '5'},        {Keys.D6, '6'},        {Keys.D7, '7'},        {Keys.D8, '8'},        {Keys.D9, '9'},        {Keys.NumPad0, '0'},        {Keys.NumPad1, '1'},        {Keys.NumPad2, '2'},        {Keys.NumPad3, '3'},        {Keys.NumPad4, '4'},        {Keys.NumPad5, '5'},        {Keys.NumPad6, '6'},        {Keys.NumPad7, '7'},        {Keys.NumPad8, '8'},        {Keys.NumPad9, '9'},        {Keys.OemPeriod, '.'},        {Keys.OemMinus, '-'},    {Keys.Space, ' '}    };} Add the public instance fields that will determine the look and content of the display: public StringBuilder Text;public Vector2 Position;public Color ForegroundColor;public Color BackgroundColor;public bool HasFocus; Include instance-level references to the objects used in the rendering: GraphicsDevice graphicsDevice;SpriteFont font;SpriteBatch spriteBatch;        RenderTarget2D renderTarget;KeyboardState lastKeyboard;bool renderIsDirty = true; Begin the instance constructor by measuring the overall height of some key characters to determine the required height of the display, and create a render target to match: public Textbox(GraphicsDevice graphicsDevice, int width, SpriteFont font){    this.font = font;    var fontMeasurements = font.MeasureString("dfgjlJL");    var height = (int)fontMeasurements.Y;    var pp = graphicsDevice.PresentationParameters;    renderTarget = new RenderTarget2D(graphicsDevice,        width,        height,        false, pp.BackBufferFormat, pp.DepthStencilFormat); Complete the constructor by instantiating the text container and SpriteBatch:     Text = new StringBuilder();    this.graphicsDevice = graphicsDevice;    spriteBatch = new SpriteBatch(graphicsDevice);} Begin the Update() method by determining if we need to take any notice of the keyboard: public void Update(GameTime gameTime){    if (!HasFocus)    {        return;    } Retrieve all of the keys that are currently being depressed by the player and iterate through them, ignoring any that have been held down since the last update: var keyboard = Keyboard.GetState();foreach (var key in keyboard.GetPressedKeys()){    if (!lastKeyboard.IsKeyUp(key))    {        continue;    } Add the logic to remove a character from the end of the text, if either the Backspace or Delete key has been pressed: if (key == Keys.Delete ||    key == Keys.Back){    if (Text.Length == 0)    {        continue;    }    Text.Length--;    renderIsDirty = true;    continue;} Complete the loop and the method by adding the corresponding character for any keys we recognize, taking note of the case as we do so: char character;        if (!characterByKey.TryGetValue(key, out character))        {            continue;        }        if (keyboard.IsKeyDown(Keys.LeftShift) ||        keyboard.IsKeyDown(Keys.RightShift))        {            character = Char.ToUpper(character);        }        Text.Append(character);        renderIsDirty = true;    }                lastKeyboard = keyboard;} Add a new method to render the contents of the text field to RenderTarget if it has changed: public void PreDraw(){    if (!renderIsDirty)    {        return;    }    renderIsDirty = false;    var existingRenderTargets = graphicsDevice.GetRenderTargets();    graphicsDevice.SetRenderTarget(renderTarget);    spriteBatch.Begin();    graphicsDevice.Clear(BackgroundColor);    spriteBatch.DrawString(        font, Text,         Vector2.Zero, ForegroundColor);    spriteBatch.End();    graphicsDevice.SetRenderTargets(existingRenderTargets);} Complete the class by adding a method to render the image of RenderTarget to the screen: public void Draw(){    spriteBatch.Begin();    spriteBatch.Draw(renderTarget, Position, Color.White);    spriteBatch.End();} In your game's LoadContent() method, create a new instance of the text field: Textbox textbox;protected override void LoadContent(){    textbox = new Textbox(        GraphicsDevice,         400,         Content.Load<SpriteFont>("Text"))    {        ForegroundColor = Color.YellowGreen,        BackgroundColor = Color.DarkGreen,        Position = new Vector2(100,100),        HasFocus = true    };} Ensure that the text field is updated regularly via your game's Update() method: protected override void Update(GameTime gameTime){    textbox.Update(gameTime);    base.Update(gameTime);} In your game's Draw() method, let the text field perform its RenderTarget updates prior to rendering the scene including the text fi eld: protected override void Draw(GameTime gameTime){    textbox.PreDraw();    GraphicsDevice.Clear(Color.Black);    textbox.Draw();    base.Draw(gameTime);} Running the code should deliver a brand new textbox just waiting for some interesting text like the following: How it works... In the Update() method, we retrieve a list of all of the keys that are being depressed by the player at that particular moment in time. Comparing this list to the list we captured in the previous update cycle allows us to determine which keys have only just been depressed. Next, via a dictionary, we translate the newly depressed keys into characters and append them onto a StringBuilder instance. We could have just as easily used a regular string, but due to the nature of string handling in .NET, the StringBuilder class is a lot more efficient in terms of memory use and garbage creation. We could have also rendered our text directly to the screen, but it turns out that drawing text is a mildly expensive process, with each letter being placed on the screen as an individual image. So in order to minimize the cost and give the rest of our game as much processing power as possible, we render the text to RenderTarget only when the text changes, and just keep on displaying RenderTarget on screen during all those cycles when no changes occur. There's more... If you're constructing a screen that has more than one text field on it, you'll find the HasFocus state of the text field implementation to be a handy addition. This will allow you to restrict the keyboard input only to one text field at a time. In the case of multiple text fields, I'd recommend taking a leaf from the operating system UI handbooks and adding some highlighting around the edges of a text field to clearly indicate which text field has focus. Also, the addition of a visible text cursor at the end of any text within a text field with focus may help draw the player's eyes to the correct spot. On the phone If you do have access to a built-in text field control such as the one provided in the "Windows Phone XNA and Silverlight" project type, but still wish to render the control yourself, I recommend experimenting with enabling the prebuilt control, making it invisible, and feeding the text from it into your own text field display.
Read more
  • 0
  • 0
  • 4001
Modal Close icon
Modal Close icon