29
Android Game Development Android Game Engine – Advanced topics

Android Game Development

  • Upload
    gad

  • View
    70

  • Download
    0

Embed Size (px)

DESCRIPTION

Android Game Development. Android Game Engine – Advanced topics. Base project. Download our base project and open it in NetBeans cg.iit.bme.hu/ gamedev /KIC/11_AndroidDevelopment/11_05_Android_AnimationScripts_Base.zip Change the android sdk location path - PowerPoint PPT Presentation

Citation preview

Billboard, Particle System

Android Game Development Android Game Engine Advanced topics1Base projectDownload our base project and open it in NetBeanscg.iit.bme.hu/gamedev/KIC/11_AndroidDevelopment/11_05_Android_AnimationScripts_Base.zipChange the android sdk location pathPer-user properties (local.properties)sdk.dir=change path hereStart an emulatorBuildRunTo compile the downloaded project in Netbeans we should set our android sdk location, which is located in local.properties. We can start an emulator and run the basic project. In this lesson we will examine some advanced classes already provided with the base project, that implement more complex engine features.2AnimationChange of a value in timeWhat?PositionColor orientationHow to store?Can be multi dimensionalArray of floatsHow to define the change of the valueKeyframe animationInterpolation (step/linear)

Virtual worlds are usually not static: object are moving according to physical laws or their motion is driven and is planed in advance. The planed motion is called animation, and animation really means the change of a value in time. The type of attributes that can be animated is countless, it can be color, position orientation and much more. Some of these values are not even single scalars (like position is usually a triple). To define a general animated value type, we handle all animated attributes as array of floats. The dimension of the array can be arbitrary.

Now our task is to define the change of the array elements with respect to elapsed time. One simple method could be to define a function and evaluate it at each time step. This works quite well for movements that can be described with common functions well, like cyclic motions, linear motions etc. However complex motions a much harder to describe with functions.

The most widely used solution is to use keyframe animation, where we store the animated values at relevant time positions and try to interpolate the values between these keyframes. In general cases keyframes can be placed at arbitrary time positions, and interpolation techniques can vary from simple linear interpolation to complex curve based interpolations.

In our implementation we restrict keyframe animation to fixed time steps and linear interpolation. We can also choose not to use interpolation at all, thus make a sudden change when we reach a new keyframe (a step function interpolation).3AnimationTrackData - AnimationTrackAnimationTrackDataRepresents a keyframe animated valueStores a float array for each keyframeKeys are evenly placed in timeThe arrays should have the same size (the dimension of the value)AnimationTrackRepresents a keyframe animationWorks on an AnimationTrackDataHandles animation timeUpdates current value (interpolation)Playback speed can be givenAnimation can loopIn our base project we can find the classes that implement basic keyframe animation features.

The AnimationTrackData class represents the value that should be keyframe animated and interpolated. Here we store the float array for each defined keyframes. The keyframes are venly placed in time. The user should pass all information in each frame, thus the values stored in each keyframe should have the same dimension.

The AnimationTrack class represents one keyframe animation (the animation of one attribute, which can be multidimensional). It works on an instance of an AnimationTrackData. Time and interpolation is handled by the AnimationTrack class. It stores and updates the actual value of the animated attribute. We can give a playback speed, or set if the animation playback should be looped or not.

Using these classes we can easily implement a moving floor for example (or an elevator).4AnimationTrack examplefloat keys[][] = {{-1,0},{1,0},{-1,0}};AnimationTrackData atd = new AnimationTrackData(2);atd.addKeyframe(keys[0]);atd.addKeyframe(keys[1]);atd.addKeyframe(keys[2]);AnimationTrack animTrack = new AnimationTrack(atd);animTrack.setUseLinearInterpolation(true);animTrack. setLoop(true);//dt between two frames should be 0.5 sec.animTrack. setPlaybackSpeed(2.0);animTrack.update(t,dt);float currentX = (animTrack. getCurrentValue())[0];float currentY = (animTrack. getCurrentValue())[1];mySceneNode.setPosition(currentX, currentY, 0);Here we can see an example of using the AnimationTrackData and AnimationTrack classes. It implements an animation of a scene node. This animation is a looping horizontal movement from left to right and back to left again. (Note that this animation can also be implemented with a sin function much more easier, but this is only a simple example, these classes show their benefits in more complicated situations)First we need to define the keyframe data: 2D positions. We have 3 keyframes: leftmost state, the rightmost state, and back to the leftmost state again.We create the AnimationTrackData instance and add the three keyframe data to it. Then we create the AnimationTrack instance and make it work on our animation track data created before. We change the interpolation to linear and make the animation looping. We can also set the playback speed. If playback speed is 1 it means that the time between two keyframes should be 1 second. If the playback speed doubles, the time interval between two keyframes halves.

During game state update we should call the update method of our animation track to refresh its state, then we can get the current state values and use them.5AnimatedTextureA texture that contains frames of animationsCan contain several animations (assigned with a unique name)Texture coordinates will be transformed with the texture matrix to use only a subregion of the textureSubregion is defined with a bounding box (min/max corners)The bounding box is calculated in each rendered frame using an AnimationTrackAnimationTrackData contains 4 floats: minX, minY, maxX, maxYWe have already seen how we can make 3D characters move with skeleton animation and skinning. Now in case of a 2D game this technique is too complicated and we will use a much more traditional 2D method: sprite animation. In sprite animation we draw several images of our animated subject, each image shows the object at a representative time step of its animation. The motion is achieved with alternating the displayed images at a suitable frame rate. This is really the same technique that we use in motion picture.

In our implementation the several images are all stored in one texture. This is due to efficiency reasons. To draw only a portion of this texture (thus only one image of the animation), we should scale and transform the texture coordinates correctly according to the texture space bounding box of the subimage. These texture coordinates should be refreshed in each frame and changed if necesarry (step the animation forward with one keyframe). As this technique requires the animation of a four component value: the bounding box coordinates of sub images, we can use our AnimationTrackData and AnimationTrack classes to do this for us.

6AnimatedTexture exampleAnimatedTexture at = new AnimatedTexture("myAnimTexture");AnimationTrackData atd = new AnimationTrackData(4);float keys0[] = {0.0f, 0.0f, 0.2f, 0.2f};float keys1[] = {0.2f, 0.0f, 0.4f, 0.2f};float keys2[] = {0.4f, 0.0f, 0.6f, 0.2f};float keys3[] = {0.0f, 0.0f, 0.2f, 0.2f};atd.addKeyframe(keys0);atd.addKeyframe(keys1);atd.addKeyframe(keys2);atd.addKeyframe(keys3);at.addAnimation("walk", atd, loopAnimation, animationPlaybackSpeed );at.setFileName("animtex.png");at.load();...at.lookLeft(true); //mirror subimage in the x directionat.playAnimation("walk");at.update(t, dt);Here we can see an example of using the animated texture class. We create an animation texture with a unique name. Create an animation track instance and fill it up with the texture coordinates of all keyframes. We add an animation to the animated texture, with passing a unique string that identifies the animation, and passing the animation track data that describes the texture coordinate values for the given animation. An AnimationTrack class is created automatically fro the AnimationTrackData. We can add several animations here, if we like. We should tell the file name to load, and call the load function. Now are texture is ready to be used.During rendering we can set an active animation at any time, and we should call the update method in each frame. 2D characters usually have a look direction, but the animation for walking left and for walking right is the same, only the image is flipped horizontally. To do this we only need to store one direction and can flip the animation with calling lookLeft().7MeshEntitypublic void update(float t, float dt) { Texture texture = this.material.getTexture(); if(texture != null && texture instanceof AnimatedTexture) ((AnimatedTexture) texture).update(t, dt); }Our MeshEntity class is also extended, that if it refers to an animated texture, the update method of the texture should also be called.8AnimatedTexture example

Here we show an example texture that stores a running animation. The subregion of the first keyframe and its bounding rectangle coordinates are shown in red.9SpriteMap exampleAlso a texture that contains subimages defined with bounding boxesEach subimage has its unique name (literal)The texture coordinates are transformed with the texture matrixOnly one literal can be active at a timeCan draw the subimage to screen at a given position and with a given height (width is adjusted to bounding box ratio)Can be used to store several small images in one texture (these images are related)E.g.: GUI strings Images of numbers or lettersThis class can be extended to implement a bitmap font drawing (StringDrawer)Our project also contains a class called SpriteMap. This also represent a texture that stores several images. The definition and rendering of subimages is similar to what we have seen in case of animated textures: they are defined with bounding box coordinates and texture coordinates are scaled and offset properly during render. But this image is not used for animation. It is really a container if images identified by a unique name. Storing several images in one big map can have performance benefits as the number of texture state changes can be reduced. This can be useful for several small sized textures if these textures are related to each other.Typically GUI strings can be stored in a single map, or the images of letters and numbers to draw arbitrary message on screen. To do this this class can be extended to implement a bitmap font drawing object. In our project this class is called StringDrawer.10SpriteMap example

First we show an example of a sprite map texture. Here we store non interactive GUI elements in our map. We created images for strings like Score or Life, but for integer number drawing need to place the images of all numbers and use the string drawer to draw them.11SpriteMap exampleSpriteMap sm = new SpriteMap("MySMap"); sm.setFileName(HUD.png"); sm.addLiteral("score", scoreMinX, scoreMinY, scoreMaxX, scoreMaxY); sm.addLiteral("life", lifeMinX, lifeMinY, lifeMaxX, lifeMaxY); sm.load(); ... sm.draw("score", x, y, height, alignLeft, alignBottom);Here we see an example of using a sprite map texture. We create a new SpriteMap and set its file name. We can add literals by defining their unique name and texture space bounding boxes. After loading the resource the texture is ready to be used. We can draw one literal in the map by calling draw() and passing the name of the literal, the screen space position, the height of the image in screen space (the width is calculated automatically). The image can be aligned to use the given coordinates as a center, or as a left/bottom corner.12StringDrawerpackage gamedev.android.UEngine.Engine;

import gamedev.android.UEngine.Engine.Resources.SpriteMap;

public class StringDrawer { public static void drawString(SpriteMap map, String s, float x, float y, float h){ char[] chars = s.toCharArray(); float t = x; for(int i = 0; i < chars.length; i++) { t += map.draw(""+chars[i], t, y, h, true, true); } }}Here we show the StringDrawer class. It draws a string to the screen using the given sprite map. It splits the message to characters and tries to find the literal for them. It calculates the horizontal offset for each character to properly align them. This class works correctly only if all possible characters are defined in the sprite map. In our game we will use only numbers in our string, so only the number literals should be set up correctly.13Script loaders I.Store material and texture definitions in scriptsEasier to editNot hardwired in codeHuman readable (no programming skills needed)We will use XML files to store propertiesPlace new classes in new package: Engine.ScriptsSo far we defined our materials from code. A more comfortable and effective solution is to load them from configuration files: resource scripts. This makes editing them more easy as script have a human readable formats, and if they are not hardwired in code, no recompilation is needed if a resource is changed. In our implementation we used XML files to store resource properties but we can also consider to write loaders for other formats like JSON too. The new script loader classes are placed in the Scripts package under Engine.14Script loaders II.ScriptLoader classBase class XML pull parser, state machine handlingTextureScriptLoaderloads a Texture, AnimatedTexture or SpriteMapMaterialScriptLoaderLoads a MaterialUsually refers to a texture nameKeyframeAnimationDataScriptLoaderLoads an AnimationTrackDataThe data can be passed to an AnimationTrack or AnimatedTextureMeshEntityScriptLoaderLoads a MeshEntityRefers to a material name and a mesh namePhysics2DObjectScriptLoaderLoads a Physics2DObjectBox and circle shapes can be definedFriction, density, restitution and type (static/dynamic/kinematic) can be given

We have a base class called ScriptLoader that defines the common functionality to handle the state machine of loading an xml hierarchy using an XML pull parser.

The texture script loader can load textures, regular, animated or sprite map.The material script loader loads material attributes and refers to a texture name.Animation data can be loaded with the KayFrameAnimationDataScriptLoader. It fills an AnimationTrackData instance.We can load MeshEntities with the MeshEntityScriptLoader.2D physics objects are loaded with the Physics2DObjectScriptLoader. In these scripts the collision shapes (circles or rectangels), and physical properties can be defined.15Script loaders III.Game related script loaders are in PlatformGame packageGameObjectScriptLoaderLoads a game objectMeshEntity and Physics2DObject can be definedInitial position and orientation can be definedLevelScriptLoaderLoads a levelLevel scripts list gameobjectsAll game related script loaders are ocated in the PlatformGame package.We can load GameObjects and Levels from scripts. The game object script can define a mesh entity and a physics2dobject, and set the initial position and orientation of the game object. The level script simply lists game objects.16Resource managers with script loadingpublic class TextureManager extends ResourceManager{ private TextureScriptLoader loader = new TextureScriptLoader(); protected Texture loadImpl(String name){ this.loader.loadScript(name + ".texture.xml"); return this.loader.getTexture(); }}

public class MaterialManager extends ResourceManager{ private MaterialScriptLoader loader = new MaterialScriptLoader(); protected Material loadImpl(String name){ this.loader.loadScript(name + ".material.xml"); return this.loader.getMaterial(); }}The resource managers are also extended to use script loaders. In the loadImpl function they search for a script file with the same name as the requested resource and use the script loader to initialize the resource. All resource script files have a special postfix indicating their resource type.This resource loading functionality is the preferred way of creating a resource, as if we create the resource by code we might forget to register it with the resource manager.17Texture script examples

The next slides show resource script examples. First two texture script examples. Note that these scripts should be placed in Brick.texture.xml and g_Item.texture.xml respectively.18Material script examples