----------------------- Development with JGame ----------------------- ----F U N D R A I S E R------------------------------------- Want to support JGame development? DONATE NOW at http://www.13thmonkey.org/~boris/jgame/ ------------------------------------------------------------ Version: 3.6, 10 aug 2012 (20120810), Boris van Schooten This document explains the theoretical background of the engine, design guidelines, and performance issues. We assume that the reader is familiar with Java. An install and compile guide and general overview is found in the README file. More information about usage of the JGame packages, classes, and methods are found in the Javadocs. For those who are already experienced in 2D game programming will probably find most JGame concepts intuitive. So, if you want to get hands-on experience right away, you may want to skip to the tutorials (see tutorial/ directory) and the javadoc. Main project goals ------------------ The JGame project has two main goals: the first is to provide a very high level API that tries to capture the universal concepts found in the majority of "classic" style games. The second is to provide an engine that can run well on as many platforms as possible. So, you can write a game in only a couple of hundred lines of code that works, without any modification, on platforms ranging from a cheap feature phone to a 10 year old Unix workstation to a 3D accelerated desktop PC. Platforms now include J2ME (MIDP2.0/CLDC1.1), Android (2.1+) plain JRE1.3+, and JRE1.4+/OpenGL (in both cases, applications can be deployed as webstart or applet). The graphical capabilities of the different platforms are rather different. To address this problem, JGame provides both basic and extended graphical functions, with the basic functions available on all platforms, and the extended functions falling back to basic functions on platforms that do not support then. Extended graphical functions, such as alpha-blended rotating/zooming sprites, shaded polygons, multilayer parallax scrolling, and rotating/zooming view, are available on OpenGL, Android, and Flash (JGame Flash is a separate package). JGame is designed so that the most common game logic found in typical action games can be programmed with an absolute minimum amount of code. The engine means to provide a complete "boilerplate" API, including not only graphics, sound, and in-game logic, but also game state sequencing, persistent store, highscores, and preferences. This conforms to my own personal wish to create games while avoiding time-consuming repetitive work as much as possible (i.e. the "lazy programmer" approach). Creating an animating object or scrolling the playfield can be done with a single line of code. Making an object remove another when touched, or bump into a particular kind of background feature can be done with only 2-4 lines of code. Initiating a game over, next level, or death sequence can be done with a single line; defining what happens in them can be done with only a couple of lines extra. Programming complex customised behaviour, such as that of a platform game player, a Pacman ghost, or a Gauntlet/Wolfenstein-2D monster, which are among the most complex things found in existing 2D games, is still relatively easy. Programming a prototype game in JGame may take as little as 30 minutes; I also did complete development cycles from concept to release in a few hours. I published more than 25 games total using JGame. Most games took me between a few hours and a few days to write. The engine ---------- Action game programming involves some special techniques which may be surprising to those who are unfamiliar with it. In fact, my experience is that the complexity of game programming is often seriously underestimated. While JGame is based on high-level concepts, we shall nevertheless give a detailed account of its essential inner workings. We shall start with the most basic principles, and proceed with a detailed description of the dynamics that JGame uses. Animation and frame rate ------------------------ Almost all games simulate dynamics in the same way: they update a set of animated objects incrementally at (an either fixed or variable) time intervals (i.e. time steps). This involves updating the objects' positions and painting them on the screen. This technique has similarities with animation techniques. In "cartoon" animation for example, you create an impression of continuous movement by showing a number of successive drawings, with each drawing being a little different from the last. Each such drawing or update is called a frame. For each frame, you should define how the game objects should move one step. In JGame, you can specify this in the method doFrame() in JGEngine. If you don't specify this method, nothing will happen. After the frame's updates are done, the engine paints the game objects at their current positions. Additionally, you can do your own painting (for example for in-game messages or status information) by defining the paintFrame() method. The speed at which successive frames are generated in games varies from about 2 frames per second (for "board game" style motion in old games) to about 100 frames per second (for smooth, realistic movement). JGame uses such frame-based dynamics too. It provides two ways to generate frames: fixed frame rate and video-synchronised frame rate (the latter is OpenGL-only). In fixed frame rate mode, it generates frames at a fixed speed that you can specify. In video synchronised mode, frames are generated in sync with the screen refresh rate of the particular computer the application is running on, which provides the maximum possible animation smoothness. This does mean, however, that the frame rate depends on the particular system, and your application has to adapt to possible changes in frame rate, by adjusting object speeds, animation speeds, timer speeds, etc. to the current frame rate. For example, if you have half the expected frame rate, the number of pixels an object should move should double for each frame, to arrive at the same effective speed. This is significantly more difficult to program than the fixed frame rate case. JGame does provide some features to help deal with variable frame rates. It has a gamespeed variable, which denotes the relative speed at which the game is running, within specified minimum and maximum limits. Gamespeed directly influences most of the standard features (see section "adapting the game speed"). This usually does most of the work, but usually, you still have to adapt some of you game's custom timers and speed settings using gamespeed. Drawing the game objects on screen is usually the most processor-intensive activity of a game application. Games with a fixed frame rate can therefore adapt to slow computers by sometimes skipping the draw action, while still updating the game objects' states. This is called "frame skipping", which results in jerky or jumpy screen motion, while the game keeps running at its original speed. JGame uses frame skipping in fixed frame rate mode, which is the easiest for the application programmer. Of course, you don't want a game to skip all of its frames, so that you can't see what's going on anymore. So, you'll want to set a limit to the amount of frames the engine is allowed to skip before drawing a frame again. If the limit is exceeded, the game will then slow down instead of skipping more frames. In JGame, frame rate and maximum frame skip can be set with setFrameRate(). Note that in fixed frame rate mode, the default gamespeed is 1.0, but it is also possible to set it to a different (fixed) value to speed up or slow down a game, or have two different (but fixed) frame rates for different platforms (in particular, you'll want this for JRE and MIDP). In video synchronised mode, the values passed to setFrameRate are still used, though in a different way. The supplied frame rate is the frame rate at which gamespeed is 1.0. If the actual frame rate is different, gamespeed is set accordingly (double frame rate results in a gamespeed of 0.5 for example). In video synchronised mode, frame skipping is not used. Instead, gamespeed is again used to compensate for slower frame rates, up to a minimum set by the maximum frame skip value. That is, if frameskip is 2 for example (skip a maximum of 2 successive frames), the minimum allowed game speed is 3 (corresponding to a frame rate of one third of the "normal" frame rate). This way you can ensure that gamespeed will stay within reasonable boundaries. Gamespeed also has a hard lower bound, determined by the maximum expected screen refresh rate of just about any system, which is currently hardcoded to 95Hz. If the frame rate somehow exceeds this maximum (this happens on some systems that do not listen to video sync requests), it is throttled by suspending the screen update thread for a short time. Note that some modern games tend to over-produce frames (often producing 100-300 frames per second, while your screen can only show about 60 on average), which is often just to make up for the lack of availability of screen-synchronous operation. Often they eat up all CPU power in the process. If you are looking for a sensible setting of the frame rate, I found that at around 30-70 frames per second, JGame produces animation that's nearly as smooth as higher framerates. On fast machines, the example games will then use as little as 5-10% of CPU. Game object updating -------------------- Action and arcade games typically involve a dynamical environment in which animated agents move around and interact with each other and with the environment. The kind of system involved here has a lot of similarities with a physics simulation model. Games, of course, simplify physical realism a lot, although modern games follow realistic physics models more and more closely. But even the oldest 2D games may be considered physics simulations. For those familiar with parallel and concurrent programming, it may help to know that games involve a unique pattern of parallelism which is rarely found in other applications. In an action game, the animated game objects are effectively parallel processes. Some call this "synchronous parallelism", as it involves updating in sync in discrete time steps. It involves some of the problems (i.e. concurrency problems) also found in other parallel programs. JGame tries to provide a model that gives as little trouble as possible most of the time. It does pay, however, to understand the dynamical model in detail. In JGame, the game objects are the objects of the class JGObject. JGObjects can be created and deleted during the course of the game, and they will animate according to the methods defined in the objects themselves. Each object has move(), hit(), and hit_bg() methods that define how it should resp. be updated, and react to collisions. A collision occurs when two objects physically overlap. Object-object collision is calculated using bounding boxes, which are approximations of the object's physical occupation of space. The bounding box of an object may be specified in several ways, for example, it may be dependent on the image, or fixed. The ideal dimensions of a bounding box depend on factors such as perceived fairness or difficulty level. The JGObjects' move, hit, and hit_bg methods are only called when you tell the engine to do so. From within the doFrame method, you can call moveObjects(), checkCollision(), and checkBGCollision(), which in turn call the move, hit, and hit_bg objects in the game objects as appropriate. In most action games, each frame involves the following things: move all objects one step (usually a few pixels), check if certain types of objects collide with each other, and check if objects bump into features in the environment (i.e. walls, platforms, doors, pickups). So typically you want to call moveObjects() once, then call checkCollision/checkBGCollision several times with different parameters, handling the collision of the different types of object in turn. You can also choose not to call these methods during some frames, or call them multiple times, or in different order, as you see fit for your particular game or in-game sequences. The moveObjects and check...Collision methods, as well as several other methods such as countObjects and removeObjects, take object type specifiers as arguments. Each JGObject is assigned a type (called the collision ID or CID), which is effectively a bitfield. The type specifiers are bitmasks, which match an object's type when the bitwise AND of the specifier with the CID is not zero. Typically, a particular bit in the bitfield being one designates a particular kind of behaviour in the game. For example, the player has type 1, a bullet has type 2, a monster or bomb has type 4, a pickup has type 8. Now you can easily specify that type 2 collides with type 4, and type 1 collides with types 4+8 (type specifier has the value 12). Background tiles can also be assigned types for use with checkBGCollision and the tile interaction methods. Object lifecycle and concurrency issues --------------------------------------- The JGObject's move and hit methods typically change something in the state of the object (such as its position, speed or animation), or may delete the object, create new ones, modify the state of others, or modify the global game state. These methods should just do their updates as necessary for this frame, then return immediately. When moveObjects or check...Collision are called, the engine will call each appropriate method of each object in turn. Even though the objects are conceptually parallel, one should keep in mind that they are in reality updated one at a time. At the moment that one object updates itself, it should be aware that part of the objects have already been updated, and part of them haven't. This is important when the object reads or writes the state of other objects. There are several ways to ensure precisely consistent behaviour. One way is to ensure that the object variables in question are only updated by a single "master object", another is to use the getLast...() methods to get the object's previous state, yet another is to split object movement into multiple moveObjects() calls with different parameters, ensuring one type of object is updated after the other. More issues emerge when objects are created and removed. One issue is whether newly created objects should already participate in the remaining action of the current frame, or should start updating only after the frame ends. In JGame we chose the latter, because this results in more controllable behaviour. In particular, this guarantees a uniform lifecycle of all objects: the order in which the objects' methods are called is always the one defined in the doFrame method. For example, this means we can ensure that an object will always move() first before it can be hit() and is painted on the screen, even if it was created when doFrame was already halfway through its updating steps. Removing objects using remove() is handled differently. Typically you want objects marked for removal to be removed as soon as possible. We chose to delay actual removal until the end of the current moveObjects or check...Collision action, in case the remove was issued by one of the objects during either of these parallel actions. This ensures that any object that is removed during the current parallel action always has the chance to do its own work for that action before it is removed, which is conceptually the most compatible with the parallelism concept of these actions. For example, if two objects of the same type collide, and their hit methods remove the other object (i.e. they try to remove each other), then both will always get the chance to remove the other. In contrast, if an object is removed directly by the doFrame method, it will be removed immediately. The removeObjects() (remove multiple objects) method is special: it specifies an object query, which may match both existing objects and objects which are still pending to be added. This means we can use it to completely clear the playfield of a certain type of object at the beginning of the next frame. For the rest, the query and its corresponding remove action are executed at the same moments as remove(). Note that this causes counter-intuitive behaviour in one important case: if one calls removeObjects() from inside an object during a moveObjects or checkCollision action, then add an object during that same action, the newly added object will be deleted at the end of the action if it matches the removeObjects query. One can circumvent this problem by, for example, doing the removeObjects/new sequence in the doFrame main loop, or setting a timer to do the remove/add sequence at the end of the frame. Game state ---------- In most games, the game's progress can be considered to go through a number of distinct states. There is a title screen, then the game begins, then there's a "level finished" message, a death sequence, or a "game over" message. JGame supports this typical state-based dynamics by defining game states. A game state can be set by means of setGameState(). A game state is represented by a String. In addition to the regular doFrame and paintFrame, the engine will also call doFrame and paintFrame methods, in case these methods exist. Here, you can specify state-specific action and painting. In addition, a method start is called once at the beginning of the frame, where you can specify state initialisation code. Game states are based on the frame update cycle, hence a game state always lasts an entire frame, and state changes only take effect on the next frame. Timers ------ JGame also has timers, represented by the JGTimer class. A JGTimer defines an alarm method, which is called when the timer expires. Timers always tick and alarm at the very beginning of a frame, and any objects they remove or add will be removed and added before that frame begins. They may be used to specify special global or local events (just make the JGTimer an inner class of the desired object to be updated). Initialising a timer to 0 means it will alarm() right after the current frame ends. This can be used to define special actions to be done at the end of the current frame, such as initialisation before a game state transition. JGTimers can be made dependent on either a game state or a JGObject (their parent), which means they will remove themselves as soon as the state or object expires. Principles of tile-based background ----------------------------------- As in many games, JGame uses a tile-based scheme to manage the (relatively stationary) physical environment in which the animated objects move around. This environment is also called the background. Tiles simplify the issues involved in defining a background, representing it graphically, and managing object-background interaction. The background is defined as a matrix of equal-sized tiles, with tiles being typically square and 10-40 pixels in size. An object collides with a tile when its tile bounding box overlaps it. So, an object with a size between 2 pixels and the tile size may overlap between 1 and 4 tiles at a given moment, depending on its alignment with the tile grid. Each tile shows up as a specific graphic (an image), and has a specific identity, which can be queried when necessary. In a game, the background typically consists of elements with a specific purpose, such as walls (to bump into), floors (to walk over), ladders (to climb onto), bonuses (to pick up), doors (to open or close), etc. With a tile-based background, each of these elements is represented by a specific type of tile. Often, one element corresponds to one tile (for example, a door or a bonus). In other cases, one element corresponds to an array of tiles (a floor may be represented by a horizontal array of tiles, and a ladder by a vertical one), or to any other arrangement (such as block of tiles that represents a wall). Typically, the animated objects have approximately the same size as one tile, so they "fit" quite neatly into the environment. So, a player, sized 1 tile, is precisely large enough to move through a 1-tile wide tunnel, or climb up a 1-tile wide ladder. Alternatively, a more fine-grained background consisting of smaller tiles may be defined, with an object being two or more tiles in size. This is generally more difficult to handle, but may be preferred for aesthetic or playability reasons. Designing tile-based graphics has special issues. Typically, tile graphics are drawn so that they can be seamlessly juxtaposed horizontally or vertically (i.e. they are "tileable") so that they have a natural appearance. Sometimes, multiple graphical variations are used for one tile type for the sake of visual variety. In some cases, larger background objects are formed by forming blocks of tiles, such as a 2x2 object made of 4 different tiles, which correspond to the corners of the object. In JGame, tiles may be transparent, so that a backdrop image can be made to show through the tiles. The backdrop image has no other purpose than decoration. Handling object-object and object-background interaction -------------------------------------------------------- Handling collisions and objects' other reactions to their environment is often the trickiest part of game programming. The simplest are the cases when an object should simply be destroyed or picked up when it collides with an object or a tile. Often though, objects should avoid and steer around tiles or objects, bounce off them, move differently when on a specific type of tile, etc. Doing this properly without the object getting stuck or exhibiting unnatural or unwanted behaviour is the tricky part. A classic example is having balls bounce around an environment and off each other. Anyone who has tried to program this knows that balls tend to get stuck into walls, into each other, may start to oscillate and move erratically through walls, etc. In the case of balls, this usually results from the fact that the balls can't make out in what direction to bounce, and they may bounce off one wall right into another, or into another approaching ball. This general problem is found in both the oldest and the most modern games, and there doesn't seem to be a universal method that's reasonably simple and works in all cases. Particular solutions to particular problems can be devised by analysing the problem, simplifying the situations if possible, and using known guidelines. Modern 3D games often use a full-fledged physics engine to solve this problem, though even such engines are often application-specific in unexpected ways. For 2D games, a full physics engine is usually too heavyweight: the application programmer has to specify more detail and/or give up some control over game dynamics. A basic method for handling object-background interaction is "looking around". One looks at the tiles surrounding the object to decide what to do. This may be done prospectively, having the object look around every step, or retrospectively, having the object look around only after it bumps into something. In the latter case, one usually takes a step backwards to the previous step, when the object still hadn't collided with anything. You can use the getLast... methods for this. A basic method for simplifying object-tile interaction is tile alignment. Here, one ensures that an object is mostly aligned with the tile grid, so that it overlaps only one or two tiles at a time. In its very simplest form, the object moves around in steps of a whole tile at a time. This looks very jumpy however. This kind of primitive "board game" style motion was popular with home computers which used character screens to draw graphics. A more appealing "smooth" version of this can be made by having objects move smoothly from one tile-aligned position to another. This technique is very appropriate for maze games, such as Pac-man. In JGame, one can use the is...Aligned methods to determine when an object is approaching tile alignment, and snapToGrid to ensure that it becomes exactly tile-aligned, and is ready to decide which way to move next. Typically, one wants to snap an object to the tile grid if it is closer to tile alignment than its speed. Note that rounding errors may play a role here, as the objects' positions are floating point values, and often move at non-integer speeds. In this respect, is...Aligned and snapToGrid have default behaviour that should work well most of the time. Tile alignment can also be used to handle special cases for objects which are not normally tile-aligned. For example, if an object bumps into a wall, one can snap it to the tile grid to bump it back to a valid position, and look from there what to do. Examples of solutions to "tricky" interaction can be found in the example games. The WaterWorld game is a prime example of handling different kinds of object-background interaction. Scrolling and wrap-around ------------------------- The area in which the game action takes place is called the playfield. By default, the playfield is the same size as the game window. It is also possible to make the playfield larger than the game window, by using setPFSize. The part of the playfield that is visible in the game window is called the view. The view can be panned using setViewOffset to reveal any part of the playfield, typically it is panned incrementally, only a few pixels per frame in any direction, to obtain an effect of continuity, e.g. scrolling. Typically one moves the view along with the player, either keeping the player in the middle of the screen, or having the player more on the sides to reveal more of what's in front of the player. Thanks to the tile-based system, it is possible to define a background for a playfield that's much larger than the screen without taking too much memory. The engine does not need to store the entire background, it draws just the part that's in view. For the rest, only the tile values need to be stored. One tile costs only 8 bytes of memory. For 32x32 tiles that's around 1/400 of the size of a 32x32 pixel screen area. There's no performance penalty involved in scrolling, except that the tiles that become exposed when the view is panned have to be drawn. JGame also supports scrolling multiple background layers at different speeds, aka parallax scrolling, when using OpenGL. This can be achieved by making some of the background tiles transparent, or using null tiles, and defining one or more background layers by setting background images with setBGImage. The topmost background image (layer 0) shows through the transparent or empty tiles of the tile background. If this image also has transparency, then the layer 1 background image shows through that image, etc. Each layer can be scrolled independently by setting the image offsets using setBGImgOffset. By default, layer 0 scrolls along with the tile background (corresponding to the behaviour found in platforms not supporting parallax scrolling), and layer 1 and higher have offset (0,0). By default, objects happily continue updating while outside of the view. In some cases though, you want objects to wait until they come in view. For example, you might have a large scrolling map (say, 20 screens high or wide), on which hundreds of objects are placed, ready to become active when they enter the view. JGObject's suspend feature is meant for this purpose. An object can be made to suspend (i.e. sleep) when it is off view. By default, an object will resume (i.e. wake up) whenever it comes in view. It is also possible to suspend and resume an object at any other time. A suspended object is invisible and no move/hit methods are called, but it can be counted in countObjects. Objects can also be removed automatically when they exit the view or the playfield. Some games use a playfield that "wraps around", that is, when something exits on one side of the playfield, it reappears on the other side. JGame has a wraparound feature which enables the playfield to wrap around horizontally or vertically. It can be enabled using setPFWrap. Wraparound makes sense both for scrolling and non-scrolling games. Implementing wraparound for non-scrolling games (such as Asteroids) is quite easy in general, and there's no real reason to use the special engine feature. But this is different for scrolling games, for which the tile and object collision logic can be quite tricky. Prime examples are Defender, which wraps horizontally, and Sinistar, which wraps in all directions. The wraparound feature should make implementing this quite easy. With the engine feature, even a non-scrolling Asteroids can be easily made to scroll along with the player's ship by simply using setViewOffset. Theoretically, wrap around means that the playfield is treated as infinitely long in a particular direction, with the content of the playfield (both tiles and objects) repeating periodically. The view offset can be set to any value, and tiles may be read or set to/from any tile coordinate. Mathematically, the tile coordinates are modulo-ed with the playfield size. Objects are treated differently, but should mostly behave intuitively w.r.t. theoretical behaviour. Objects are not modelled as if they are repeated infinitely, instead, they are always on one specific coordinate only. This coordinate is wrapped around so as to ensure that the objects is within the range -playfieldsize ... +playfieldsize of the middle of the current view. This enables the object coordinates to be compared and collision to be calculated as usual in most cases. In some cases, coordinate (distance) comparisons must take into account the wraparound feature, you can use the method getX/YDist for this purpose. If the playfield is the same size as the view, there will be a visible "jump" when the object wraps from one side to the other. This is due to the fact that, otherwise, the object would have to be modelled as being in two positions at once. This can be remedied (as it almost always is in actual games; for a nice counterexample see the C64 game Dino Eggs) by making the playfield just a little bit larger than the view, so that the objects only wrap when off the view. The precise location of the "middle" of the view can also be set by means of a shift parameter. Basically, shift should be 0 if you want to wrap objects that have their coordinates in the center of their sprites, and -spritelength/2 if you want to wrap objects that have their coordinates at the top left of their sprites (as is the case for image-based sprites). Sound ----- JGame has a simple sound system based on applet AudioClips. You can play Wav (sample) and Midi (music) files, and possibly, if you don't mind being platform-specific, other formats. A clip is identified by a clipid, which is associated with an appropriate file using defineAudioClip. Simply playing a clip (playAudio) will play the sample or tune regardless of how much sound is already playing. In some cases you want to stop a previous clip when you start a new one. For example, you only want to play one tune at a time! This can be achieved by using channels. You can play an audio clip on a specific channel (specified by an arbitrary name of your choice), which means that any clip already playing on that channel will be stopped. It is also possible to stop a channel (stopAudio) so that any clip still playing will be stopped. Limiting the playing of a certain clip to only one sound at a time may also be desirable for clips other than music. For example, I found it's unnatural when you hear two pairs of scissors snip simultaneously in Munchies while you only have one, so I limited the snip sound to a channel "scissors". It is also possible to loop a clip so it continues playing until you stop it. Naturally, this is only possible if you play it on a channel. If you play a looped sound while the same looped sound is already playing, it will not be restarted but will simply continue. This is specially designed so that you can call playAudio or stopAudio every frame depending on resp. an "on" or "off" state of your game, without having to know whether you already started or stopped looping. There is a global switch for enabling and disabling audio. This is useful if you want the user to enable/disable game sound. The StdGame class has a settings option for enabling and disabling audio. When audio is disabled, the audio administration is handled as usual, except that actual audio calls are skipped. If you re-enable it the system does its best to restart the audio as it would have sounded as if you never disabled it. In practice, only looping clips are restarted. For future versions, we want to have an enable/disable for music and other sounds separately. We wish to reserve the channel "music" for that purpose. Java has a serious sound latency problem, which is gradually being resolved though. I experienced latencies ranging from 0.05 to 0.7 seconds on different platforms. I thought of providing a getAudioLatency function to query the latency so that you can do something creative with it (such as delay an animation in some way until a sound starts playing) but as yet I find it too hard to figure out which platform has what latency. I noticed most Windows javas have a latency of around 0.1-0.15 second, and in java 1.5 the latency is apparently less. On Linux the new Sun-java also has pretty low latency, but there's a bug (on my machine at least) that freezes the thread when trying to stop a looping clip. Blackdown Java works well, but has a latency around 0.3s. The StdGame framework --------------------- JGame now comes with a StdGame engine that pre-defines some typical game functionality. StdGame is a subclass of JGEngine, and basically defines a standard state machine for games, comprising of states such as Title, StartLevel, InGame, LifeLost, and GameOver, along with some handy timers and special events on certain transitions. Furthermore it defines some basic variables, such as lives, level, game keys, and game fonts. The game has default behaviour for the game states and events that serve as a "raw" framework for a game, which means you can have a working game prototype in no time at all, and define a nicer look and feel for your game later on. The SpaceRun and SpaceRunII examples demonstrate how to use and configure the game framework. StdGame is found in the jgame package. StdGame also provides a basic configuration screen. When you press enter (by default) on the title screen, you get a configuration window where you can configure keys and disable sound. The MIDP version now also has a very simple "enable sound" dialog at the beginning. Disabling sound is an essential option for a MIDP application with sound. Also present are several standard game objects, all starting with Std..., which are found in the examples package, along with the example games. These objects do some of the typical "tricky" interaction, and can be subclassed or modified to fit in your own games. The example games should demonstrate the usage of StdGame and the standard objects well. Adapting the game speed ----------------------- The effective game speed can be defined as the increments at which things are updated at each frame (for example, the number of pixels that an object moves each frame), times the frame rate. Your game may run at a fixed frame rate, variable frame rate, or at one frame rate for one platform, and at another for another platform. If frame rate is decreased, you can maintain the same game speed by increasing all appropriate update increments. You may want to adapt the actual game speed too, for example to gradually increase the difficulty level of the game. Adapting the update increment in a consistent and predictable way is quite difficult in some cases. In practice it usually involves two basic things: namely, changing the per-frame timer increments for any timers you have, and changing the per-frame object move speeds. One has to ensure that the logic of both objects and timers is appropriate for working at different speeds, which is sometimes difficult. In particular collision and tile alignment may be a problem. In general, you have to make decisions about the range of speeds you want to support. While JGame provides no general solution for this problem, there is a game speed setting that helps relieve some of the more boring work. It is handled through setGameSpeed and getGameSpeed, and is set to an appropriate value automatically in video synchronous mode. It influences the built-in update increments, which are the following: it is used as JGTimer tick increment, animation speed multiplier, JGObject expiry increment, multiplier for object x/yspeed, and also determines the default margins of is...Aligned and snapToGrid to be compatible with the multiplied x/yspeed. The StdGame timers also increment using the gamespeed setting. In a basic game in which objects move only by means of x/yspeed, using setGameSpeed may be sufficient for adapting the entire game in one go. General comparison of platforms ------------------------------- Not all platforms implement all features. Some features are not yet implemented, others are not available on some platforms. Note that the flash platform, which is released as a separate package but will be merged eventually, is included in the table. Graphics: alpha/scale/rot pre-rotated img BG parallax layers VideoSync JRE No Yes No No OpenGL Yes Yes Yes Yes MIDP No Yes No No Android Yes No No No Flash Yes No Yes No Sound. JRE Multichannel (with some latency). Wav, midi MIDP Single channel (with some latency). 8 Khz Wav, midi Android Multichannel low-latency. Wav only. Flash Multichannel (with some latency). MP3 (Wav not supported!) Persistent Storage. JRE Yes, applet needs write access to file system MIDP Yes Android Yes Flash Yes On-screen Debugging. JRE Yes OpenGL Yes MIDP No Android No Flash No Game states. JRE Arbitrary game state names MIDP Fixed set of game state names Android Fixed set of game state names (as yet) Flash Fixed set of game state names Input: Keyboard Mouse/Touchscreen Accelerometer JRE Yes Yes No MIDP Yes Yes No Android Yes Yes Yes Flash Yes Yes Yes OpenGL versus Android versus vanilla JRE (AWT) graphics ------------------------------------------------------- You can use OpenGL instead of the regular AWT graphics engine to access enhanced graphical features. Most of the OpenGL enhanced graphics features are also available on Android. The system design is such that an application using OpenGL specific features will still work under AWT, only it will look less nice, and may even look significantly different in some cases. OpenGL rendering is _not_ for providing a speed boost (as the AWT backend is already accelerated; the OpenGL backend is in fact slower), but rather, a quality boost. There are some extended versions of regular drawing methods which provide added functionality when OpenGL is enabled, in particular image and shaded polygon drawing. These methods will use a lower-quality fallback when OpenGL is not available, and for example, produce a plain polygon rather than a shaded one. Additionally, OpenGL uses the alpha component of JGColor, enabling translucency/alpha blending for all draw methods. Alpha is ignored when using the AWT backend. A second advantage of OpenGL rendering is double buffered and screen-synchronous updates. Even in fixed frame rate mode, OpenGL will look better than AWT, because double buffering will prevent the tearing effect (this happens when the game's screen update crosses the screen's physical update), which is especially disturbing for scrolling games. OpenGL also enables the video synchronised mode, which achieves the theoretically maximum animation smoothness. Android does not (yet) support video synced update, but due to Android's double buffering system, tearing is avoided. See the table below: Backend VideoSyncedUpdate Update quality ------------------------------------------------------------------------- AWT/MIDP off tearing, jerky animation AWT/MIDP on NOT AVAILABLE, setting it is ignored OpenGL off no tearing, jerky animation OpenGL on no tearing, smooth animation Android off no tearing, jerky animation Android on NOT AVAILABLE, setting it is ignored Designing for MIDP ------------------ J2ME (and other mobile platforms) suffer from a certain level of device and implementation diversity, which leads to software development problems for the application programmer. Problems include having to test on multiple devices, or worse, maintaining a separate version for each class of devices. This problem is also known as "device fragmentation". Reasons for not having one version often include technical demands that are incompatible with the J2ME platform (such as using softkeys, for which no platform-independent keycodes have been defined, or wanting your MIDI file to sound exactly the same on all devices), and publisher/distributer demands (such as a phone vendor requiring any content providers to use the phone-specific API, or the right icon size, or softkeys). This typically includes maintaining multiple build scripts, or using special software that rewrites the code for each different version that would, in programming languages other than Java, be performed using a set of macros. We will not address these issues here. Instead, JGame tries to solve most of the fragmentation problems using resolution independence and a "greatest common denominator" approach. The basic goal is to have just one version of the software that works well on a great range of devices. The basic premise is that, to develop good 2D games, you don't really need any additional APIs or special phone-specific features. Having just one version is also advantageous to the user, who otherwise has to find out how to get the right version. This may involve selecting phone type and finding that his/her phone is not in the list even though the application may work well (it happens to me all the time). Actually there's a lot of mobile software out there which already makes do with just one version. My experience is that the available input and output devices are actually the worst problem. See below for some tips on how to make your game work on most devices. The current mobile version of JGame inherits almost all features of the original, including scrolling and resolution independence. Only the debug facilities are not found in the mobile version. Usually though, games will need to be adapted to fit the slow processor (typically 10 times slower), small keyboard (JGame chose as "greatest common denominator": 0-9, #, *, cursors, and fire), and small screen (3/4 of existing phones range from 128x128 to 240x320). The default key settings in StdGame are adapted to mobiles, but for fast action games, one typically needs to reduce the frequency of keypresses, since the keyboard is typically small and cumbersome. Some phones do not even support multiple simultaneous keypresses. Even while most newer phones do, you may expect some quirks in this respect. For example, the SE K320i has a joystick used for cursor movement, which is pressed inwards for fire, and due to this design, it is not possible to move the joystick in a direction and press fire simultaneously. Also, the joystick is less responsive to diagonals. So, make sure that your game does not require multiple keys to be pressed to be playable! The range of screen sizes on mobiles actually varies less than that of PCs. Mobiles are mostly 120x160, 176x208, 240x320, or slight variations of these. One major screen size problem is accounting for both horizontal, square, and vertical screens. You can use a square viewport, and use setScalingPreferences to control how it should be stretched. The screens may have high latency, so that at frame rates above about 10-20fps, the action may become very blurry. Setting the frame rate to 10-20fps solves that problem, and also reduces processor consumption. With the recent introduction of touch screens, there are now many phones which do not have a keyboard at all, and the game will have to be controlled by mouse input to be compatible with this class of phones. While a touch screen appears to the programmer as a mouse, it is different and more limited in terms of usability. There is also a big difference between operating touch screens with a finger and with a stylus. You'll really need to test your game on a real touch screen if you want to know if it works usability-wise. In general, I found that a game can be made to run nicely on mobile as well as PC by doing at least two things: reducing the frame rate, and adapting the game speed. When you reduce frame rate, you will have to increase the game object speeds and timer increments by a proportional amount to have the same effective game speed. JGame now has a built in gamespeed variable which automatically adapts some basic behaviour, in particular xspeed/yspeed and timer increments (see section "adapting the game speed"). Since games are more difficult to control on the mobile, you may also want to adapt the effective game speed to run a little slower than the PC version. For example, I might have 45 frames/second on the PC, and 15 frames/second on the mobile, while the PC runs at gamespeed 1.0 and the mobile at 2.0. Then, the effective mobile game speed is 15/45 * 2.0 = 2/3 as fast as the PC game. In some cases, you want to reduce the number of sprites or tiles visible in the view. It's quite easy to reduce the view size while keeping the original playfield size, you just need to scroll more. I myself prefer using relatively many small objects rather than few large objects. Many commercial games use 16x16 sprites, while I think 8x8 or even smaller is more suitable. Only eight 16x16 sprites fit on a 128 pixels wide screen, which is so cramped that gameplay may suffer. Here are some tips for optimising your game. In some cases, it makes sense to make objects suspend when they go out of view (using suspend_off_view). Objects use only little CPU when suspended. You can also decide to use less particles in explosions, etc. in the MIDP version of your game. Some general optimisation tips are: avoid expensive operations, in particular object creation, string manipulation, and even calling methods. It's better to just create a JGPoint once in a JGObject constructor, and then overwrite its contents each time the move() method requires some coordinate manipulation, rather than create a new JGPoint inside the move() method. If you need to traverse a list of items, use a plain array instead of a Vector with an Enumerator. String construction is expensive because it involves multiple object creations, but string comparison is also expensive. Method calling is also relatively expensive. In particular calling get/set methods is significantly slower than just reading a variable. For this reason, JGObject provides a set of (static) variables representing the game and playfield settings, such as tilewidth/height, pfwidth/height, and gamespeed. WTK also provides a profiler which gives very detailed CPU usage information. Saving memory is another significant issue. Jars should be smaller than about 200-300Kb (You can expect a MIDP2.0 phone to support at least 200Kb Jars) and it is preferable to make them less than 100K. JGame's basic Jar footprint is about 60-65Kb, but this can be reduced if you use a bytecode optimiser such as proguard (see README for how to use it). It removes unused methods and reduces the symbol size, so the JGame footprint will shrink to something like 35K for your typical game. Beside Jar size, memory usage should also be kept to a minimum. WTK provides a nice memory profiler where you can monitor memory usage and object counts. MIDP can be rather a nuisance w.r.t. supported file formats. There is no clear minimum guarantee. The documentation only mentions 8Khz 8-bit PCM Wav and PNG as minimum standards. That means you'll have to experiment to find a suitable minimum. For my test devices, I found the following: Sagem My401C: WAV: 8000Hz 16-bit PCM MIDI: supported PNG,GIF,JPG: No large images (such as 240x320). If image is too large, sometimes produces I/O error, sometimes hangs (the Sagem software is rather buggy) Sony Ericsson K320i: WAV: 8000Hz or 11025Hz (11050Hz is already too high) 16-bit PCM MIDI: supported PNG,GIF,JPG: supported up to at least 240x320. Vairy Touch II: WAV: Tested up to 44100Hz 16-bit PCM MIDI: supported PNG,JPG: supported GIF: NOT supported According to these results, it's best to keep WAVs to 8000Hz, and do not use GIFs. You'll probably want JPGs because they can help reduce Jar size. It appears that phone models other than the Sagem also have problems with image sizes much bigger than the screen size, so keep image sizes as small as possible. If possible, do not make them larger than the screen size of your target devices. Designing for Android --------------------- Android devices are more powerful than MIDP devices, and most of them have GPUs, which make OpenGL style effects possible. Yet some of the issues found in MIDP apply here to. Since applications all share the same limited internal SD storage, and internet download speeds are sometimes slow, it is a good idea to keep your app as small as possible. Furthermore, a minority of Android devices have limited memory, which means that memory will easily run out when loading too many images and sounds. Most Android devices do not have a keyboard or even a numeric keypad, so you'll have to rely on touch screen and accelerometer (I believe almost all of them have an accurate accelerometer). Graphical performance varies per manufacturer. Expect basic graphic rendering to be fast, but scaled, rotated, and blended images to be slow on some machines. Performance on screens with more pixels is also significantly slower, so if you develop on a 320x240 device, be sure to check performance on a 640x400 or 800x480 device. Performance issues and troubleshooting -------------------------------------- What machine do I need to run JGame? While JGame should run very well on modern machines, there are a number of things to consider to get optimal results. My overall experience is that proper graphics acceleration makes the biggest difference. When it is available, the games run several times faster. On a 1Ghz machine with acceleration, JGame should only take about 10% CPU in fullscreen, or something like 20% when using JOGL. It can still be run on a 200Mhz machine when graphics acceleration is available. It will also run very smoothly over a network X11 connection (something which OpenGL-based applications cannot do!). Screen size matters a lot, so if your game runs too slow, try a lower resolution. The worst that can happen is that your video memory is full, at which point the game slows down dramatically. This may happen when you have less than 32 Mb video memory. There also seem to be window size limits beyond which the drawing operations slow down, possibly depending on buffer or texture size limits of graphics cards. Why is my applet running slower than my application/webstart? There are two problems with JGame applets. The current version of JGame cannot use VolatileImages in Windows applets, so only applications will benefit from full graphics acceleration. Typically it will be quite fast enough, though. I will fix that in a future version. The second problem is that, for some unknown reason, JOGL applets (those loaded with the JNLPAppletLauncher) run significantly slower than JOGL applications. Why do I get a delay in the keyboard/mouse input? On slower machines and in the WTK emulator, there may be delays in the input: keyboard and mouse actions may be delayed by up to a second. This typically happens when the CPU is seriously hogged, and appears to be caused by the input thread not getting enough time for handling input events. The problem can usually be solved by lowering the resolution or frame rate, especially frame rate is a significant factor. I found it may also be solved by using another threading implementation in some cases. Generally, just ensure that your target system has enough CPU cycles left to handle the input properly. How do I use large images? Large images are generally slow to load (especially from the internet) and scale, and take up a lot of memory. So, when you want to have large background images for instance, you should be aware that it's not a good idea to load them all into memory if you're only going to display only one per level. Moreover, images may occupy video memory, and if the video memory is full, the performance tends to drop dramatically. For machines with 16 or 32 megabytes of video memory, this may be a significant issue when you run JGame fullscreen. Then it's better to load an image only when it's needed, and discard it afterwards. This can be done by re-using the same image id for the different background images. One just loads each new image with defineImage just before the level begins. The old one is removed from memory. This not only saves memory but also chunks the waiting time into smaller, more user friendly pieces. Why is drawing my background / scrolling / text rendering so slow? Drawing transparent tiles in combination with a background image is rather slow on some slower machines, in the current implementation. This will give a performance hit when setting a lot of tiles each frame. Once the tiles are set though, there is no performance penalty compared to any other background. Scrolling will also involve a greater performance penalty, as the system needs to draw newly exposed tiles when scrolling. I also found that on some machines, OpenGL scrolling is very slow. This is apparently because of inefficiencies in the TextureRenderer implementation. I found the same thing with drawing text, which is not so surprising, as TextRenderer also uses TextureRenderer. In some MIDP implementations, drawing text is also slow. If possible, avoid drawing lots of text using drawString; put it in an image instead. When I enable sound, the game slows down and I get hiccups. What's going on? Sound may give a performance hit on all platforms, so be a bit careful if you care about performance. I found that midi is pretty fast, but triggering a lot of samples at a time may hog slower machines some. In Linux/OpenJDK, sound causes serious hiccups in the animation (on my machine). Use the Sun (Oracle) JDK if possible. Why does the animation look jerky? In some cases animation may be less than smooth. This happens when the frame rate is slow and the screen updates are not processed at equal time intervals, or the game's screen update crosses the screen's physical update (tearing). This is especially visible when scrolling quickly, and typically much less when scrolling slowly or not at all. Using OpenGL will make animations look less jerky, as double buffering is enabled on most platforms, and tearing should disappear altogether. On some slow machines (typically 300 Mhz or less) you may also see hiccups about every one or two seconds. These are caused by garbage collections. Since JGame 3.1, the number of object creations has been greatly reduced, but the hiccups should not occur on faster machines anyway. I found that the mobiles I tested do not suffer from garbage collection hiccups. I enabled video synced mode, and animation is still jerky. What's going on? With the OpenGL video synced update mode, animations should be completely smooth, if your machine is fast enough. However, on some platforms, JOGL video sync does not work, and JOGL will produce a high, but not video synced frame rate. I'm not sure why this is, it could be a bug in JOGL, or lack of support in some OpenGL drivers. It may be remedied by increasing the frame rate in fixed frame rate mode. Why does the animation flicker? In some cases, you may see flicker that is reminiscent of a badly implemented screen update algorithm. This is actually caused by your screen being interlaced. I found that many TFT screens are interlaced, that is, they advertise themselves as for example 60 Hz screens, but are 30 Hz 2x interlaced in reality. Flicker will be most visible when you have moving objects with thin lines or stippled patterns, or when scrolling. Avoid such patterns if this is a problem.