tag:blogger.com,1999:blog-73441376530748646952024-02-19T17:49:21.355+02:00game programming algorithmsthis blog is the distillation of my over 10-years experience in game programming. You can find ideas and already implemented algorithms for all stations of the life cycle of the creation of your game. Since I am a fan of the unified field theory I use a pseudo language for the presented algorithms and you can transfer them to whatever language/platform you use. Of course, I have tested and used everything in my own projects and I can assure you that everything works the way it is described.Theodosiοs Ekizoglouhttp://www.blogger.com/profile/09965963637480862598noreply@blogger.comBlogger7125tag:blogger.com,1999:blog-7344137653074864695.post-33343834083218795572016-03-03T11:56:00.006+02:002016-03-03T13:06:26.113+02:00Create cross-platform HTML5 games - Episode I<span style="font-family: "verdana" , sans-serif;">Creating games is the most exciting and challenging area of the software development. Once you have created the next hit game of the year, the challenge for your newborn baby is to support as many different platforms as possible since there are thousands of different devices out there. Thus, unless your target is a specific device, like iPhone, Android, Smart watch, Windows PC, Mac, etc. with previously known technical characteristics, then the need of such a wide support could become a real headache.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFMf2f1CKoMSeBFoDLZoOtlVir6ws6N1Cp-BYgmvCtGxZm9nPHJ9i-J8DudsUVKyo7LbgyDwUA6AITRYXHaUTJidxdj5zyOBDohZRr7ivv1PZjXboNQL30nqWThP9H_zrBYZTBFYYJOg/s1600/responsive_spaceball-300x182_transp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFMf2f1CKoMSeBFoDLZoOtlVir6ws6N1Cp-BYgmvCtGxZm9nPHJ9i-J8DudsUVKyo7LbgyDwUA6AITRYXHaUTJidxdj5zyOBDohZRr7ivv1PZjXboNQL30nqWThP9H_zrBYZTBFYYJOg/s320/responsive_spaceball-300x182_transp.png" width="320" /></a></div>
<br />
<br />
<span style="font-family: "verdana" , sans-serif;">An approach to this is to re-write the source code (or specific parts of it) for every platform using the SDK and the tools for this platform. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">Another approach is to write the game using technologies that come with the promise "write once, run everywhere". One such very promising technology is HTML5. Orienting your coding habits into this direction, a cross-platform implementation becomes feasible. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">In this tutorial I am going to show you how I have used HTML5 to write such a cros-platform game. The reader could easily be confused with the term“HTML5” believing that somehow it is possible to write such complex applications using HTML-tags (something like <game><img src=”….”, …, put some game logic here </game> and …that’s all. NO!). In fact, HTML5 is 1% HTML and 99% JavaScript. The tutorial assumes that you already have a basic experience with JavaScript programming, or with …programming. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">Actually, an HTML5 game (or application) runs inside a browser that supports this level of the markup language. Fortunately, almost every browser on any device of the last half decade can translate HTML5. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">You can play a game that I have created with HTML5 and combines all these promises. Just visit from everywhere the link bellow. If you open it from a desktop browser please keep changing the dimensions to experience the effect of responsive resizing in real-time. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;"><a href="http://www.liknongames.com/spaceball">http://www.liknongames.com/spaceball</a> </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">Now, let’s begin with the fun.
</span><br />
<a name='more'></a><span style="font-family: "verdana" , sans-serif;">The magic thing in HTML5 is that this version of the language offers a new <canvas> HTML-element and from now on, dozens of built-in JavaScript functions can interact with it. Like in a canvas of the real world, JavaScript can draw on this element all primitive graphic shapes (lines, squares, circles, etc), set and change its dimensions, rotate it, color it, write text on it and of course draw preloaded images. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">An internal sound-library is also available. Touch events can be caught as usual. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">What else does someone need to create a game? …Nothing else. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">For the needs of this first tutorial I have created a simple version of the game above. The astronaut can be navigated left or right through two in-game buttons or using the arrow keys if a keyboard is presented. In this downgraded version, the game is resized (not in the smart way as in the full version) but not rotated according to the orientation of the device (like it happens with the full version). </span><br />
<span style="font-family: "verdana" , sans-serif;"><a href="http://liknongames.com/sf3n40nsb/html5tut1/spaceball">http://liknongames.com/sf3n40nsb/html5tut1/spaceball</a> </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">These advanced behavior will be covered in the next tutorial (<span style="font-family: "verdana" , sans-serif;">Episode II</span>).
The index.html (http://liknongames.com/sf3n40nsb/html5tut1/spaceball/index.html) looks quite simple: </span><br />
<br />
<pre class="brush: html"><!DOCTYPE html>
<html>
<head>
<title>Spaceball</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
<link href="scripts/style.css" rel="stylesheet" type="text/css">
<link REL="SHORTCUT ICON" HREF="images/fav.ico">
</head>
<body>
<div id="stage">
<canvas id="canvas"></canvas>
</div>
<script src="scripts/game.js" type="text/javascript"></script>
</body>
</html></pre>
<br />
<span style="font-family: "verdana" , sans-serif;">It defines the <canvas> element and gives an id to it (id=”canvas”) so that it can be imported into the source code of the JavaScript program stored to the
<a href="http://liknongames.com/sf3n40nsb/html5tut1/spaceball/scripts/game.js"> http://liknongames.com/sf3n40nsb/html5tut1/spaceball/scripts/game.js</a> </span><br />
<span style="font-family: "verdana" , sans-serif;">line 30:
</span><br />
<pre class="brush: java">// Canvas itself
var canvas = document.getElementById('canvas');</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">A style-sheets script is also paired with the HTML source
<a href="http://liknongames.com/sf3n40nsb/html5tut1/spaceball/scripts/style.css">http://liknongames.com/sf3n40nsb/html5tut1/spaceball/scripts/style.css</a> </span><br />
<span style="font-family: "verdana" , sans-serif;">that defines the background color (black) and disables the horizontal and vertical scrollbars of the browser. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">Once the HTML-page is being loaded the ‘onload’ listener in our JavaScript program is triggered (line 465) </span><br />
<br />
<pre class="brush: java">document.addEventListener('load', function ()...</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">There, the dimensions of the window are assigned to a pair of variables and the startGame(boolean) function (line 79) is executed with “true” as its single, boolean argument, that indicates that this is the very first time the page is loaded (and the game too) and thus all resources of the game need to be initialized and a thread-like function has to start the periodic call of itself (runGame, line 577, re-called in line 604 with the JavaScript function setTimeout ) </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">By calling startGame all values related with the size of the canvas are calculated. This take place inside the function initDimensions (line 511). The meaning of the assignment there is that the canvas-object will be as big as the entire window of the browser (lines 513 + 514). This tutorial is about games that use the entire window of the browser. </span><br />
<br />
<pre class="brush: java">canvas.width = window.innerWidth;
canvas.height = window.innerHeight;</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">Right after this point a context is extracted from the canvas-object with the option to support 2D-operations, since the game is a 2D game. 3D is also supported in HTML5.
line 515:
</span><br />
<pre class="brush: java">context = canvas.getContext('2d');</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">And now comes one of the most significant variables in our coding- journey. A multiplication-factor is calculated that keeps the value of how the scene has to be scaled so to fit to the browser’s window. (line 517)
</span><br />
<pre class="brush: java">fScaleFactor = window.innerWidth/WIDTH_GRAPHICS_DESIGNED_FOR;</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">There, the predefined value “WIDTH_GRAPHICS_DESIGNED_FOR” tells us the width of the scene (in pixels), that the designer has drawn the graphics of the game for
After these actions have been completed, the initContext() function at line 112 applies the previous calculated multiplicator on the previously instantiated context of the canvas. </span><br />
<span style="font-family: "verdana" , sans-serif;">line 114:
</span><br />
<pre class="brush: java">context.transform ( fScaleFactor, 0, 0, fScaleFactor, 0, 0);</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">As already said, all resources must be loaded before the beginning of the repeatedly projection of graphic elements on the screen. One problem is that the built-in function of JavaScript that loads graphics from raw files is no-blocking. The programmer simply specifies the path and the name of the graphics file... </span><br />
<br />
<pre class="brush: java">imgForButtons[0] = new Image(); // line 193
…
imgForButtons[0].src = "./images/btn_released.png"; // line 200</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">...but that doesn’t guarantee that after this line the image is loaded. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">For this reason, an ‘onload’ listener should be applied for single image so to specify the actions when finally the image is completely loaded. Since at any point of loading of resources it is known how many of them should be loaded, I increase a counter (iLoadedImages) every time an image is loaded and at time when the counter reaches the total amount of graphics (TOTAL_GRAPHICS_TO_LOAD) then and only then the initialization is indicated as complete and we can go to the next step. </span><br />
<span style="font-family: "verdana" , sans-serif;">lines 194 to 199:
</span><br />
<pre class="brush: java">imgForButtons[0].onload = function()
{
iLoadedImages++;
if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
initGameObjects(true);
}</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">Once the contition </span><br />
<span style="font-family: "verdana" , sans-serif;">if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD ) </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">is satisfied, the function initGameObjects(boolean) creates all ….game-objects (specifically implemented for this game) that use the recently loaded resources. When passing ‘true’ as argument (which is the case in the very first loading of the game) the function creates the yet undefined instances of these game-objects (line 133) since they were not existed before. The other choice is to call it with ‘false’ as it is the case in the startGame when only the positions of the already, previously instatiated game-objects should be rearranged (line 145). </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">Now, everything is ready and and the runGame( ) function (line 577) gets into the interesting part </span><br />
<span style="font-family: "verdana" , sans-serif;">(line 595)
</span><br />
<pre class="brush: java">...
else if ( GameState != STATE_LOAD_RESOURCES )
{
handleUserInput();
if ( GameState == STATE_GAME_STARTED )
{
calculate_positions();
}
}</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">The function acts like the main game-loop since it is repeatedly executed by itself (line 604).
setTimeout(runGame, 10);// every 10 milliseconds </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">In every single cycle of this loop the user’s input is checked by calling the function handleUserInput() (defined in line 316). It checks if the coordinates of a click on the screen are inside the two in-game buttons or if the left/right arrow-keys of a keyboard (if presented) has been pressed. The value of these coordinates is updated asynchronously through the listeners ‘mousedown’ (from a connected mouse, line 434), ‘touchstart’ (for mobile devices, line 451) and ‘keydown’ (line 476). Also there, the touch-coordinates are translated to the (perhaps) resized world of the game. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">Back now to the runGame( ) function. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">After handling the input of the user, the new positions are calculated by executing the function calculate_positions( ). This is where the logic of the game is implemented and the new positions for every actor or thing in the game is re-calculated. For the needs of this demo this function does absolute nothing, while the body of the same function in the full version of the game counts a few hundreds lines of source code. It is completely up to you to code there your own logic about how your game will interact with the player. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">At last, after this Odyssey we are ready to display the frame according to the newly calculated positions. This is performed at the end of our game-loop through the function onDrawFrame(), defined at line 526. There, the actual drawing on the screen take place. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">This is possible by calling the draw() member-function of every game-object. For example,such objects are the graphics to the background, mentioned in line 600,
</span><br />
<pre class="brush: java">gameImageBgrGraphics[i].draw()</pre>
<br />
<span style="font-family: "verdana" , sans-serif;">The class “GameObject” is defined in line 367. Actually, in JavaScript there isn’t any “class”-keyword. That what is known as a “class” can however be defined even in JavaScript as a “function”. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">The GameObject-class specifies a structure that stores the image or the images used of a game-object, its coordinates and two member-functions. The first, draw(), is a wrapper to the built-in drawImage(…) function of JavaScript. The other, is used to recalculate the coordinates in case of changing the rotation of the device or when the browser’s window is resized (more about it in the next tutorial, <span style="font-family: "verdana" , sans-serif;">Episode <span style="font-family: "verdana" , sans-serif;">II</span></span>). </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">Now that I spoke about these two cases, it is time to reveal the big secret: Both of these cases are in fact the same one. How different is to change the orientation (of a smartphone) than changing the dimensions of the browser? When a device is rotated then the new width of the browser is (almost) its previous height and similar with the new height. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">And since I discovered that it was not safe to rely on the indicator of the device that it has changed its orientation, this is why at the line 579, I decided to check if the (eventually) new dimensions are different than these of the previous frame and if they are, then the startGame function is called but this time with a ‘false’ argument, so that only a re-position of the elements follows (line 145, initGameObjects). In this case also the dimensions of the canvas are re-initialized and the new value of the scale factor is re-calculated. </span><br />
<br />
<span style="font-family: "verdana" , sans-serif;">That's all for this first part. Now, you have all information you need to look deeper into the code and get the knowledge from the source.</span>
<pre class="brush: java">"use strict";
var LEFTRIGHT_BTN_Y = 340; // The y-axis position of the bottom buttons
var LEFT_BTN_X = 30; // The x-axis position of the buttons at the left
var WIDTH_GRAPHICS_DESIGNED_FOR = 560;
var HEIGHT_GRAPHICS_DESIGNED_FOR = 420;
var PORTRAIT = 0;
var LANDSCAPE = 1;
var MODE_GAME_IS_DESIGNED_FOR = (WIDTH_GRAPHICS_DESIGNED_FOR>HEIGHT_GRAPHICS_DESIGNED_FOR)?LANDSCAPE:PORTRAIT;
// Game states
var STATE_LOAD_RESOURCES = 1;
var STATE_GAME_STARTED = 2;
// Options for the clickable objects
var OPTION_TYPE_INSTANT = 0;
var OPTION_TYPE_CONTINUOUS = 1;
var OPTION_TYPE_PRESSED = 2;
var POINTER_PRESSED = 1;
var POINTER_RELEASED = 2;
var canvasIsActivated = false; // the canvas is activated once it is clicked for the first time
var timeOnCanvasActivation = 0;
var showMessage = true;
// Canvas itself
var canvas = document.getElementById('canvas');
// and two-dimensional graphics context of the canvas
var context;
var MAIN_BACKGROUNDS = 1;
var STATIC_GRAPHICS = 4;
var TOTAL_GRAPHICS_TO_LOAD = 4 + MAIN_BACKGROUNDS + STATIC_GRAPHICS ;
var GameState = STATE_LOAD_RESOURCES;
var iLoadedImages = 0;
var touch_x = -1;
var touch_y = -1;
var pointerStates = POINTER_RELEASED;
// Image objects (standard JavaScript)
var imgBackgroundGraphics = [];
var imgPlayer = [];
var imgBgr;
var imgForButtons = [];
// Game objects (this game)
var gameImageBgrGraphics = [];
var gameImageMainBackground;
var gameImageRightBtn;
var gameImageLeftBtn;
var oPlayer;
// Options for the left/right buttons.
var optionRightButton;
var optionLeftButton;
var isLeftButtonClicked = false;
var isRightButtonClicked = false;
// dimensions of the canvas
var previousInnerW = 0;
var previousInnerH = 0;
var fScaleFactor = 0;
//
// starts a new game or restarts it after a change in the viewport has occured
// true: in the loading of the game so to load all resources
// false: when only reposition is needed (in resizing or rotation)
//
function startGame(isFirstLoading)
{
initDimensions();
initContext();
if ( isFirstLoading == true )
{
runGame();
LoadResources();
}
else
initGameObjects(false); // only recalculate some properties of the used objects
}
//
// clears the stage so to draw the next frame in an empty (white in this case) context
//
//
var clear = function ()
{
context.fillStyle = '#FFFFFF';
context.beginPath();
context.rect ( 0, 0, WIDTH_GRAPHICS_DESIGNED_FOR, HEIGHT_GRAPHICS_DESIGNED_FOR);
context.closePath();
context.fill();
};
//
// initialise the context by taking into consideration the scaling
//
//
function initContext()
{
context.transform ( fScaleFactor, 0, 0, fScaleFactor, 0, 0);
}
function initGameObjects(isFirstLoading)
{
// initial position of the player
var PlayerX = 378;
var PlayerY = 116;
// itinial positions of the static graphics on the stage
var posOfBgrGraphics =
[[ 1,10], // u.f.o
[100,240], // star
[304,64], // mountain
[ 7,254]]; // asteroid
// if it is the first time the game is loaded, then create all objects
if ( isFirstLoading == true)
{
oPlayer = new GameObject( 2,PlayerX,PlayerY,0,imgPlayer);
for ( var i = 0; i < STATIC_GRAPHICS; i++ )
gameImageBgrGraphics[i] = new GameObject(1, posOfBgrGraphics[i][0], posOfBgrGraphics[i][1], 0, imgBackgroundGraphics[i] );
gameImageMainBackground = new GameObject ( 1, 0, 0, 0, imgBgr);
gameImageLeftBtn = new GameObject ( 2, LEFT_BTN_X, LEFTRIGHT_BTN_Y, 0, imgForButtons );
gameImageRightBtn = new GameObject ( 2, WIDTH_GRAPHICS_DESIGNED_FOR-(LEFT_BTN_X + imgForButtons[0].width), LEFTRIGHT_BTN_Y, 0, imgForButtons);
optionLeftButton = new Option();
optionRightButton = new Option();
}
else // on resising the screen or on changed orientation, re-calculate the positions
{ // to the already existed game-objects.
oPlayer.reposition();
for ( var i = 0; i < STATIC_GRAPHICS; i++ )
gameImageBgrGraphics[i].reposition();
gameImageMainBackground.reposition();
gameImageLeftBtn.reposition();
gameImageRightBtn.reposition();
}
optionLeftButton.x = gameImageLeftBtn.x;
optionLeftButton.y = gameImageLeftBtn.y;
optionLeftButton.width = gameImageLeftBtn.width[0];
optionLeftButton.height = gameImageLeftBtn.height[0];
optionLeftButton.type = OPTION_TYPE_CONTINUOUS;
optionLeftButton.keyCode = 37;
optionLeftButton.prevState = 0;
optionRightButton.x = gameImageRightBtn.x;
optionRightButton.y = gameImageRightBtn.y;
optionRightButton.width = gameImageRightBtn.width[0];
optionRightButton.height = gameImageRightBtn.height[0];
optionRightButton.type = OPTION_TYPE_CONTINUOUS;
optionRightButton.keyCode = 39;
optionRightButton.prevState = 0;
GameState = STATE_GAME_STARTED;
}
//
// asynchronous loading of the graphics
//
function LoadResources()
{
var sStatics = ["ufo","star","mountain","asteroid"];
for ( var j = 0; j < STATIC_GRAPHICS; j++ )
{
imgBackgroundGraphics[j] = new Image();
imgBackgroundGraphics[j].onload = function ()
{
iLoadedImages++;
if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
initGameObjects(true);
}
imgBackgroundGraphics[j].src = "./images/" + sStatics[j] + ".png";
}
// graphic for the released-state of the buttons
imgForButtons[0] = new Image();
imgForButtons[0].onload = function()
{
iLoadedImages++;
if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
initGameObjects(true);
}
imgForButtons[0].src = "./images/btn_released.png";
// graphic for the pressed-state of the buttons
imgForButtons[1] = new Image();
imgForButtons[1].onload = function()
{
iLoadedImages++;
if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
initGameObjects(true);
}
imgForButtons[1].src = "./images/btn_pressed.png";
// The player
for ( var i = 0; i < 2; i++)
{
imgPlayer[i] = new Image();
imgPlayer[i].onload = function ()
{
iLoadedImages++;
if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
initGameObjects(true);
}
imgPlayer[i].src = "./images/me" + i + ".png";
}
// MAIN_BACKGROUND(s)
imgBgr = new Image();
imgBgr.onload = function ()
{
iLoadedImages++;
if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
initGameObjects(true);
}
imgBgr.src = "./images/bgr0.png";
}
//
// returns true when the current position of the touch
// has been done inside the option and false in the other case
//
function hasTouchedOnOption ( option )
{
if ( pointerStates == POINTER_RELEASED )
{
if ( option.type == OPTION_TYPE_PRESSED )
{
if ( option.prevState == 1)
{
option.prevState = 0;
return true;
}
}
option.prevState = 0;
return false;
}
if ( option.type == OPTION_TYPE_INSTANT )
{
if ( option.prevState == 1 ) // it was pressed
{
return false;
}
}
// touch has been performed out, at the right of the option
// [OPTION] [TOUCH]
if ( touch_x > option.x + option.width )
{
option.prevState = 0;
return false;
}
// touch has been performed out, at the left of the option
// [TOUCH] [OPTION]
if ( touch_x < option.x )
{
option.prevState = 0;
return false;
}
// touch has been performed out, under the bottom of the option
// [OPTION]
//
// [TOUCH]
if ( touch_y > option.y + option.height )
{
option.prevState = 0;
return false;
}
// touch has been performed out, over the top of the option
// [TOUCH]
//
// [OPTION]
if ( touch_y < option.y )
{
option.prevState = 0;
return false;
}
option.prevState = 1;
if ( option.type != OPTION_TYPE_PRESSED )
{
return true;
}
return false;
}
//
// performs the interaction of the user
//
function handleUserInput ( )
{
// ...just to make sure...
if ( isNaN(touch_x) || isNaN(touch_y) )
{
touch_x = -1;
touch_y = -1;
}
isLeftButtonClicked = isRightButtonClicked = false;
isLeftButtonClicked = hasTouchedOnOption(optionLeftButton);
if ( isLeftButtonClicked == false )
{
isRightButtonClicked = hasTouchedOnOption ( optionRightButton);
}
if ( GameState == STATE_GAME_STARTED )
{
if ( isLeftButtonClicked == true )
{
oPlayer.currentFrame = 0;
oPlayer.x -= 1;
}
else if ( isRightButtonClicked == true )
{
oPlayer.currentFrame = 1;
oPlayer.x += 1;
}
}
}
//
// 'structure' for clickable objects
//
function Option()
{
this.x = -1;
this.y = -1;
this.keyCode = -1;
this.width = 0;
this.height = 0;
this.type;
this.prevState;
}
//
// 'class' for the special object of this game
//
function GameObject(Frames, x, y, startingFrame, img )
{
this.x = x;
this.y = y;
this.type = 0; // type of a clickable object
this.bActive = false;
this.currentFrame = 0;
this.Frames = Frames;
this.img = img;
this.width = [];
this.height = [];
this.currentFrame = startingFrame;
if ( Frames == 1 )
this.img[0] = img;
for ( var i = 0; i < Frames; i++ )
{
this.width[i] = this.img[i].width;
this.height[i] = this.img[i].height;
}
this.draw = function()
{
context.drawImage ( this.img[this.currentFrame], 0, 0,
this.width[this.currentFrame],
this.height[this.currentFrame],
this.x,
this.y,
this.width[this.currentFrame],
this.height[this.currentFrame] );
}
this.reposition = function()
{
this.y = y;
}
}
document.onmouseup = function(e)
{
// do 'release touch' actions
onPoinerRelease()
}
// The equivalent for onmousedown on touchscreen devices is ontouchstart,
// and the equivalent of onmouseup is ontouchend.
document.ontouchend = function(e)
{
// do 'release touch' actions
onPoinerRelease()
}
function onPoinerRelease()
{
pointerStates = POINTER_RELEASED;
touch_x = -1;
touch_y = -1;
isLeftButtonClicked = false;
isRightButtonClicked = false;
}
document.addEventListener('mousedown', function (e)
{
if ( canvasIsActivated == false )
{
canvasIsActivated = true;
timeOnCanvasActivation = new Date().getTime();
}
touch_x = e.pageX/fScaleFactor;
touch_y = e.pageY/fScaleFactor;
pointerStates = POINTER_PRESSED;
});
// How to impliment a onmousedown and onmouseup on a touch screen iphone
// http://stackoverflow.com/questions/21009821/how-to-impliment-a-onmousedown-and-onmouseup-on-a-touch-screen-iphone
// The equivalent for onmousedown on touchscreen devices is ontouchstart,
// and the equivalent of onmouseup is ontouchend.
document.addEventListener('touchstart', function (e)
{
if ( canvasIsActivated == false )
{
canvasIsActivated = true;
timeOnCanvasActivation = new Date().getTime();
}
touch_x = e.pageX/fScaleFactor;
touch_y = e.pageY/fScaleFactor;
pointerStates = POINTER_PRESSED;
});
document.addEventListener('load', function ()
{
setTimeout(function ()
{
previousInnerW = window.innerWidth;
previousInnerH = window.innerHeight;
startGame(true);
}, 1000)
}, true);
document.addEventListener('keydown', function(event)
{
pointerStates = POINTER_PRESSED;
if ( event.keyCode == 37 )
{
touch_x = gameImageLeftBtn.x[0] + 5;
touch_y = gameImageLeftBtn.y[0] + 5;
}
else if ( event.keyCode == 39 )
{
touch_x = gameImageRightBtn.x[0] + 5;
touch_y = gameImageRightBtn.y[0] + 5;
}
if (event.preventDefault) { event.preventDefault(); }
if (event.stopPropagation) { event.stopPropagation(); }
return false;
});
document.addEventListener('keyup', function(event)
{
onPoinerRelease();
});
function GameOver()
{
// do 'game over' actions
}
function initDimensions()
{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context = canvas.getContext('2d');
// calculate how finally was the scaling
fScaleFactor = window.innerWidth/WIDTH_GRAPHICS_DESIGNED_FOR;
}
// OnDrawFrame, performs the actual display on the screen of all graphic elements.
// It is called repeatedly from the main loop of the game.
// While the resources are still loaded, it displays the percent of the
// loading procedure, using the embedded fonts (line 604)
function onDrawFrame( )
{
if ( GameState != STATE_LOAD_RESOURCES )
{
gameImageMainBackground.draw();
for ( var i = 0; i < STATIC_GRAPHICS; i++ )
gameImageBgrGraphics[i].draw();
oPlayer.draw();
}
if ( GameState != STATE_LOAD_RESOURCES )
{
gameImageLeftBtn.currentFrame = optionLeftButton.prevState;
gameImageRightBtn.currentFrame = optionRightButton.prevState;
gameImageLeftBtn.draw();
gameImageRightBtn.draw();
if ( showMessage == true )
{
if ( canvasIsActivated == false )
{
context.font = "16px Comic Sans MS";
context.fillStyle = "black";
context.fontWeight ="bold";
context.textAlign = "left";
context.fillText("Click anywhere on the stage to start", 100, gameImageLeftBtn.y[0] + 14);
}
else if ( new Date().getTime() - timeOnCanvasActivation < 5000 ) // 5000ms
{
context.font = "16px Comic Sans MS";
context.fillStyle = "black";
context.fontWeight ="bold";
context.textAlign = "left";
context.fillText("You can play also with the arrow keys", 100, gameImageLeftBtn.y[0] + 14);
}
else
{
showMessage = false; // so to not perform any kind of calculations/comparisons/etc...
}
}
}
}
function calculate_positions()
{
}
function runGame()
{
if ( (previousInnerW != window.innerWidth) ||
(previousInnerH != window.innerHeight) )
{
startGame(false);
previousInnerW = window.innerWidth;
previousInnerH = window.innerHeight;
}
clear();
if ( GameState == STATE_LOAD_RESOURCES )
{
context.font = "24px Comic Sans MS";
context.fillStyle = "red";
context.textAlign = "center";
context.fillText("Loading: " + ~~((iLoadedImages*100)/TOTAL_GRAPHICS_TO_LOAD) + " %" , 10, 150);
}
else if ( GameState != STATE_LOAD_RESOURCES )
{
handleUserInput();
if ( GameState == STATE_GAME_STARTED )
{
calculate_positions();
}
}
onDrawFrame();
setTimeout(runGame, 10);// every 10 milliseconds
};</pre>Theodosiοs Ekizoglouhttp://www.blogger.com/profile/09965963637480862598noreply@blogger.com0tag:blogger.com,1999:blog-7344137653074864695.post-20768683050334168572012-12-16T01:34:00.000+02:002018-11-14T12:31:33.515+02:00Room (or scene) loader for games with non-linear storyboard<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">In a game with linear storyboard when the player completes
the tasks in the current scene/room</span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> (henceforth: room) then the loader reads
the next record from the rooms-file where all rooms are stored and the core engine
of the game designs the next room.</span></span><br />
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">This is the case in almost any platform, arcade game, like
it is Pac-Man, Solomon’s Key, Bobble Bobble, Bob Jack etc.</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">But this is the easy case, the good one.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Recently I have uploaded my new game for java-phones called
Vampire. You can find more details and how to install it on </span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><a href="http://www.liknongames.com/#game_vampire" target="_blank">Liknongames - Vampire</a></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">. It is a platform,
multi screen game where the main ghost-like character walks inside a complex
castle searching for the vampire.</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">On his
path there are a lot of unfriendly creatures and lethal traps. </span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Here is a part of the huge map of some rooms in the castle
including the starting one. Every rectangle with white border is a room of the
castle.</span></span></div>
<span style="font-size: small;">
</span><span style="font-size: small;">
</span><span style="font-size: small;">
</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFhJMr6vyOjhQQsC8Z-omlxJral3r688QMjZlKpiZF_chKmynTIQBX09Llzh1k6VdypH4ZvcKx97aY0G5ZvRA4iyH_upZ3gTNp6uWXLp05rn78Pca5p8Tm_E_I7e03Bx2F4Ld6LuRqpg/s1600/map.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFhJMr6vyOjhQQsC8Z-omlxJral3r688QMjZlKpiZF_chKmynTIQBX09Llzh1k6VdypH4ZvcKx97aY0G5ZvRA4iyH_upZ3gTNp6uWXLp05rn78Pca5p8Tm_E_I7e03Bx2F4Ld6LuRqpg/s1600/map.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Picture 1</span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">As you can see there are rooms with one, two, three or even
four openings that lead to another </span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">room
in other floor or in other side, thus it doesn’t make any sense to speak for
“the next” or for “the previous” room.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Let us see now how I store every single room and how I
implemented this navigation system. It will help.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">This
is a screenshot of the room at the right side of the starting one.</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">
</span></span><br />
<span style="font-size: small;">
</span><span style="font-size: small;">
</span><span style="font-size: small;">
</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAnzOsXJjpm1vdeZvJaQp4aTrqFPt5JUk8jcOQDnpCzznNpefSwrRZfcHiH3mMA_fKLeNhyphenhyphenB4MNYtrkN6kcBHtPh7_nLdYaRaXcNoql04DlkMnYlHfS1ZDov036XD_yghZTz8mJGnqcw/s1600/room.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAnzOsXJjpm1vdeZvJaQp4aTrqFPt5JUk8jcOQDnpCzznNpefSwrRZfcHiH3mMA_fKLeNhyphenhyphenB4MNYtrkN6kcBHtPh7_nLdYaRaXcNoql04DlkMnYlHfS1ZDov036XD_yghZTz8mJGnqcw/s1600/room.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Picture 2</span></span></td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">You understand now that It has no meaning at all to say
“…this is the second room…” because from the starting room he may either fall
into the (trap-)room or exit from the right edge of the room.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">And this is exactly what I am going to explain in this post:
How to make an automated navigation mechanism that is responsible for the
transition from one room to another in such a game world where there may be
openings in all sides of the current room.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Vampire is a tile-based game. Every room is a grid of 16
cells along the horizontal and 8 cells along the vertical direction. So there
are 128 tiles in each room. </span></span></div>
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span>
<span style="font-size: small;">
</span><span style="font-size: small;">
</span><span style="font-size: small;">
</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdu_GtYcWMuqWl153JgmIbYkzyhwRLtbr_DfJU9MCJww3YnkM5frKd8XLaOq4gemmeS8o727TGYVurCE3TfMZA2QMB9PgIINW-DrLhntWL7Krfl9uJCRI_Zo_KQ1VE6fIdgJF-EqOsVw/s1600/room_grid.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdu_GtYcWMuqWl153JgmIbYkzyhwRLtbr_DfJU9MCJww3YnkM5frKd8XLaOq4gemmeS8o727TGYVurCE3TfMZA2QMB9PgIINW-DrLhntWL7Krfl9uJCRI_Zo_KQ1VE6fIdgJF-EqOsVw/s1600/room_grid.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Picture 3</span></span></td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">There are (and will be) about 200 different objects (tiles,
enemies, goodies, etc).</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Each kind of object
is given a numerical code used also for the storing into the file. So, I need values
from 0 to 200 (0 is the value for an empty tile). </span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">As we know, one byte is enough to keep such a
value since a byte can keep 256 different values. </span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">It means that each room can be considered as a
sequence of 128 bytes. </span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Inside the rectangle is the part of the binary file with
the values of the bytes of the room of the screenshot in hexadecimal system.</span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">
</span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<span style="font-size: small;">
</span><span style="font-size: small;">
</span><span style="font-size: small;">
</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKJI-3XxWMjhGI7fellwcHacqzLhVDA8LAcb4gdALRp40dnYwAbrakakqZpipghhfa353mA-SzgUYlJWr0nDE7Jx7ZCjXR0xyTL5IU2Hqrw_RoKlo-V0k-iBBgAo3WbuOuWHYR3NpmRQ/s1600/levels.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKJI-3XxWMjhGI7fellwcHacqzLhVDA8LAcb4gdALRp40dnYwAbrakakqZpipghhfa353mA-SzgUYlJWr0nDE7Jx7ZCjXR0xyTL5IU2Hqrw_RoKlo-V0k-iBBgAo3WbuOuWHYR3NpmRQ/s1600/levels.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Picture 4</span></span></td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">How ugly is it, isn’t it? Let’s remove the 00’s and we get a
friendlier and already known picture:</span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><br />
<span style="font-size: small;">
</span><span style="font-size: small;">
</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKK7zzEB-ejRUd4zp8ypszZ1As-SwpaOvxaz0oV-XGqOwTkwMjmrnue8FVcNpeqhtXpPTidw7SC85nzmA1vrRK7inHoRmpJN-SBUq1Dp7Y52fKW85emdhk9gO_n-TK6-S1A0TmKgqWbA/s1600/levels_nozero.png" style="margin-left: auto; margin-right: auto;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Picture 5</span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span></td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Yes, this is the same room in picture-3 seeing it through
X-rays.</span></span><br />
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Short F.A.Q. about this actinography:</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Q: In the upper row I see only green tiles. The values however
are different: 0x42, 0x43,0x4D, 0x45…</span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">A: The tiles are different too. Stare at them carefully and you can see slight
differences.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Q: What is the “74” in the middle column? </span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">A: This is an almost hidden canon that throws arrows if the player is in its height
and on its side.</span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Q: I see a “97” in the middle column under the “83” but nothing is it to be
seen in the screen:</span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">A: This is an completely transparent tile. Its value is out of the range of the
tiles the player can collide with, however it belongs to the range of values
that the enemies can collide with.</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">It
forces the bird (62) in the same row not to reach the blue wall at the right,
but to bounce earlier.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">At the upper left side of the rectangle there is a value,
00000180h. It is the position in the room-file where this screen is started
from. This is in hex. 0x180=384.</span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">From the 384</span></span><sup><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">th</span></span></sup><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> byte begins this room. How many
rooms are stored in the file before it? Since every record that represents a
room has a constant length of 128 bytes, we have 384/128 = 3 rooms.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">After the player has passed the right edge of the starting room
the loader knew that it has to spring to the 384</span></span><sup><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">th </span></span></sup><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">position of the
binary file that keeps all rooms and read from there the next 128 bytes.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">How did the loader know this? </span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">For this reason I had to split in tiles not only the room
but the entire map of the game and use a coordinate system to specify the
location of a room into the castle starting from the room 00x00 to be the
leftmost room in the lowest basement of the castle.</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">The first coordinate is increased by +1 from left
to the right and the second coordinate is increased by +1 when climbing in a
room on the ceiling of the current one.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">By this addressing the rooms in the part of the map in
picture are assigned with the coordinates as in the picture </span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span></div>
<span style="font-size: small;">
</span><span style="font-size: small;">
</span><span style="font-size: small;">
</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLN-toGWuct7OoKY5zyWqRS1Qc5rICHbpltFnTFI3_VsxKqUNBYUNL7rX4c7hH2GL07akN9qet9GYs_-9xhZ2xCWJMjDQGqItpjsCGTc61qfMIdbatNQE1ZtBu9lkV6hWr48Gjj7ku3Q/s1600/map_adresses.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLN-toGWuct7OoKY5zyWqRS1Qc5rICHbpltFnTFI3_VsxKqUNBYUNL7rX4c7hH2GL07akN9qet9GYs_-9xhZ2xCWJMjDQGqItpjsCGTc61qfMIdbatNQE1ZtBu9lkV6hWr48Gjj7ku3Q/s1600/map_adresses.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-size: small;"> Picture 6</span></td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Oh! I have just given away that by the starting of the game
you are already in the 7</span></span><sup><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">th</span></span></sup><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> floor !!!!</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span>
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Now that we always know the exactly coordination of a room
in the map of the castle that is going to be visited it is easy to know from
which position in the rooms-file to start reading from.</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">One thought would be to extend the record that stores a room
from 128 bytes to 130 bytes,</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">keeping in
the first two the coordinates of the room.</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">
</span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Then every time the player touches an opening the loader would scan the
entire file with the levels trying to identify the possibly “next” room with
the values of these first two bytes. </span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">However I decided for a more clear solution.</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">I introduced another file that correlates
map-coordinates into positions in the rooms-file. The first idea was to store
in it records of a length of 4 bytes. The first two are the map-coordinates and
the last two a short value of the position in the levels-file.</span></span><span style="mso-spacerun: yes;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"> </span></span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">Finally I decided to keep for the position only one byte
since the position is always a multiple of 128. Thus, I multiply the value of
this byte with 128 and I have the position where the pointer in the levels-file
has to be spring and read from there. </span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">I hope I didn’t ruin your mind. </span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span><span style="font-size: small;">
</span><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">If you like it please leave a comment, share it, or give it a "g+1".</span></span></div>
<div class="MsoNormal">
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;"><br /></span></span>
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: small;">In a “coming soon” tutorial I will describe how to design
rooms with a freeware tile editor and how to produce from the xml (its output)
the room file and correlation file.</span></span><span style="font-family: "calibri" , "sans-serif"; font-size: 11.0pt; line-height: 115%;"><br /></span></div>
</div>
Theodosiοs Ekizoglouhttp://www.blogger.com/profile/09965963637480862598noreply@blogger.com0tag:blogger.com,1999:blog-7344137653074864695.post-3536679245050729472012-03-28T17:27:00.006+03:002013-02-08T13:21:35.312+02:00(The easy way) OpenGL, Bypass the power of two (POT) restrictionMany friends have mailed me and told me that they faced difficulties when they tried to apply inside their application the algorithm in my previous post about how to bypass the ‘power of two’ restriction in Open GL ES.<br />
<br />
Thus I decided to approach the issue with a much simpler algorithm than the previous one. <br />
<br />
Why didn't I do that already in the first post ?<br />
<br />
Well, the cost of the simplicity is a little loss of quality.<br />
<br />
No, no, don't leave the page. It will be insignificant loss and you can base on this method even commercial products and nobody will observe any issue in the final product.<br />
<br />
Let me start again by telling the same story: Your designer has drawn nice backgrounds in a 320x240 resolution like the one with the colored circles in picture 1<br />
<br />
<br />
<br />
<table style="width: 320px;"><tbody>
<tr><td><br />
<img src="http://www.liknongames.com/blog/20120328pot/bgr001_original.png" /></td></tr>
<tr><td align="center"><center>
picture 1</center>
</td></tr>
</tbody></table>
<br />
During the development you test the application on the emulator and you are happy seeing that everything goes fine. Suddenly you transfer your application on the real device and instead of nice backgrounds you see a white rectangle 320x240.<br />
<br />
What happened???<br />
<br />
OpenGL ES binds as textures only images where the width and the height are both power of 2 (POT). This means that only 32×16, 128×64, 8×256 etc images are supported. It does not sound flexible, huh? It is a certain restriction for your designer (I bet it is you!). <br />
<br />
Newer GPU`s that support OpenGL version 2.0 or above can load any arbitrary dimension but why to take such a huge risk for the marketing of your application? <br />
<br />
The main idea is very simple: OpenGL is stupid enough to load a texture where width & height are of power of two but since the texture is loaded and bound OpenGL accepts to scale it even using different scale factors for width and height. <br />
What does it mean for our example? <br />
<br />
After the picture is ready by your artist and well painted open it in your editor ( I use GIMP) and scale it by giving for new dimensions what you believe is the nearest values that are of power of two. For our example it could be 256x128. Then you have it a stretched image like in picture 2.<br />
<br />
<br />
<br />
<table style="width: 256px;"><tbody>
<tr><td><br />
<img src="http://www.liknongames.com/blog/20120328pot/bgr001.png" /></td></tr>
<tr><td align="center"><center>
picture 1</center>
</td></tr>
</tbody></table>
<br />
<br />
This image is perfectly loaded and bound as an OpenGL-texture.<br />
But you don’t want it to be 256x128. You want to see circles on the screen, not ellipses. <br />
<br />
I am sure you know what the next step will be: You have to stretch it back to the original dimensions. For this there must be calculated the scale factors for width and height.<br />
<br />
<pre class="brush: java">float fScaleWidth = 320/256 = 1.25f
float fScaleHeight = 240/128 = 1.875f</pre>
<br />
<br />
Q: What values do you need to have as fixed to make it full parametrically?<br />
<br />
And then in the render function<br />
<br />
<pre class="brush: java">if ( mesh.isBackground == true )
{
g.glScalef(fScaleFactor*fScaleWidth, fScaleFactor*fBgrScaleHeight, 1.0f);
}</pre>
<br />
<br />
where fScaleFactor is the global scale factor, assuming that you may have designed everything in your game initially for a resolution 320x240 but you want it to fill screens even with bigger resolutions.<br />
<br />
Good luck.Theodosiοs Ekizoglouhttp://www.blogger.com/profile/09965963637480862598noreply@blogger.com0tag:blogger.com,1999:blog-7344137653074864695.post-27905882589073313402011-12-28T00:13:00.009+02:002013-02-08T13:22:20.007+02:00(The hard way) OpenGL, Bypass the power of two (POT) restrictionAssuming you have decided to use OpenGL ES for your game and your designer drew all the backgrounds as 324×226 images like the one on the picture 1.<br />
<br />
<br />
<br />
<br />
<table style="width: 360px;"><tbody>
<tr><td><br />
<img src="http://www.liknongames.com/blog/20111202pot/background.png" /></td></tr>
<tr><td align="center"><center>
picture 1</center>
</td></tr>
</tbody></table>
<br />
<br />
During the development you load these backgrounds as textures in the OpenGL and you are happy watching nice graphic results on the emulator.<br />
<br />
At some point of the timeline of your project you transfer the application on a real device and suddenly you realize than instead of a nice artwork you see a white square 324×226 (scaled at whatever scale-factor you have calculated according to the physical screen resolution)<br />
<br />
What happened actually????<br />
<br />
OpenGL ES binds only images where the width and the height are both power of 2 (POT).<br />
<br />
This means, only 32×16, 128×64, 8×256 etc images are supported. It does not sound flexible, huh?. It is a certain restriction for your designer (I guess it is you!). <br />
<br />
Newer GPUs that support OpenGL version 2.0 or above can load any arbitrary dimension but why to take such a huge risk? <br />
<br />
In this tutorial I am showing to you a solution to this issue so to leave your talent free and wake up the hidden Van Gong who lives inside you and forget about any such kind of restrictions.<br />
<br />
The main idea is very simple: You feed the engine with images of whatever dimension you want and the procedure that I am going to describe right now will split this image to the minimum amount of images with all their dimensions to be of POT.<br />
<br />
In the example above, a background will be split in 12 images:<br />
<br />
4 × 2 <br />
4 × 32 <br />
4 × 64 <br />
4 × 128 <br />
64 × 2 <br />
64 × 32 <br />
64 × 64 <br />
64 × 128 <br />
256 × 2 <br />
256 × 32 <br />
256 × 64 <br />
256 × 128 <br />
<br />
Like you can see on the picture 2<br />
<br />
<br />
<br />
<table style="width: 360px;"><tbody>
<tr><td><br />
<img src="http://www.liknongames.com/blog/20111202pot/pot_background.png" /></td></tr>
<tr><td align="center"><center>
picture 2</center>
</td></tr>
</tbody></table>
<br />
Seeing this picture it is clear what steps has to be executed by an automated procedure: <br />
<ol><br />
<li>Express the width and the height of the image as a sum of numbers of only powers of two.</li>
<br />
<li>Combine these numbers that sums the width with the numbers that sums the height and we have the dimensions of the new images.</li>
<br />
<li>Crop the initial image using these dimensions and get all the part-images as also the coordinates of them in relation of the (0,0) corner of the initial image.</li>
</ol>
<br />
Expressing any natural number k∈N as a sum of powers of two is the same as to represent this number into the binary numeral system. In fact this is the definition of the binary numeral system.<br />
<br />
Apply this step in our example:<br />
<br />
324 = (101000100)<sub>2</sub> <br />
= 1×2<sup>8</sup> + 0×2<sup>7</sup> + 1×2<sup>6</sup> + 0×2<sup>5</sup> +0×2<sup>4</sup> +0×2<sup>3</sup> + 1×2<sup>2</sup> + 0×2<sup>1</sup> +0×2<sup>0</sup> <br />
= 256 + 64 + 4 <br />
= 4 + 64 + 256<br />
<br />
similar for the height<br />
226 = (11100010)<sub>2</sub> <br />
= 2<sup>7</sup> + 2<sup>6</sup> + 2<sup>5</sup> + 2<sup>1</sup> <br />
= 128 + 64 + 32 + 2 <br />
= 2 + 32 + 64 + 128<br />
<br />
It would be handy for the second step if we had an array for the width and another for the height to store these constants. For example it could be: <br />
<br />
for 324, int w[] = {4, 64, 256 }<br />
for 226, int h[] = {2, 32, 64, 128 }<br />
<br />
And since the speech is about arrays it raises automatically the need to allocate memory for them before making any operation on them. What we exactly seek is the appearances of ‘1’s in the binary representation of the number. Let’s introduce our first function<br />
<br />
<pre class="brush: java">int bitCount ( int number)
{
int ones = 0;
for ( int i = 0; i < 16; i++ )
{
if ( (number&1) == 1 )
ones++;
number >>= 1;
}
return ones;
}</pre>
<br />
<br />
Nothing special has been used than just a few boring bitwise operations. <br />
(...or you can simply use the Integer.bitCount(int i) static method of Java!!!!)<br />
<br />
Now we can allocate memory for our arrays and play safely with them. According to your IDE environment you can use malloc (Ansi C) , NSMutableArray (iPhone) or the “new” operator (Android/Java/C++)<br />
<br />
Using the same pattern we can fill the array with values.<br />
<br />
<pre class="brush: java">void getPowersOfTwo ( int number, int[] array )
{
int pos = 0;
for ( int i = 0; i < 16; i++ )
{
if ( (number&1) == 1)
{
array[pos++] = (int) Math.pow((double) 2, (double) i);
}
number >>= 1;
}
}</pre>
<br />
<br />
Be very careful about the precedence of the operators since pos++ here doesn't have the same effect as ++pos. (Why?) <br />
<br />
After we have called this function on both w[] and h[] arrays we loop through their elements to build all pairs we need for the cropping. <br />
<br />
There are (w.length)×(h.length) combinations. That means that for our example there will be 3×4=12 images.<br />
<br />
For this purpose we introduce two nested “for” loops :<br />
<br />
<pre class="brush: java">int fromX = 0;
int fromY = 0;
for ( int i = 0; i < w.length; i++ )
{
for ( int j = 0; j < h.length; j++ )
{
System.out.printf("Crop %d x %d from (%d,%d\n", w[i],h[j],fromX,fromY);
fromY += h[j];
}
fromY = 0;
fromX += w[i];
} </pre>
<br />
<br />
Inside the body of the second “for” it is where you have to call a special function of the standard graphics library (not the OpenGL) and get the part of the image that you can bind right after as an OpenGL-texture. The printf has everything we need: Where to crop from (and where in the OpenGL mesh to put) and what size to crop.<br />
<br />
Of course you have to save also the values of fromX and fromY to a data structure and don't blast them into oblivion after this print-out.<br />
<br />
Here are these functions for the main platforms that rule the galaxy nowadays:<br />
<br />
Android: <br />
<pre class="brush: java">Bitmap android.graphics.Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height) </pre>
<br />
<br />
iPhone: <br />
<pre class="brush: cpp">CGImageRef image = CGImageCreateWithImageInRect(CGImage source, rect);
UIImage* uiimage = [UIImage imageWithCGImage:image];</pre>
<br />
<br />
And here is the output of this function on the console. <br />
<br />
Crop 4 × 2 from (0,0) <br />
Crop 4 × 32 from (0,2) <br />
Crop 4 × 64 from (0,34) <br />
Crop 4 × 128 from (0,98) <br />
Crop 64 × 2 from (4,0) <br />
Crop 64 × 32 from (4,2) <br />
Crop 64 × 64 from (4,34) <br />
Crop 64 × 128 from (4,98) <br />
Crop 256 × 2 from (68,0) <br />
Crop 256 × 32 from (68,2) <br />
Crop 256 × 64 from (68,34) <br />
Crop 256 × 128 from (68,98)<br />
<br />
Is this all what you need? No, but it is the 90%. <br />
<br />
I leave the remaining ...90% as an excercise.<br />
<br />
Good luckTheodosiοs Ekizoglouhttp://www.blogger.com/profile/09965963637480862598noreply@blogger.com0tag:blogger.com,1999:blog-7344137653074864695.post-23610784026792943532011-12-17T23:36:00.002+02:002012-03-21T14:13:49.110+02:00How to draw your own buttonsIn this tutorial I am presenting a way for the case where you don’t want to use the high level library to add buttons in a game (or whatever else application) and want to draw your own instead. <br /><br />Suppose you are making a game for the iPhone using the OpenGL ES. Of course you can use UIKit buttons and simply lay them over your OpenGL view. Using UIKit buttons works fine and is probably the easiest way to add buttons to your game. However, if you are trying to achieve the best performance possible, you should avoid putting a UIView on top of your OpenGL view. This is especially true if you want to use the controls in the game, unless your game is a “Find The Differences”-like game.<br /><br />And since everything in my engine is texture everything that you can imagine can be considered as a button. From graphics and single alphanumeric characters (this is very useful for the “write your name” menu) to words and entire phrases. <br /><br />Three types of buttons are fully supported:<br /><ul><br /><li>Buttons that fire a single signal of the touch event even when the button is kept pressed</li> <br /><li>Continuous sending of signals to the core mechanism of the game as long as the button is kept pressed</li><br /><li>One signle signal only if the button is released after it has been pressed.</li><br /></ul><br /><br />We do need a procedure for this because the operating system (iOS or Dalvik) triggers one single event when we click on the screen, no matter if we keep our finger on the screen.<br /><br />As a first step we have to introduce a new structure (or class, in java) where we are going to store whatever is needed for a clickable object or Option, to speak more general. <br /><br /><pre class="brush: java"><br />public class Option<br />{<br /> int x;<br /> int y;<br /> int width;<br /> int height;<br /> int type;<br /> int prevState;<br />}</pre><br /><br />Where x,y are the (x,y) coordinates of the upper left corner of the option in actual screen coordinates, calculated in the initialization procedure of the clickable object that is bound with this option.<br />‘width’ and’ height’ is the width and the height of the object , again in screen coordinates.<br /><br />The field ‘type’ stores the information of the type (what else?) of the option according to the three different cases as I described above. I have defined them as follow: <br /><br /><pre class="brush: java"><br /> final static int OPTION_TYPE_INSTANT = 0;<br /> final static int OPTION_TYPE_CONTINUOUS = 1;<br /> final static int OPTION_TYPE_PRESSED = 2;<br /></pre><br /><br />The ‘type’ property of the option can be changed on runtime to achieve other effect for the same button in different game situations.<br />In the field ‘prevState’ is stored the previous state of the clickable object. You don’t have to deal with it since it is calculated automatically from the procedure that I am going to attach here. This state is defined as 0 for previously not pressed and 1 for previously pressed.<br /><br /><pre class="brush: java"><br /> final static int PREVIOUSLY_NOT_PRESSED = 0;<br /> final static int PREVIOUSLY_PRESSED = 1;<br /></pre> <br /><br />So,I am ready to reveal how our function will look like<br /><br /><pre class="brush: java"><br />boolean hasTouchedOnOption ( Option option )<br /></pre> <br /><br />The function returns a ‘boolean’ (BOOL for iOS). This means that in the current iteration of the game loop an option is either active or not active.<br /><br />Do we need anything else? As you can guess we need a ‘touch_x’ and a ‘touch_y’ variable that represent the coordinates of the point where the click has taken place. We need also a ‘pointerStates’ variable that will do exactly what it says.<br /><br />In my engine there are two states for the pointer. A finger is either on the screen or it is not on the screen. <br /><br /><pre class="brush: java"><br /> final static int POINTER_RELEASED = 0;<br /> final static int POINTER_PRESSED = 1;<br /> <br /> int pointerStates = POINTER_RELEASED;<br /></pre><br /><br />I chose to declare all these three variables as global variables. They are initialized in the function that catches the touch events directly from the underlying hardware. <br /><br />iPhone: touchesBegan, touchesMoved, touchesEnded<br />Android: public boolean onTouchEvent<br /><br />I am pasting here my implementation for Android<br /><br /><pre class="brush: java"><br />@Override <br />public boolean onTouchEvent(MotionEvent event)<br />{ <br /> int action = event.getAction();<br /> if ( action == MotionEvent.ACTION_OUTSIDE || <br /> action == MotionEvent.ACTION_UP )<br /> {<br /> touch_x = -1;<br /> touch_y = -1;<br /> pointerStates = POINTER_RELEASED;<br /> }<br /> else if ( action == MotionEvent.ACTION_DOWN || <br /> action == MotionEvent.ACTION_MOVE )<br /> {<br /> touch_x = (int) event.getX();<br /> touch_y = (int) event.getY();<br /> pointerStates = POINTER_PRESSED;<br /> }<br /> return super.onTouchEvent(event);<br />}<br /></pre><br /><br />Now it is time to open the curtain and reveal the body of the hasTouchedOnOption function<br /><pre class="brush: java"><br />protected static boolean hasTouchedOnOption ( Option option )<br />{<br /> if (pointerStates == POINTER_RELEASED )<br /> {<br /> if ( option.type == OPTION_TYPE_PRESSED )<br /> {<br /> if ( option.prevState == PREVIOUSLY_PRESSED)<br /> {<br /> option.prevState = PREVIOUSLY_NOT_PRESSED;<br /> return true;<br /> }<br /> }<br /> option.prevState = PREVIOUSLY_NOT_PRESSED;<br /> return false;<br /> }<br /><br /> if ( option.type == OPTION_TYPE_INSTANT )<br /> {<br /> if ( option.prevState == PREVIOUSLY_PRESSED ) <br /> {<br /> return false;<br /> }<br /> }<br /><br /> // [OPTION] [TOUCH]<br /> if ( touch_x > option.x + option.width )<br /> {<br /> option.prevState = PREVIOUSLY_NOT_PRESSED;<br /> return false;<br /> }<br /><br /> // [TOUCH] [OPTION]<br /> if ( touch_x < option.x )<br /> {<br /> option.prevState = PREVIOUSLY_NOT_PRESSED;<br /> return false;<br /> }<br /><br /> // [ OPTION]<br /> //<br /> // [TOUCH]<br /> if ( touch_y > option.y + option.height )<br /> {<br /> option.prevState = PREVIOUSLY_NOT_PRESSED;<br /> return false;<br /> }<br /><br /> // [TOUCH]<br /> //<br /> // [OPTION]<br /> if ( touch_y < option.y )<br /> {<br /> option.prevState = PREVIOUSLY_NOT_PRESSED;<br /> return false;<br /> }<br /><br /> option.prevState = PREVIOUSLY_PRESSED;<br /> if ( option.type != OPTION_TYPE_PRESSED )<br /> return true;<br /><br /> return false;<br />} <br /></pre> <br /><br />I think there is not a lot of stuff that needs detailed explanation. [OPTION] and [TOUCH] is the relative position of the touch and the option. The function is extremely quick and it needs from 2 to 10(in the worst case) operations to return a result.<br /><br />But where is the best place to call this function?<br /><br />It depends on the nature of your application. Either directly inside the functions that transfer the touches from the OS(see above) or inside the main game loop.Theodosiοs Ekizoglouhttp://www.blogger.com/profile/09965963637480862598noreply@blogger.com2tag:blogger.com,1999:blog-7344137653074864695.post-89415343817743117242011-07-15T01:10:00.002+03:002012-03-21T14:16:39.580+02:00Predefined FPS (Frames Per Second) value for your game loopIn the last decades tons of inks have been spent in articles about how to achieve a constant value for FPS in a game loop. <br /><br />But why add another headache in the game development process? Why to time it? Why not just leave things run as quick as the CPU can and forget about on-purpose delays and timing?<br /><br />The main reason is that you have to make sure that the game has the same tempo independent of the power of the CPU and if possible to synchronize the game loop with the refresh rate of the display. Imagine that your hero is at the leftmost bound of the scene and as you hit the right arrow key then he is at the rightmost edge in no time. <br /><br />Another reason it that once we know the FPS then this is the key for defining the step for x, y direction (and even z in a 3D environment). You know the width of your scene, you know in how much time your hero has to walk all this distance(completely up to you) as if his way was free of obstacles, and if you also know how many frames he walks in a second, then you are a simple division away from setting a correct step (…not exactly …but I will discuss it in another article)<br /><br />Particularly nowadays where mobile gaming rules the galaxy it is most important to find a way to stabilize on-the-fly the FPS value because you don’t know in the creation time of the game in what hardware configuration the game is going to run. Of course if you write games for an a priori known hardware (as example, if you are an iPhone-developer) then you can experiment in the development time and find the correct values so to put them hardcoded in your source code. <br /><br />Of course if the game logic is implemented in a terrible way then you have to take other medicine and return later in this tutorial.<br /><br />Please read first the wrong way for achieving a constant FPS value: It says that...<br />...if you want to have in your game, let’s say, 25 frames per second (FPS) then you have to add a delay of 1000/25 = 40 milliseconds at the end of every game cycle.<br /><br />This approach is absolutely wrong because it doesn’t take into consideration:<br />1) the execution time of one cycle of your game loop. <br />2) the time that the delay() or sleep() function needs to execute itself.<br /><br />And, you may ask, what if alone the execution time covers completely the optimal value for the delay? Can you in this case ignore the delay(X milliseconds) function? No! A delay() or sleep() function must be there even with a minimum value in its argument so that the CPU can give time and resources to other threads as well(Input, Display, System threads, etc).<br /><br />So, a first conclusion for what will follow is that the delay time in unknown in the initialization process and thus we have to keep a variable for this:<br /><br /><pre class="brush: java"><br />int iGameThreadDelay = 1;<br /></pre><br /><br />and change this value on-the-fly according to the everytime calculated FPS value. You will decide for a new value every 1 second.<br />So, we introduce a variable called lTimer, initialized before going into the while(gameRunning) loop and in every cycle we check if the difference with the current time is greater or equal(the best) than 1000 milliseconds (= 1 second). <br />And this “1-second-has-passed” moment is the best opportunity to get the cycles performed in this interval (then, FPS=cycles). The variable lGameCycles is increase by 1 in every iteration of the game loop and we can compare this value with the desired one to decide if the delay time has to be increased or decreased. Of course, there must a check for this delay time not to take values less than 1. <br /><br />Here is a game loop in pseudo language. You can easily find the libraries of the language you use to get the current time in milliseconds and for letting a thread go to sleep mode for some milliseconds.<br /><pre class="brush: java"><br />final static int FPS = 30;<br />Timer = GetCurrentTimeInMillis();<br />// better, if you initialize it from a precalculated value<br />iGameThreadDelay = 1;<br /><br />// will be set to zero every (almost) 1 sec<br />lGameCycles = 0;<br />while ( gameRunning == true )<br />{ <br /> lGameCycles++; <br /> // .... <br /> // Game logic here <br /> // .... <br /> if ( GetCurrentTimeInMillis() - lTimer >= 1000 ) <br /> { <br /> if ( lGameCycles > FPS ) <br /> { <br /> // if too many FPS, then increase the delay <br /> iGameThreadDelay++; <br /> } <br /> else // ...decrease the delay ... <br /> { <br /> // ... making sure that the minumum value is 1<br /> if ( iGameThreadDelay > 1 ) <br /> { <br /> iGameThreadDelay--; <br /> } <br /> } <br /> lGameCycles = 0; <br /> lTimer = GetCurrentTimeInMillis(); <br /> } <br /> delay ( iGameThreadDelay );<br />}</pre><br /><br />Inaccurate can occur when the difference of the current time and the saved one (in the previous ‘second’) is > 1000 milliseconds. This can be happened while in the k iteration of the loop the difference is let’s say 998 milliseconds and of course in this case the flow doesn’t enter the if (CurrentTimeMillis() - lTimer >= 1000 ) section and right after this in the k+1 iteration(the next) have already been spent 80ms for the execution of the game logic then the calculated difference will be 998 + 80 = 1078 milliseconds. <br /><br /><br />This value can be stored somewhere and be used in the next game session because – and I have to admit it – using this approach and starting the iGameThreadDelay from 1, it may take a few seconds for the game mechanism to calculate the best value and some players may experience bizarre movements in these few seconds. Thus, by storing it somewhere, it will happened only for the very first time the game is start.<br /><br />Exactly the same logic can be applied on platforms where a time scheduler executes periodically a callback function (like iPhone & Android).Theodosiοs Ekizoglouhttp://www.blogger.com/profile/09965963637480862598noreply@blogger.com0tag:blogger.com,1999:blog-7344137653074864695.post-85170838349436722242011-07-13T09:52:00.002+03:002012-03-21T14:18:06.466+02:00Collision detection by detecting ...no-collisionWhat can destroy your game? A bad collision detection algorithm that either returns error results or it is slow or both of them. What if you have hundreds of objects in a scene that have to be checked for collisions using an algorithm hungry for CPU resources?<br /><br />In this tutorial I am presenting a way that I believe covers the needs of the 90% of the game developers. I am showing you how to perform very quick collision detection in 2D assuming that every object that carry collision dynamics in your game universe can be consider either as a rectangle or as a circle.<br /><br />If there are special reasons to perform collision detection between objects that are of any kind of shape then you can approach them by splitting them in rectangles/circles or ...re-thing if you really need all this geometry. And if you finally decide to proceed with this then after you have prayed and sacrificed to Zeus...<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitYSZRbU7pVGSsWKLmFeCtMAvHM4H4pmUmOFT_F1XFOMOyPVDxFVduzQc3LTgj4WinyKYtPbJx-3AgOVTVpexRso9DnjeAMfz8NKpJZikvYIu-uLkWEshNHh6qOle1s2z4-wnpOtZN_w/s1600/zeus.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="200" width="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitYSZRbU7pVGSsWKLmFeCtMAvHM4H4pmUmOFT_F1XFOMOyPVDxFVduzQc3LTgj4WinyKYtPbJx-3AgOVTVpexRso9DnjeAMfz8NKpJZikvYIu-uLkWEshNHh6qOle1s2z4-wnpOtZN_w/s320/zeus.png" /></a></div><br />... you have to befriended with trigonometry but remember: Never call trigonometric functions (sin(x), cos(x), tan(x), etc). Create instead (use <a href="http://www.liknongames.com/files/trigonometry.xls">this</a> excel spreadsheet) an array (or more) with all possible angles that your game uses and get from there whatever you need.<br /><br />Checking if two rectangles DO intersect can lead to a complex system of inequalities that has to be checked one-by-one, no escape, no 'break's, no 'return's, for each pair of objects, in each cycle of the game loop.<br /><br />I don't even want to reproduce here the nightmare of such an inequalities system. Just take a pencil and a (large) piece of paper and draw all possible relative positions of two objects to understand the underlying complexity.<br /><br />Instead of doing this it is much simpler to find whether two objects DO NOT collide at all. See the sketches bellow. There are only four cases:<br /><br /><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-Lr4T6qiEtndo57Nt-7hfFrwDlUyye_DqBOkzJPAHNKgwXaqpmRNTGrYV3xwj0oDDBART4V8XugnfQoMu6M-u2BrRzE_PK-WKD__ed_rX7ttwqFKreK8lJ5VSYsg25vlEaXOB0HFouw/s1600/case1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="202" width="274" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-Lr4T6qiEtndo57Nt-7hfFrwDlUyye_DqBOkzJPAHNKgwXaqpmRNTGrYV3xwj0oDDBART4V8XugnfQoMu6M-u2BrRzE_PK-WKD__ed_rX7ttwqFKreK8lJ5VSYsg25vlEaXOB0HFouw/s320/case1.png" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgytvNyWa9mthwMOsYF4IOuIpujNqnMjO4VaQp3h7guaTIq4-1FEu6Toiq9oqKbnUhyBdMeXVBYtEoZ3gfUPB9y75JHWlLkMSk1cu_kBgPsvNN6iPXLoLezTj-zzTyj7sIw94zC-6j46w/s1600/case2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="190" width="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgytvNyWa9mthwMOsYF4IOuIpujNqnMjO4VaQp3h7guaTIq4-1FEu6Toiq9oqKbnUhyBdMeXVBYtEoZ3gfUPB9y75JHWlLkMSk1cu_kBgPsvNN6iPXLoLezTj-zzTyj7sIw94zC-6j46w/s320/case2.png" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSf1G8n8Wyd26u6ejRyU3q6HfFNAjq9kiVFh8AZLYHCliKO_-BbW2mTdhnInQCS5iKPba1nai0HYJA99iheNEvjzjJe4s0CydcU1tMz4ZaQzTn_fscKhyca0woUff72XnSS98U1OAQRQ/s1600/case3.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="298" width="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSf1G8n8Wyd26u6ejRyU3q6HfFNAjq9kiVFh8AZLYHCliKO_-BbW2mTdhnInQCS5iKPba1nai0HYJA99iheNEvjzjJe4s0CydcU1tMz4ZaQzTn_fscKhyca0woUff72XnSS98U1OAQRQ/s320/case3.png" /></a></div><br /><br /><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirb5YHCl2tGTbRtUzAa9L9sTTCgdYSgJVqSoDl9LuxfXs4DB_1GN849dWUS_qLjc0ebNfy2hnHm3fHokrYnFZZq8tZ46yuww9rXa0je4zKDmQeHbhD5LasUXz2jK0l22eMjzp0e66rog/s1600/case4.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="288" width="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirb5YHCl2tGTbRtUzAa9L9sTTCgdYSgJVqSoDl9LuxfXs4DB_1GN849dWUS_qLjc0ebNfy2hnHm3fHokrYnFZZq8tZ46yuww9rXa0je4zKDmQeHbhD5LasUXz2jK0l22eMjzp0e66rog/s320/case4.png" /></a></div><br />To understand the simplicity, I want you to forget for one moment that there are programming languages in this world. Staring at these four pictures it makes sense that if you detect that you belong to one of these cases, then there is absolute no reason to check the remaining cases and you simply return with a result like COLLISION_NO. But if you end up by realizing that you are not in any case of them then only one result remains for you to accept: COLLISION_YES<br /><br />And exactly this is the logic that I am going to implement. So, assume we have two rectangles A and B and call (x, y) the coordinates of the upper left corner. By knowing also the width and the height of each rectangle we can easily detect all other sides of it. It is very easy to get width & height in the loading procedure of the (image-)resources since almost every graphics library offers functions like getWidth() and getHeight() for its supported images.<br /><br />So, I am repeating the sketch above by adding in each case the algebraic condition<br /><br /><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsKk4K12FrxyYEVBd3N1MZHIbFC4jcblrOYnSxRBGbPgnVyIFzv1ISxFvvAKw68OKjZmIM7VQBni6OEINW8IRi9R2TQ9BuJWZ-Ph7kGqiWZ-Kkv1PED2lfb2gC1DxLAeXu_Km1xcEivw/s1600/picture1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="226" width="268" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsKk4K12FrxyYEVBd3N1MZHIbFC4jcblrOYnSxRBGbPgnVyIFzv1ISxFvvAKw68OKjZmIM7VQBni6OEINW8IRi9R2TQ9BuJWZ-Ph7kGqiWZ-Kkv1PED2lfb2gC1DxLAeXu_Km1xcEivw/s320/picture1.png" /></a></div>Picture 1<br />A.x + A.width < B.x<div class="separator" style="clear: both; text-align: left;"><br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDq9U3UUdNfCkg6lcT5WFKr9vVgy9ny03HHmUz2tgE4qd2Io7YwF4P-DtTFuDUzGPSTl_zZ8RuFRvhSpkRwtTwWxpbW_MhPpPHyav96b3P7NFuKcFuhsLQ7r8Ud_H0P1k7b_3qZQe9QQ/s1600/picture2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="212" width="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDq9U3UUdNfCkg6lcT5WFKr9vVgy9ny03HHmUz2tgE4qd2Io7YwF4P-DtTFuDUzGPSTl_zZ8RuFRvhSpkRwtTwWxpbW_MhPpPHyav96b3P7NFuKcFuhsLQ7r8Ud_H0P1k7b_3qZQe9QQ/s320/picture2.png" /></a></div>Picture 2<br />A.x > B.x + B.width<br /><br /><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjs0yzRnWQ7rvridAk25GDLkaabXmb1wW46h2iwWTPTdmInGBTbHCYD2raEGD2COCZZ_FEYoLvzFrtFDo_dChhI_3eJQNnSkKB0YBnx9E4goe_ih_oMefZhLoDBT1gYZRW8BPV9lx6EgQ/s1600/picture3.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="290" width="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjs0yzRnWQ7rvridAk25GDLkaabXmb1wW46h2iwWTPTdmInGBTbHCYD2raEGD2COCZZ_FEYoLvzFrtFDo_dChhI_3eJQNnSkKB0YBnx9E4goe_ih_oMefZhLoDBT1gYZRW8BPV9lx6EgQ/s320/picture3.png" /></a></div>Picture 3<br />A.y + A.height < B.y<div class="separator" style="clear: both; text-align: left;"><br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEfOrYSHAtqv6Lc7a9mk66pujx0d7emAYvli8dOTjagg-igZX8cwd8sTk4Nu1cj7MI2TeOp_hAj9XOpg8JgdZdJjiKd46OlDoKve9tIkgRjPK_itif_csKY1GTavf-xNyCqYfJ41Cuag/s1600/picture4.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="288" width="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEfOrYSHAtqv6Lc7a9mk66pujx0d7emAYvli8dOTjagg-igZX8cwd8sTk4Nu1cj7MI2TeOp_hAj9XOpg8JgdZdJjiKd46OlDoKve9tIkgRjPK_itif_csKY1GTavf-xNyCqYfJ41Cuag/s320/picture4.png" /></a></div>Picture 4<br />A.y > B.y + B.height<br /><br />Now, pack these conclusions in a function, let's call it “getCollisionResult”, that takes two rectangles as arguments and returns COLLISION_NO or COLLISION_YES that can be (#)defined as integers(0 or 1) , booleans(false or true) or whatever other primitive type you like.<br /><br /><pre class="brush: java"><br />result getCollisionResult ( rectangle A, rectangle B)<br />{ <br /> // picture 1 <br /> if ( A.x + A.width < B.x ) // 1 math operation (addition), 1 comparison<br /> return COLLISION_NO; <br /> // picture 2 <br /> if ( A.x > B.x + B.width ) <br /> return COLLISION_NO; <br /> // picture 3 <br /> if ( A.y + A.height < B.y ) <br /> return COLLISION_NO; <br /> // picture 4 <br /> if ( A.y > B.y + B.height ) <br /> return COLLISION_NO; <br /><br /> // reaching at this point means that the rectangles overlap<br /> return COLLISION_YES;<br />}</pre><br /><br />As you see with a little luck you can end up a cycle of the game loop by doing very few CPU-operations for performing collision detection between two rectangles, 4 at the worst case plus 4 comparisons. There is another implementation of this function to reduce the operations to 2 but I leave it as an exercise for you.<br /><br />This is not the main function that is going to realize your wishes when a collision is detected, but it is a helper function instead. The main function would look like this<br /><pre class="brush: java"><br />result_type HandleCollision( )<br />{ <br /> for ( i = 0; i < objectsInGame; i++ ) <br /> { <br /> for ( j = i+1; j < objectsInGame; j++ )//why j=i+1 ??? <br /> { <br /> // before proceeding with checking for a collision,<br /> // see if these objects don't even need to be checked.<br /> if ( checkObjects(obj[i],obj[j] ) == true)<br /> { <br /> if (getCollisionResult(obj[i].rect,obj[j].rect)<br /> == COLLISION_YES) <br /> { <br /> // ...do whatever you want. <br /> } <br /> } <br /> } // END for ( j = ... ) <br /> } // END for ( i = ... )<br />}</pre><br /><br />What happens now if your have circles?Then also in this case you can use the function getCollisionResult by passing to it the rectangles that contain these circles. But it may be not accurate enough since it is possible to fall in this case:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU6zzsloRhQGG_AGI0px2qek3qwCd9qfV2sN1gSwKEelsiK7flibKlh66DIHcjNqg5mH-ByxYuAXsiuWivPZKAep9kGZNF1tT9Zq0dnc_q4KeSDlRnpkbke25s2HOk_fbVVOKEL8YLqQ/s1600/colliderects_nocoll_circles.bmp" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="152" width="153" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU6zzsloRhQGG_AGI0px2qek3qwCd9qfV2sN1gSwKEelsiK7flibKlh66DIHcjNqg5mH-ByxYuAXsiuWivPZKAep9kGZNF1tT9Zq0dnc_q4KeSDlRnpkbke25s2HOk_fbVVOKEL8YLqQ/s320/colliderects_nocoll_circles.bmp" /></a></div>The solution for this is to add another argument in the getCollisionResult function so that also the kind of the shapes is known now to the function. So, if the four “if”s are passed without returning NO, then the rectangles collide and only from now on is the best time to check the circles themselves by applying in our calculations the following lemma from the Euclidean geometry:Two circles collide only if the distance of their centers is less or equal to the sum of their radius as it can be seen in the following picture:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMNzM2yotDuMVSBjGOwQzARaNmIdvCkXJ3Yuieix0b2aTr8ZyhKRpjhdhQOj8EIrn8C9CMrHLNVR19Pq5wuO-9L8dBjWv9iG91YpQ-Kex9tzrugem4xJtazRZAqO-fCudwOHL7SoxzTQ/s1600/circles_relative_pos.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="400" width="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMNzM2yotDuMVSBjGOwQzARaNmIdvCkXJ3Yuieix0b2aTr8ZyhKRpjhdhQOj8EIrn8C9CMrHLNVR19Pq5wuO-9L8dBjWv9iG91YpQ-Kex9tzrugem4xJtazRZAqO-fCudwOHL7SoxzTQ/s400/circles_relative_pos.png" /></a></div>But given the centers (cx1,cy1) and (cx2,cy2) of two circles with radius r1 and r2 respectively the distance d between the centers is <div class="separator" style="clear: both; text-align: left;"><a href="http://www.liknongames.com/files/01distance.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="33" width="208" src="http://www.liknongames.com/files/01distance.png" /></a></div>And to check if our circles are far away from each other then it has to be check if<div class="separator" style="clear: both; text-align: left;"><a href="http://www.liknongames.com/files/02distance_sqroor.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="33" width="273" src="http://www.liknongames.com/files/02distance_sqroor.png" /></a></div>To strike the CPU with a square root is not the most gently you can do to it. It is a heavy operation and could lead to unnecessary delays. To avoid this take the square of the above relationship and you finally have to check this inequality:<div class="separator" style="clear: both; text-align: left;"><a href="http://www.liknongames.com/files/03distance_sq.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="26" width="283" src="http://www.liknongames.com/files/03distance_sq.png" /></a></div>where there is no square root. After all these, dive into coding and extend the function getCollisionResult like this:<br /><br /><pre class="brush: java"><br />...<br />radius_A = (A.width)/2;<br />radius_B = (B.width)/2;<br />...<br /><br />result getCollisionResult ( rectangle A, rectangle B,<br /> int shape )<br />{ <br /> // picture 1 <br /> if ( A.x + A.width < B.x ) <br /> return COLLISION_NO; <br /> // picture 2 <br /> if ( A.x > B.x + B.width )<br /> return COLLISION_NO;<br /> // picture 3<br /> if ( A.y + A.height < B.y )<br /> return COLLISION_NO;<br /> // picture 4<br /> if ( A.y > B.y + B.height )<br /> return COLLISION_NO;<br /><br /> // reaching at this point means that the rectangles overlap<br /> if ( shape == CIRCLE )<br /> { <br /> centerA_x = A.x + radius_A;<br /> centerA_Y = A.Y + radius_A;<br /> centerB_x = B.x + radius_B;<br /> centerB_y = B.y + radius_B; <br /> } <br /> distance_square = (centerA_x - centerB_x)^2 +<br /> (centerA_y - centerB_y)^2;<br /> if ( distance_square > (radius_A + radius_B)^2 )<br /> return COLLISION_NO;<br /><br /> return COLLISION_YES;<br />}</pre><br />As it is clear when looking into the if ( ...CIRCLE ) section it would be very painful for the CPU to consider from the beginning the two circle-objects as circles and proceed directly with the if ( shape == CIRCLE ) section. At least do it once the container-rectangles collide which is extremely lighter to find it out.Of course you can optimize this new variation of the function getCollisionResult but I leave it as an exercise.Theodosiοs Ekizoglouhttp://www.blogger.com/profile/09965963637480862598noreply@blogger.com0