iPhone Game Programming – Tutorial 4 – Bitmap Font Class
Tutorial 4 is now available. This tutorial comes in at around 1:40 and covers both the creation of a new class called AngelCodeFont, but also changes to the Image and SpriteSheet classes. The changes to the Image and SpriteSheet classes was necessary so that I could make use of vertex arrays which significantly improves the performance on the iPhone. The AngelCodeFont class allows you to specify an image and control file which has been created using an AngelCode bitmap font tool and then render any string you like to the screen.
Play Tutorial
Information about the website and tools that I use are in the tutorial, but I have included links below as well. Their Hiero tool is hosted on n4te.com who has recently worked on the Hiero tool to allow it to handle unicode fonts etc and make changes to the GUI.
The project created with this tutorial can be downloaded from the link below.
As always, let me know if you have any questions or queries. The next tutorial will be on Animation followed by a TileMap tutorial.
Enjoy!
Mike
129 Comments
mike on April 14th, 2009
Hey Dan, thanks for the post and the coffee, its really appreciated.
I hope you find the tutorial useful and if you get time, check out the post I’ve done on getting people ideas and suggestions. If there is anything you would like to see me cover, or you have a game idea you are happy to share just let me know :)
Mike
Eskema on April 14th, 2009
After read and listen the tutorial, i found some ¿¿¿errors???, you told us about use vertex objects to improve performance, but i see that you arent using the vertex array. To use the vertex array method, you need to fill the vertex and in only one pass, draw all the elements of your screen, with this:
glVertexPointer(3, GL_FLOAT, 0, vertices); glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates); glDrawElements(GL_TRIANGLES, index*6, GL_UNSIGNED_SHORT, index);
So we need to pass the data for each image into the indices, and then draw all with drawelements.
Maybe im wrong, but im looking at cocos source to improve performance on my tilemaps and it seems to me the perfect option. :)
Anonymous on April 15th, 2009
Let me first say I discovered your great tutorials a week ago and they are the best tutorials bar none!
I have always been interested in using the iPhone in landscape mode so I worked through tutorial 3 then modified it to rotate the display into landscape position by adding:
UIInterfaceOrientation:UIInterfaceOrientationLandscapeLeft
to Info.plist
After some funny render results I discovered when you rotate the display to landscape the origin does not re-orient itself with the new bottom left hand corner of the display; it remains in the now top left hand corner.
Without much fuss I remedied the problem by modifying the “renderAtPoint” method as follows:
- (void)renderAtPoint:(CGPoint)point centerOfImage:(BOOL)center {
// Use the textureOffset defined for X and Y along with the texture width and height to render the texture
CGPoint pointTransformed = CGPointMake(320-point.y,point.x); //Flip X&Y to transform coordinates to landscape left
CGPoint texOffsetPoint = CGPointMake(textureOffsetX, textureOffsetY);
[self renderSubImageAtPoint:pointTransformed offset:texOffsetPoint subImageWidth:imageWidth subImageHeight:imageHeight centerOfImage:center];
}
and rotating all my images (-90). This fixed the problem and I went on my way, until…
The new display approach utilized in your tutorial 4. I love how it reduces OGL overhead! but my coordinates hack no longer works in the new renderAtPoint method.
Does anyone have a better solution for displaying images in landscape mode?
Thanks again! If I figure anything out I will re-post.
mike on April 15th, 2009
@Anonymous, I’m glad your enjoying the tutorials.
I’ve not tried landscape mode yet, which is a good idea for me to try :)
I would try the following
Change the glOrthof command in the initOpenGL method to
glOrthof( -screenBounds.size.width / 2, screenBounds.size.width / 2, -screenBounds.size.height / 2, screenBounds.size.height / 2, -1, 1 );
and then place the following code under the glMatrixMode(GL_MODELVIEW) line in the same method
glLoadIdentity(); glTranslatef(160.0f, 240.0f, 0.0f ); glRotatef(270.0, 0.0, 0.0, 1.0); glScalef(1.0, -1.0, 1.0);
That should get everything to render ok on the screen, you will just have to remember that things are rotated and reversed when doing your own calculations. I’ve not been able to try this yet, but I’ll have a go when I get home later.
Let me know if that works ok or what problems your getting.
Mike
koko on April 16th, 2009
Mike,
Just curious were you also able to test the enhanced code on the real device? I’m using simulator to test my code and I heard that the simulator performance can be 100x faster than the real device.
I guess it would be good if there’s some way to “tune” the simulator to match the real device. But I don’t know if xcode has a way to do this.
mike on April 16th, 2009
Hi koko
Your right, the simulator is no good at all to test the speed, you really need to run it on the real device. The figures I provided re the speed improvement were from my iPhone and not the sim. I was really surprised at how much CPU overhead multiple OGL API calls have. The change to using vertex arrays was fantastic and has got rid of all the performance issues I was having with the bitmap fonts and tilemap classes.
As the sim uses the hardware on which it runs, it is much more able to deal with load and therefore is not a good representation of the real device. I’m not aware that this can be tuned or changed at all.
Mike
Anonymous on April 16th, 2009
Mike,
Thanks for the head start with the landscape mode stuff.
Looking further into the problem I found some interesting information. The iPhone simulator does not emulate the physical iPhone when “UIInterfaceOrientationLandscapeLeft” is called. The physical device corrects the coordinate system while the simulator does not. (I cannot test this but it is what i have read)
Using your method the screen does flip but ends up mirror image to what is expected. The origin is transformed to the now bottom right hand side of the screen with positive x toward the left. You could transform all the images but this is tedious for each object, not to mention no reflect method in your new .bmp font class. (no I do not think it is necessary to implement :)
To remedy the problem I discovered a solution that rotates the glView when running code in the iPhone simulator by modifying the following:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft animated:YES];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:YES];
if (TARGET_IPHONE_SIMULATOR)
{
UIScreen *screen = [UIScreen mainScreen];
glView.bounds = CGRectMake(0, 0, screen.bounds.size.height, screen.bounds.size.width);
[glView setTransform:CGAffineTransformMakeRotation(-M_PI/2.0f)]; //Use (M_PI/2.0f) for rotate right
glView.center = window.center;
}
[glView mainGameLoop];
}
and flipping the screenBounds as follows:
// Get the bounds of the main screen screenBounds = [[UIScreen mainScreen] bounds]; float swapTemp = screenBounds.size.height; screenBounds.size.width = screenBounds.size.height; screenBounds.size.height = swapTemp;
The status bar style is set to landscape then turned off to tell the physical iPhone to change screen coordinates. I cannot confirm that this is a requirement at this time but its included just in case.
I am pretty much a n00b with the iPhone SDK some of this code may be in completely the wrong location/bad coding practice. some of this still feels like a “hack” but it is working in my iPhone simulator. I understand you are a busy person and I hope this gives you a head start on 100% proper implementation.
Thanks again for your help,
~Paul
mike on April 16th, 2009
Hey Paul thanks for the info. I need to make sure I can handle landscape as there would be many game ideas which would require this view. Your info should help me when I start to check it out, so thanks for sharing.
If you find out anything else on the subject let me know and I will do the same when I look into it. The only thing to watch out for is the performance of glview. I can remember reading somewhere than performing a transform on a glview can cause it to perform really slowly. Let me know if you see this behavior.
Cheers
Mike
koko on April 17th, 2009
Mike I have another question. Perhaps you have already addressed this before in your tutorial, if so I apologize about the redundant question.
I notice that you are using GL_TRIANGLE to setup the vertices and textures, but will I see any difference (in performance) if I use GL_QUAD instead? Seems like at opengl layer everything is triangle anyway, but with GL_QUAD it would be easier to manage since each index only have to specify 4 value instead of 6?
mike on April 17th, 2009
Hi koko
Great question and one which took me a while to get my head around. OpenGL supports GL_QUADS no problem, but OpenGL ES does not support GL_QUADS, you have to use GL_TRIANGLES to make everything you want to render.
It can take a little getting used too, but when you are used to it, it works fine. I also don’t think you will see any difference in performance, just a tiny amount of extra memory used to hold the extra vertex info.
Mike
Jamie Hill on April 18th, 2009
Hi Mike,
Just finished watching the fourth instalment and been trying to follow along with my own implementation so that I actually learn something rather than just copying and pasting your code.
To be completely honest I’ve got myself all in a bit of a ca-fuffle as it’s all working but I don’t really have a good understanding of why it’s working. I think I need a better understanding of what is happening underneath with Texture2D and all the vertex/matrix black magic voodoo.
Do you know of any articles on a kind of “OpenGL for dummies” as I’m really not grasping the fundamentals? I’m fine when it come to 2D games programming in general and I’m sure I could just use your implementation or Cocos2D and be fine but I like to know what’s going on. All this OpenGL stuff is a bit more advanced than the old-school 2D bitmap graphics where you just have X and Y coords along with a blitter.
I am going to start dissecting the Texture2D class and writing my own simplified version to try and get an idea of what it’s doing… in fact, that would make a great screencast in itself with a brief introduction into the theory behind OpenGL.
Anyway, keep up the great tutorials, I’m sure I’ll get there eventually… enjoy the coffee!
mike on April 18th, 2009
Hi Eskema and sorry for the delay in a response, I missed your comment.
I’m not 100% sure I have understood your comment completely, but I think you were asking of there is an error in the bitmap code that is causing it not to use vertex arrays.
I actually checked the Cocos2D code when I was looking into using vertex arrays as like you I felt it looked like a good solution.
In the bitmap font code, as I loop over each character which is to be rendered I calculate the vertex and texture coordinates and add them to two seperate arrays, one called vertices and the other texCoords. Once I’ve finished dealing which each character in the string I’m rendering I then use the following code to load the vertices using glVertexPointer and the texCoords using glTexCoordPointer. I then pass in the indices array to the glDrawElements command which provides the reference indexes into the vertices and texCoords array.
glVertexPointer(2, GL_FLOAT, 0, vertices); glTexCoordPointer(2, GL_FLOAT, 0, texCoords); glDrawElements(GL_TRIANGLES, currentQuad*6, GL_UNSIGNED_SHORT, indices);
That code is taken straight from Cocos2D as I have used their approach.
So I think the code is doing exactly what you have described. I’m using exactly the same code in my tilemap class when rendering tiles to the screen.
Let me know if I’ve miss understood your comment or if you have any other questions.
Mike
mike on April 18th, 2009
Hi Jamie and thanks for the coffee, its great to know that these tutorials are helping others and that its appreciated.
I know what you mean about OpenGL being hard to pick up. I have read many forums where people have described OpenGL as being a steep learning curve. I think your idea of a ‘Basics of OpenGL’ would be a good tutorial and one I’ve added to the list.
The Texture2D class which Apple have used in their examples is really only part of what we are doing. I am using it in my code to just load an image and create an OpenGL texture from it. All rendering is done using the Image class rather than the code in the Texture2D class. So once an image has been created, the Image class does all the rendering work.
Where this does not happen is when I’m rendering my bitmap fonts. To get speed improvements I just ask the Image class to calculate the vertices for a given character and the texture coordinates for that character. These are then taken from the Image class and added to two arrays within the AngelCode Font class, one called vertices and the other texCoords.
Once I am ready to render these characters to the screen I load the vertices array which now has information on ALL the quads I want to dray, one for each character, using the following OpenGL command
glVertexPointer
I then load the texCoords array which has a list of all the texture coordinated for each character I am rendering from the bitmap font spritesheet using the following OpenGL command
glTexCoordPointer
Then when I call the glDrawElements array I pass in a third array which holds the indexes into the other two arrays, vertices and texCoords. This tells the glDrawlElements command where to get the vertices for each quad and then texture to be rendered within it. The command then just moves through those arrays rendering as necessary to the screen.
I hope that makes a little more sense. There is a link on the right hand side to http://www.nehe.com which I used to get a grip of the basics and still use today to further my understanding.
Let me know if you have any other questions and I’ll get around to a basics of OpenGL as soon as I can.
Thanks again for the coffee.
Mike
riq on April 20th, 2009
Hey Mike,
Nice tip about the Hiero bitmap font editor.
I’m going to support it in cocos2d.
cheers,
mike on April 20th, 2009
Thanks riq
I have to say that Cocos2D is a great looking engine. What I am doing with the tutorials to teach myself is really simple compared to what Cocos2D can do. I’ve also learnt a ton of stuff by seeing how things are done in Cocos2D. It is what provided me with the missing pieces to using vertex arrays. The indices were causing me great problems until i saw how it was done in Cocos.
I’m glad the info about Hiero was useful and I’m looking forward to see how its implemented. I’m working on my own simple particle system at the moment. I’ve got the basics working but finding that all the blending I’m doing is chewing up render utilization. I’m sure I’ll be heading to the Cocos code again to see how you have got it working :D
Thanks
Mike
riq on April 20th, 2009
Mike,
do you have any problem if I use your “parseFont” method in cocos2d ?
Thanks,
mike on April 20th, 2009
Hey riq, not at all. If the code is useful then I’m more than happy for you to use it :D
Mike
riq on April 20th, 2009
Mike,
regarding the particles, in trunk we are doing some refactoring. Basically, iPhone supports GL_POINT_SPRITE (1 vertex per particle). This is very fast!
The drawback is that the size is limited by a factor of 64 and that you can’t scale the whole system. We are implementing a subclass (BigParticleSystem) that is implemented using quads (4 vertex per particle).
// “fast” particle system
http://code.google.com/p/cocos.....leSystem.m
// big particle system
http://code.google.com/p/cocos.....leSystem.m
BTW, congratulations for your blog!
Jamie Hill on April 30th, 2009
Mike,
Have you found a way to do proper kerning using character pairs i.e. for something like “Wo” the “o” should be tucked under the “W”, however for “Wh” you obviously can’t shift the “h” left as it will overlap the “W”?
I see a mention of kerning pairs on the AngelCode website but it doesn’t look like the java version of the tool supports it.
mike on April 30th, 2009
Hi Jamie
I’ve just checked myself and your right, it doesn’t appear that the kerning info is being produced. I’ve put a query into the Slick2D forums to see if this is a known bug etc. If they provide a fix I’ll post on it here. I’ve got the Java source for the previous version which I’ve also tried and that does product the kerning info, so looks like it could be a bug.
I need to add kerning support to the AngelCode class. Its something riq from Cocos2D added when he took some of my code to support AngelCode in Cocos2D, so it would be easy enough to add, just need to work out how to make more hours in the day :o) but I’ll get around to it and post the updates.
Thanks for the info Jamie and I’ll get back to you ASAP.
Mike
caglar10ur on May 2nd, 2009
First of all thanks for the tutorials, they are really great source of information for a GL newbies like me :)
While watching Tutorial 4, i realized you start to rely on your AppDelegate object to manage your global variables which seems a really bad thing in obj-c world and its also breaks the MVC logic :)
You may want to check the (i just found this with a quick google search) http://cocoawithlove.com/2008/.....level.html for more detailed explanation s and find some alternate ways.
Thanks again…
mike on May 2nd, 2009
Hey caglar10ur, thanks for the feedback.
When I added the code to use the app delegate it didn’t feel right at the time, but as I was focused on the Image class I didn’t look too deeply into alternatives.
The link you have provided is really useful and I’m going to work on changing the Image class to not use the App Delegate as it does now.
Thanks again for the feedback and I’m glad your finding the tutorials useful :o)
Mike
Will on May 3rd, 2009
Hi Mike.
First, thanks very much for doing these tutorials – I know you’ve heard this before, but for me they have been (and continue to be) extremely helpful and I’m learning a lot.
I’ve been following along and making a program that currently loads up an Image and prints it to the middle of the screen and then in the “update game logic” part I rotate it a little more each time so that the image continues rotating. I also have a sprite that’s drawn to the screen and just stays there and finally I have a text string that scrolls across the top of the screen. Everything works fine on the simulator, but then when I try it on my iPhone, the sprite image does not show up and I get some strange effects with the text kind of going all over the screen. Have you seen anything like this and do you have any idea what’s going on? I know it’s hard to say without seeing the code but I’m pretty sure I’ve been following along pretty closely and I was wondering if you’d experienced anything like that.
Thanks very much and keep up the great work!
Will
Will on May 3rd, 2009
UPDATE: Hi Mike, I found a comment in one of your other posts where someone mentioned that in the simulator it’s ok if the case is wrong for loading images but it’s not ok on the phone… that was my problem! However, I still don’t know why I’m getting the strange artifacts to do with the scrolling text but I’m sure it has something to do with the rotation of the centre image because the “Artifacts” that I’m describing are almost like the text is being rotated – but why would rotating the image affect the text?
Thanks again.
mike on May 3rd, 2009
Hi Will. I’m glad you got the image problem sorted out. From your description I’m not sure what the problem could be with the Artifacts. Can you post/send a screen shot or your code and I’ll take a look.
Mike
Will on May 4th, 2009
Hi Mike.
Thanks for the quick response. I feel a bit silly but the problem seems to be gone and I really have no idea why. All that changed was that I was originally using your “font1″ font and that’s when the problem occurred. I switched to the “test1″ font and things were fine. Then, I wanted to take a screen shot so I could show you the issue but when I switched back to “font1″ the problem did not reappear. Of course it’s good news but I’m sorry to waste your time and also I always like knowing *why* something happened to prevent it from happening in the future.
Essentially what was happening, as I described above, was that after the first scroll of the message, when it was supposed to reappear again it would show up sideways along the left-hand side of the screen and rotate a bit and then the rest of the message would go back to normal. Then it would continue like that.
Anyways, thanks again for the response and if something like that pops up again I’ll be sure to document it before it fixes itself! ha!
Thanks.
Will
mike on May 4th, 2009
Hey Will, don’t worry, I have had that kind of thing happen a number of times and its very frustrating. I’m glad it seems to be ok now but let me know if anything like that happens again :o)
Mike
Will on May 5th, 2009
Hi Mike,
I’m pretty sure I have a real problem this time. It’s easily fixable, but I think it could also be easily fixed in the class to make it work more nicely. The problem is that if you set the scale of a font to 1.0 and its height is say, 50 pixels, and you print a message to the screen at y=300, the top of the message will be at y=300. So far, so good. However, if the scale of the font is set to 0.5, and you do a drawStringAt with y=300, the top of the font will not be at 300, but it will be at 275. In essence, the drawStringAt method does not pay any attention to the scaling factor of the font. I think it would be more conceptually correct if it did. What do you think?
Thanks again.
mike on May 5th, 2009
Hi Will, I’ll check that out. I was expecting to see the bottom of the text be drawn at the point provided in the drawStringAt rather than top of the string, so it sounds like there is a bug Image class somewhere in calculating the vertices.
I’ll try it out and see what is happening.
Thanks
Mike
mike on May 5th, 2009
Hi Will, I think I found the problem with the fonts. I’ve tried to get the fonts to render with the bottom of the line on the point specified but I’m having problems with that, so, I worked on the current model which is that the point is the top of the line and found a very small problem with a line in the AngelCode font draw method.
Inside the drawStringAt method, replace the the line where I define newPoint with the line below:
CGPoint newPoint = CGPointMake(point.x + ([charsArray[charID] xOffset] * [charsArray[charID] scale]), point.y - ([charsArray[charID] yOffset] + [charsArray[charID] height]) * [charsArray[charID] scale]);
I’ve tested this and it uses the scale correctly now, so that even if you scale down a font the top of the font is where you would expect it to be.
Let me know if this sorts out the problem or if you are still having a problem.
Mike
mike on May 5th, 2009
Hi Jamie.
I’ve tried to find out why Heiro v2 does not produce Kerning information and I’ve not got very far. In the console when you run the app it produces an error telling you that Kerning info cannot be found for the font you have selected, which is odd as it does it even when it is a TTF. I’ve posted on the forum where people have been working on it but had no responses.
I’ll keep looking and If I cant get the Java version working, I’ll use the windows version so I can implement the kerning anyway.
I’ll keep you posted.
Mike
mike on May 6th, 2009
@Jamie, I’ve had info back on Hiero from Nates the chap who did the recent upgrade work taking it to v2. It turns out that due to the Java API being used, you need to manually select the TTF file using the File option rather than the System option.
Using the File option allows Hiero to read the necessary Kerning information and it then provides the kerning info in the output file.
Now I know how to make this work I’m going to add kerning to the AngelCodeFont class. I’ll keep you posted on progress.
Mike
Will on May 10th, 2009
Hey Mike,
Sorry it took me a while to get back to you, but yes, the code that you posted above for defining newPoint fixed the problem that I was describing.
Thanks so much again for all this work you’ve been putting into the tutorials and with helping out those who are using them.
Will
RoberRM on May 10th, 2009
Hello Mike:
First of all, thank you very much for yet another great tutorial. I’m learning really fast here with your help! :D
One question though (maybe you’ve addressed this already in one of the following tutorials, and in that case, I’m sorry to bother you; but just in case you haven’t, I don’t want to forget this): is there a limitation in the number of characters a string can have to be rendered to the screen?
I was trying to render a longer sentence than the ones you used in the tutorial and all I got was a black screen. I shortened the sentence (to around 112 characters and it worked flawlessly. At 113 characters it started rendering weirdly, and at 120 or so the screen goes to black again.
At first I thought it was because the size of the image involved (or wherever OGL renders the Quads to), so I reduced the scale of the letters to see if that fixed the problem (or at least let me print more letters to the screen) but it had no effect.
I’m out of ideas right now, and I know a solution could be to manually fragment each sentence to 100 characters, but I just do not understand why it’s happening and that’s what is biting me! :D
Thank you in advance for your help.
RoberRM
mike on May 10th, 2009
Hi RobertRM, thanks for the comment.
I’ve had a look and indeed, when the message was over 95 characters it would actually crash. I’ve tracked it down to a small bug in the AngelCodeFont class.
At the end of the parseFont method there is a call to initVertexArrays. This set ups the arrays which will hold the quads for each letter to be rendered in your message. I was defining the size of this array based on the number of characters read from the font file rather than the possible number of characters in the message being rendered. Therefore, if you tried to access locations over that amount you were accessing memory which is not part of the array which unusual results :o)
If you work out the largest number of characters you will need to render, you can change that method as I have done below.
[self initVertexArrays:256];
I have just set it to 256 characters and as long as you don’t try to render more chars than that in a single method you will be fine. You can set this number to whatever you need.
I’m also going to place a check in the render code which will warn you if you are trying to render a message which is larger than the vertex array which has been defined.
Let me know if you have any problems.
Mike
RoberRM on May 10th, 2009
Thank you very much for your quick response, Mike!
I’ll be using your suggestion for the time being, but I’ll also think of something so that there’s no limit to the number of characters.
I’m still trying to fully understand this tutorial (for instance, I don’t know yet why the limit of characters I can render in to the screen is limited by the method used to get every character out of the ftn file). I’m trying to see if that could be changed.
As soon as I know why it can’t (or see how it can be done) I’ll post it here, OK?
Meanwhile, once again, thank you very much for your patience and help.
Rober
mike on May 10th, 2009
No problem RoberRM. The reason the call to initVertexArrays was in the parseFont method was that I was using the number of chars read from the font file to define how many vertices I was going to need. That was of course wrong :o) so I’ve now moved the method call into the initWithFontNamed method and created a constant value, 512, which is used to define the number of vertices supported.
If you were going to only have a constant length of string being created with a font then you could set the number of vertices to the length of that string. As you may well be creating strings of different lengths, then the number of vertices needs to be large enough to cover the longest single string you will render.
You could dynamically resize the array when a string is rendered which is longer than the longest string rendered so far, but allocating memory has a cost and doing things like that in the middle of a game can create performance problems. So the easiest and quickest way I’ve found is to create the vertex arrays as big as you need them once and then leave them like that.
I hope that makes sense and if you do find a cool way of making this dynamic let me know as I’m learning new techniques all the time.
Mike
RoberRM on May 11th, 2009
Wow! You are absolutely right! :D Now that’s why you are making tutorials and I’m following them! :D I’m more used to create software that is flexible (hence my previous suggestion); this game orientation is new to me, so I’ve never thought of performance before. :o
One last question: will you include this change of code in a future tutorial or is it too late already and I’d better try and figure it out myself?
Thank you very much for your help!
mike on May 11th, 2009
@RoberRM, I’m going to be putting those changes into the code and including it future projects :O)
Mike
Dan on May 21st, 2009
Hi Mike,
Firstly I would like to say thanks for creating these excellent tutorials, I really appreciate the time you have put into them.
I am very new to Objective-C and iPhone dev (only about 1 week so far) but my background is in C++ so my comments below may be incorrect and if so I apologise. Anyway I have just finished tutorial 4 and I have noticed a couple of things along the way that may be small issues (I have had a quick look at tutorial 6 code to see if you have already seen them but it looks like they are still there).
Firstly, in the Image class – getSubImageAtPoint
You alloc a new image object then at the end of the method return the image. If I understand correctly, this relies on the calling class to take ownership of releasing the memory for that object. If the calling class fails to release the object the memory will leak. I have implemented the return statement as return [subImage autorelease]. I was wondering if you did this for performance reasons or if it was a mistake (or that I have it wrong ;).
Secondly, in the AngleCodeFont class
In one of your tutorials when you implement the charsArray you mention something about memory management being taken care of as it is a NSArray. However you have implemented it as a c-syle array of CharDef*. If it was an NSArray the memory management would have been taken care of but I dont think it is fully taken care of in your implementation. i.e. in the parseFont method you have the following:
// Parse the current line and create a new CharDef CharDef *characterDefinition = [[[CharDef alloc] initCharDefWithFontImage:image scale:scale] retain]; [self parseCharacterDefinition:line charDef:characterDefinition]; // Add the CharDef returned to the charArray charsArray[[characterDefinition charID]] = characterDefinition; [characterDefinition release];
If I have this correct, you have alloc’d the object and also called retain on it so the reference count should be 2. You then assign it to an element in the array and call release on it. So your object is still valid (1) but there is nothing in the dealloc method to finally release this memory. I dont think you need the retain and release (because when you alloc an object it gets a reference count of 1) but think you do need something like the following in the dealloc:
for(int i=0; i< 256; i++) {
[myCharsArray[i] release];
}
Incidentally, I tried implementing the array as a NSMutableArray but found out that you can not add objects at indexes that are greater than then number of objects +1 that are already in the array (even if you initialise the array with a size). This makes it more difficult to use the unicode value of the character as an array index.
Sorry for the long post and thanks again for your work!
Cheers,
Dan.
mike on May 21st, 2009
Hi Dan. Thank is fantastic feedback and I appreciate your time in putting this together. There are certainly a couple of areas where I have had memory leaks and through trial and error found ways that seem to remove them. What you are saying above does make sense and I’ll check out your suggestions.
I’m learning more about Objective-C all the time coming from a Java background most recently, so these kinds of comments and feedback helps no end.
Thanks again and I’ll post about what I change so others get the updates as well.
Mike
mike on May 21st, 2009
Hi Dan
I’ve made the changes to the AngelCodeFont class and it seems to be working ok. The changes to the Image class cause the image to be released too early and it crashes. I need to spend some time checking it out a little more I think.
Thanks again for the feedback
Mike
Dan on May 22nd, 2009
Hi Mike,
Glad to give something back.
Once you have changed the Image class to auto release the object it passes back you will need to call retain on the returned object in any class that calls getSubImageAtPoint method. I can not access the code at the moment so cant remember which class calls this method. But basically you will need that class to take ownership of the returned object by using retain and release either in the scope of the calling method or use retain in the calling method and release in the calling classes destructor.
Hope that makes sense?!
Dan.
mike on May 22nd, 2009
Thanks Dan, that makes perfect sense to the point of causing me to blush as I didn’t think of that :o) I’ll make the change to the SpriteSheet class which uses that method and I’m sure that will sort things out.
Cheers Dan
Mike
mike on May 22nd, 2009
Yep, that did the trick :o) I’ll add the changes to the latest tutorial code so others can get that change.
Mike
Dan on May 22nd, 2009
Great stuff.
There was one other point that you may or may not want to consider :0).
It seemed to me that there was quite a bit of code duplication between the Image, AngleCodeFont and SpriteSheet classes. Looking at the structure of AngleCodeFont and the Image class it seemed to me that the AngleCodeFont class is actually a type of Image and should probably inherit from the Image class rather than contain it. I believe it is probably the same story for the SpriteSheet class. I have refactored the AngleCodeFont class to inherit from the Image class rather than contain it and it seems to work fine. I will be looking to do the same with the SpriteSheet class.
The main changes (from memory) are to take out the duplicated member variables from the AngleCodeFont class, update the vertices accessor methods in the Index class to take an index parameter (to enable the accessing of multiple vertices arrays) and passing self into method calls (on the Texture2D class I think) that expect an image object rather than the index reference.
I think that is it. When I get back to the code (probably not till Monday) I can check and explain further if necessary.
Cheers,
Dan.
mike on May 22nd, 2009
Thanks Dan. Anything you can share on that would be great. I am sure there is plenty of cleaning up which can be done :o) as its a side effect of not knowing in advance what your going to create. This stuff is all rather organic at the moment.
Having someone check back and highlight areas where we can streamline the classes is cool and a real help. If I get a chance I’ll check out the classes and see if I can make the changes. At the moment I’m getting the Particle Emitter class tutorial finished up.
Thanks again. Any and all info on this stuff is appreciated.
Mike
Dan on May 22nd, 2009
No problem Mike. If it helps I can mail you my refactored code (it differs a bit to yours as I have used my own coding standards but is on the whole the same).
This stuff is not all that important (I am sure you have plenty to do continuing your great tutorials), just something that can be used as ideas to tidy up later.
Cheers,
Dan.
mike on May 22nd, 2009
Anything you can send Dan would be great. All donations of code etc gratefully appreciated.
Your right about this stuff not being the end of the world, but it does tidy things up. I’m working on a larger project which I can’t say much about at the moment ;o) and this kind of information and keeping things as clean as possible will be a big help.
I will share more soon I hope :o)
Mike
MFerron on June 2nd, 2009
Hey Mike,
After completing this tutorial, I noticed that scrolling text will stop scrolling whenever I touch and drag. Once I release my finger, it will continue to scroll.
I have only been coding to your tutorials, so I have not done anything different than you have. Is this something you are aware of and covering later on?
Thanks for the help in advance, and sorry if you go over this later and I just haven’t gotten to it. And many thanks for these tutorials. I’d be lost without them.
MFerron on June 2nd, 2009
Nevermind! It was that 0.02 vs. 0.002 issue in the main game loop.
False alarm.
RoberRM on June 11th, 2009
Hello Mike!
I’m replying also to Anonymous who posted a message way back asking for a way to render in landscape mode and you provided a solution which flipped the image and made a mirror effect. Anonymous found a solution but I think I found a simpler one (although I’m not sure why nobody else has… Which makes me doubt myself).
1.- You tell the iPhone you’ll be using landscape mode (so that it knows and automatically tilts the simulator) by adding to the Info.plist the Key
UIInterfaceOrientation
with the value
UIInterfaceOrientationLandscapeRight
2.- As you said, you change
glOrthof(0, rect.size.width, 0, rect.size.height, -1, 1);
into
glOrthof( -rect.size.width / 2, rect.size.width / 2, -rect.size.height / 2, rect.size.height / 2, -1, 1 );
3.- And, almost as you said, after the
glMatrixMode(GL_MODELVIEW);
command you insert these four lines:
glLoadIdentity(); glTranslatef(-160.0f, 240.0f, 0.0f ); glRotatef(270.0, 0.0, 0.0, 1.0); glScalef(1.0, 1.0, 1.0);
That will make the game play perfectly in landscape mode and there won’t be any need of further calculations (at least I don’t think so). Notice that with those numbers glScalef has all positive numbers, which means it won’t be applying any mirror effect to anything.
Please, let me know if I’m wrong.
Cheers!
RoberRM
mike on June 11th, 2009
Hi RoberRM. I’ve not had a chance to try your code, but I see no reason why it should not work.
I’ve actually added some code to my project which handles the landscape view and I use the code below, which is even less code and seems to work ok.
// Switch to GL_PROJECTION matrix mode and reset the current matrix with the identity matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Rotate the entire view 90 degrees to the left to handle the phone being in landscape mode
if(!PORTRATE_MODE) {
glRotatef(-90.0f, 0, 0, 1);
// Setup Ortho for the current matrix mode. This describes a transformation that is applied to
// the projection. For our needs we are defining the fact that 1 pixel on the screen is equal to
// one OGL unit by defining the horizontal and vertical clipping planes to be from 0 to the views
// dimensions. The far clipping plane is set to -1 and the near to 1. The height and width have
// been swapped to handle the phone being in landscape mode
glOrthof(0, screenBounds.size.height, 0, screenBounds.size.width, -1, 1);
} else {
glOrthof(0, screenBounds.size.width, 0, screenBounds.size.height, -1, 1);
}
This puts the 0,0 origin in the bottom left hand corner when in landscape mode.
Mike
RoberRM on June 11th, 2009
Hi Mike!
You are completely right! Your solution is also more efficient. :o
Thank you for sharing :)
RoberRM
RoberRM on June 11th, 2009
One question, though: where do you define PORTRATE_MODE?
(Sorry for the double-post)
RoberRM on June 11th, 2009
I think I’ve solved it by myself: right before your code I’ve added
UIInterfaceOrientation PORTRATE_MODE = [UIApplication sharedApplication].statusBarOrientation;
and it seems to work.
Now I have a really difficult question: I’m trying to make my game detect touches and I think I’ve succeeded. The problem is I’ve also HIDDEN the status bar and I can’t seem to get any response in the area where the status bar is, so if someone clicks there nothing happens.
I’m going crazy over this and I cannot find any solution. Your ideas would be much appreciated.
Thank you very much in advance.
RoberRM
mike on June 12th, 2009
Hi RoberRM
I and others on the blog has also seen this problem. All I can tell you is that the issue only seems to occur on the simulator. When you run the same code on a real device it works fine with touches being handled all the way to the top of the screen.
Mike
brownb49 on July 22nd, 2009
Hey Mike,
I really love the tutorials, they are a great help.
I have been going through them using the 3.0 simulator and have not run into any problems until now. When i compiled the program (after adding my own font and about 1:30 through your video) the simulator would open the program and just be a black screen. I also tried downloading your project4 posting from above and compiling it in the 3.0 simulator. It did the same thing. It does work in the 2.2 simulator however.
Do you know what the problem could be? I know you are on vacation so no worries on the response time.
Thanks
Mike on July 22nd, 2009
Hi brownb49, there is a blog post called “a big black screen of nothingness”. That post describes how to overcome the black screen when using the 3,0 sdk.
Let me know if that does not work.
Mike
brownb49 on July 22nd, 2009
Thanks Mike, everything is working now. Sorry about the post when there is a clear solution posted, I just didnt see your actual topic. I was looking through all of the comments from each game tutorial instead haha.
Eric on August 2nd, 2009
Has anyone adapted the code from this tutorial to render text into a bounding rectangle with word-wrap etc?
mike on August 2nd, 2009
Hi Eric
That is something I started to look at a while back as the original Texture2D class from Apple allowed you to render text into a rectangle. I removed that code from my version of Texture2D as I was not using it. I didn’t finish that code, but taking at a look at the original Texture2D class may help and may even provide you with what you need for the moment.
It is certainly something I want to look at doing though, just not sure when at the moment :o)
Mike
Eric on August 2nd, 2009
I’m doing all my UI in OpenGL and I’ve come to the last things. Like the Help screens. I started writing code to parse a big string and break into line starts and line lengths and then I’m using your Angel Font code to render out the lines. So far so good although I’m not doing anything fancy. I’m still having the font kerning problems at normal font sizes though (e.g. at about whatever font size your web page renders at).
Eric
p.s. I’d love to see forums here, especially if I could post a screenshot of what I’m working on and an example of the font issue.
Simon on September 2nd, 2009
Hi Mike,
I’ve just found your tutorials (very useful), but I have a problem with this tutorial in that. When I recreated it I was presented with a blank screen when running it on a device using 3.0.
I downloaded your code and got the same problem.
The app works fine when I change to 2.2.1 or 2.0 on the device. Do you know what is causing this problem, is it something you’ve seen before?
I should also state that in the simulator I have the same problem.
I appreciate this tutorial is not your latest but would really love to know what’s going on.
thanks
Simon on September 2nd, 2009
Hi Mike,
I’ve noticed that you responded to this question previously. Sorry I missed that on my first scan.
Simon on September 2nd, 2009
Hi Mike,
Sorry again for posting for the third time (in one day and on the same subject) but you reference a blog titled “a big black screen of nothingness”. I’m obviously a bit slow but I can’t find it and would really appreciate it if you could point me in the right direction.
Thanks
Simon
Simon on September 2nd, 2009
John,
Thanks… Can’t believe that one line was giving my such my pain.
thanks
Simon
tdk08 on September 7th, 2009
I can’t understand why no one has posted about this problem before unless I am not realizing something obvious but this is what the Tutorial 4 project’s image.m file looks like when I download it:
http://i29.tinypic.com/35n95vq.png
which gives me errors about flipHorizontally and flipVertically, any idea what the problem is?
That’s not what the image.m file looks like in the video!
tdk08 on September 7th, 2009
Specifically the calculateTexCoordsAtOffset method is the problem, forgot to include that, and sorry for the double post!
mike on September 7th, 2009
Hi tdk08, I’ve just downloaded Tutorial 4 and tried it to see if there are any problems. I’m not getting any error messages at all when compiling. What errors are you getting.
The only change that needs to be made to run the tutorial from OS 3.0 onwards is to replace the following line in the app delegate
[glView mainGameLoop];
with this line
[glView performSelectorOnMainThread:@selector(mainGameLoop) withObject:nil waitUntilDone:NO];
Let me know how you get on.
Mike
tdk08 on September 7th, 2009
Yeah I did change that line (thanks 4 the tip though, fast response, really appreciate it) like I said before, it could be some stupid mistake on my part but this is the class from the video:
http://i29.tinypic.com/r8dl39.png
It’s not the one in the project link at the top of this page, I guess I could just change mine to look like the video’s method but I just wanted to know the deal with this whole thing. The errors I get are:
‘flipHorizontally’ undeclared (first use in this function)
‘flipVertically’ undeclared (first use in this function)
What were those variables intended for? I tried running the project untouched(except for changing the sdk which is required because I only have 3.0) and still get these errors so even if the project is supposed to be the way it is at that link, there is no mention of those ‘flip’ variables anywhere else in the project, thanks again and I actually have another quick question(srry for the long post) these amazing tutorials that you have worked so hard on; what is your rule on using these for an actual game to be released? I mean just the image, spritesheet, font, etc. classes. Those are extremely useful, both to learn from(which I definitely have) and to utilize in-game. It just seems that you worked hard on these and I feel like I’m freeloading if I use them so I wanted to be sure and ask you first! Thanks!
tdk08 on September 7th, 2009
I looked through the downloaded project and I did see those ‘flip’ variables and now I placed them in my project which now successfully runs but was that in the video? Just curious at this point :)
mike on September 7th, 2009
Hi tdk08, that’s my mistake on the video. I don’t think I covered those variables which are in the actual project, I’ll have to go back and correct that.
As for the code, you are free to use it as you wish. I did a post on the license for all the code on the site which you can find at
http://www.71squared.com/2009/.....e-details/
Thanks for the comments and good luck on any projects you have running :o)
Mike
Sergiu on October 7th, 2009
Hi mike,
Quite and amazing tutorial you got there, but i have a question if i may. Why do you use malloc and then bzero ? Shouldn’t it be faster doing a calloc() ?
texCoords = calloc(sizeof(texCoords[0]), totalQuads); verticies = calloc(sizeof(verticies[0]), totalQuads); indices = calloc(sizeof(indices[0]), totalQuads * 6);
Thx for the tutorials.
Sergiu
mike on October 7th, 2009
Hi Sergiu
Thanks for your comments and your right, calloc would be easier and is in fact what I am now using. I only realised after I had completed the tutorial :o)
Mike
Sergiu on October 7th, 2009
Sorry made a mistake above it’s
texCoords = calloc(totalQuads, sizeof(texCoords[0])); verticies = calloc(totalQuads, sizeof(verticies[0])); indices = calloc(totalQuads * 6, sizeof(indices[0]));
Rick on October 14th, 2009
Hi Mike, thanks for your great tutorial. I have a problem with EAGLView.m when I load the text.
If I write:
font1 = [[AngelCodeFont alloc] itWithFontImageNamed:@"font1.png" controlFile:@"font1" scale:1.0f filter:GL_NEAREST];
The application crash, I have checked and there are no errors on the debug.
If I delete the string everything is fine, but obviously the text doesn’t appear.
Thanks again.
Rick
mike on October 14th, 2009
Hi Rick
When the app crashes do you get any error message at all?
If you step through app line by line, does it actually fail on the line above or when you try to draw a string with that font.
Any info you can provide would be great.
Mike
Rick on October 14th, 2009
Hi Mark
Thank you for your replay. At the moment I’m using SDK 3.0, but I have also made some changes in the code.
This is the initGame function in EAGLView.m
- (void) initGame {
glInitialised = NO;
playerShip = [[Image alloc] initWithImage:[UIImage imageNamed:@"uno.png"] filter:GL_LINEAR];
ss = [[SpriteSheet alloc] initWithImageNamed:@"MauriceFrames.png" spriteWidth:90 spriteHeight:90 spacing:0 imageScale:1.0f];
sprite = [ss getSpriteAtX:1 y:2];
//font1 = [[AngelCodeFont alloc] initWithFontImageNamed:@"test1.png" controlFile:@"test1" scale:1.0f filter:GL_NEAREST];
message = @"Message on the screen";
messageX = 320;
messageWidth = [font1 getWidthForString:message];
}
You can see that I changed 2 files (uno.png and MauriceFrames.png), test1 and font1 are as the original.
I didn’t include playerShip1 because I didn’t need it.
and this is a renderScene function
- (void)renderScene {
if(!glInitialised) {
[self initOpenGL];
}
// Make sure we are renderin to the frame buffer
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
// Clear the color buffer with the glClearColor which has been set
glClear(GL_COLOR_BUFFER_BIT); // Pulisce lo schermo !!!
// *** Render the game Scene ***
[playerShip renderAtPoint:CGPointMake(160, 240) centerOfImage:YES];
[sprite renderAtPoint:CGPointMake(160, 100) centerOfImage:YES];
[font1 drawStringAt:CGPointMake(messegeX, 450) text:message];
// Switch the render buffer and framebuffer so our scene is displayed on the screen
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
In EAGLView.h I have this beat
// Game specific items Image *playerShip; SpriteSheet *ss; Image *sprite; AngelCodeFont *font1; int messageWidth; float messageX; NSString *message;
When I build or debug it doesn’t give me any errors.
The App run for 1 second, than it stops and crash.
This is the problem, but I need to fix it before to move to the next great tutorial.
Thanks for your help
Rick
Chad Fuller on November 16th, 2009
Mike,
I have been following along with the tutorials. I think I have a great understanding of what is going on now, but I have one problem. When I run my project, I can not get anything to render to the screen unless I take out the call to the mainGameLoop in the AppDelegate class. The result is the ship plus the text but the text doesn’t scroll across the screen because the loop is not running. I am not really sure what to do to fix this. Any ideas?
Chad Fuller on November 16th, 2009
Ok…I found your suggestion to change the call to the mainGameLoop for OS 3.0. Now I have my images rendered to the screen but still no scrolling text.
mike on November 16th, 2009
Hi Chad
From what you have said I’m not sure why you don’t have anything scrolling. If the game loop is running and its called your update methods then it should be working ok.
Is there any code you can post or email to me and I’ll take a look.
Mike
Chad Fuller on November 16th, 2009
I am going to give it another go first. I have messed with so much of the code trying to make it work before I changed the call to mainGameLoop for OS 3.0. I am going to look back through the tutorial download and make sure my code is the same as your code.
On another note…you may cover this in a future tutorial but I was wandering what your thoughts were on managing players/multiple players in a game and all the attributes that could go with them like score, game pieces they have, etc…
Thanks.
mike on November 16th, 2009
Hi Chad. Let me know how you get on checking through the code.
As for multiple players, its not something I have covered yet. I do have plans to do something on mult-player over the network/bluetooth using the GameKit APIs, but that is not going to happen until next year because of the book.
As for storing info for the different players, you could have a single player class that has an instance created for each player. Within that class you could then manage the state of that player, pieces or inventory they have etc. Not sure if that helps any :O)
Mike
Chad Fuller on November 16th, 2009
As for the player class, that is what I was thinking also.
Tell me about the book…
mike on November 16th, 2009
Great minds think alike :o)
As for the book, I’m writing a book called “Beginning iPhone Game Development” for Addison-Wesley. You can read a post I did on it here http://www.71squared.com/2009/07/writing-a-book/
The key thing about the book is that I am documenting the creation of a complete game, Sir Lamorak’s Quest of which you can see a sneak peak at http://www.71squared.com/2009/.....uest-demo/
Its going to be following the same kinds of lines as the tutorials on the blog, but the code is much more optimised with a LOT of new code over what the tutorials are using. There will also be videos for each chapter in the book as well.
Current release schedule is Spring 2010 :o)
Mike
Chad Fuller on November 17th, 2009
The book sounds like something I would be interested in. Thanks for the tips.
Rob Jones on January 1st, 2010
Has anyone tried this in a 3D OpenGL world? I’ve made it pretty far down the path, but I’m stumbling around with the different coordinate systems: CG, OpenGL, Textures, converting to GL_SHORT for efficiency.
I was just wondering if anyone else has been down this path and maybe some advice as well.
Rob Jones on January 6th, 2010
I’ve mostly solved this. I still get a bit of a wiggle on my fonts though, and they look slightly different at different points on the screen. I think this might be an artifact from using a viewing frustum. I’m rendering the fonts as close to the front clipping plane as possible to eliminate these problems.
mike on January 6th, 2010
Hi Rob, it does sound like your making progress. If there is anything you can share I’d be really interested in a couple of things. How you have used the font class for 3D and also how you are converting everything to GL_SHORT. I’ve thought about doing it myself but not got around to it.
Cheers
Mike
Rob Jones on January 6th, 2010
Hey Mike. I can share on what I did to use the font class in 3D. That will take a bit of time to put together. I basically rolled my own font class with 3D as the goal, and used your tutorial and AngelCodeFont class as a guide. Also, I never implemented a version of your Image class so I use Texture2D directly.
As for the GL_SHORT part, well, ahem, I sort of punted on that for now. I *am* using GL_SHORT for all my other 3D rendering and texture mapping, but that’s fixed objects that get animated around the screen. I’d be happy to share details on that. It was an adventure. It’s well worth the effort though.
Rob
Rob Jones on January 7th, 2010
I found my problem. I had hard-coded the filter to GL_NEAREST in the init: method of my BitmapFont class. The bitmapped fonts look great now!
Scott on January 20th, 2010
Argh!
mike on January 20th, 2010
??
Scott on January 20th, 2010
Sorry, not very constructive, I know. I can’t seem to find a feedback button on the site. If you can see my email address, could you send me a message so I could respond there? (Or tell me where to email to reach you?)
YH on January 27th, 2010
First of all, thank you very much for this great series of tutorials, I find it really helpful with my learning.
I encountered a problem using this Bitmap font class where some characters doesn’t align properly for some of the fonts. For example “Settings” with the “Snell Roundhand” font, the ‘g’ seems to have a wrong x-offset value. I tried to edit the .fnt file and that doesn’t seem to work right.
I am just wondering if anyone has ever encountered the same problem and solved it?
mike on February 5th, 2010
Hi YH and thanks for the comment. There are some problems with the BitMapFont class that I found whilst rewriting the class for my book. The calculation for the y location of each character needs to be changed.
The original code in the tutorial looked like this
CGPoint newPoint = CGPointMake(point.x + ([charsArray[charID] xOffset] * [charsArray[charID] scale]),
point.y - ([charsArray[charID] yOffset] + [charsArray[charID] height])* [charsArray[charID] scale]);
The new code I am now using looks like this
int y = aPoint.y + commonHeight - (charsArray[charID].height + charsArray[charID].yOffset) *
charsArray[charID].image.scale.y;
int x = aPoint.x + charsArray[charID].xOffset;
CGPoint renderPoint = CGPointMake(x, y);
You’ll see that I am now using the commonHeight parameter which is being read from the .fnt control file. These changes correctly position the characters in fonts.
I have fixed the original tutorial code, so if you download the code for tutorial 4 again, you should see the fixes in place and it should fix the problem you are seeing.
Hope that helps.
Mike
Bitmap Font Update | 71² - The ramblings of two 30-something developers on February 5th, 2010
[...] Tutorial 4 – Bitmap Fonts [...]
Rob Jones on February 5th, 2010
I assume you mean the lineHeight parameter on the common line, i.e. the second line of the .fnt file.
mike on February 5th, 2010
Hi Rob, yes, your right, I went and used the name of the ivar I’m using in the class. The actual param in the file is indeed lineHeight.
Thanks for spotting that :o)
Mike
Boss on February 6th, 2010
Hi Mike, I am very excited about your font code, I am currently rebuilding it for my own purpose, but still, thats an awesome piece of work.
Only one problem i have – I am not able to reproduce scaling properly, it seems that texture mapping is not affected, therefore it only scales X a Y coords, but font size remains unchanged, even if I use your last version posted few days ago. I’ve implemented option to use scale as font size in PX, but that’s working fine, calculation sets the scaling as expected, no mistake in that. Should I look into my code for a bug, or is your class also affected and scaling is wrong?
Thanks in advice, keep your good work up!
mike on February 6th, 2010
Hi Boss
The code seems to be working OK in the latest Tutorial code I posted. I just tried rendering the text at 4x size and it worked fine. It sounds as though something is not right with the texture coordinate calculations you have.
Let me know if you spot anything.
Mike
Boss on February 6th, 2010
I’ve discovered problem – I was not passing scale parametr into image, so it remain same for every scale I used.
I’ve also found a bug with your calculation – you are using this formula:
y = point.y + commonHeight – (charsArray[charID].height + charsArray[charID].yOffset) * charsArray[charID].scale;
However, commonHeight should also scale with scale parameter, if you want the text to be aligned “left,bottom”. I think that corrent formula then looks like:
y = point.y + (commonHeight * scale) – (charsArray[charID].height + charsArray[charID].yOffset) * charsArray[charID].scale;
mike on February 6th, 2010
@Boss, nice spot. Your right. Using scale with commonHeight means that the x,y is the bottom left had corner of the text rendered. Thanks for the feedback. I’m going to change my new class now :O)
I’ve also made the change to the tutorial 4 code and posted it back so it’s also make use of the scale.
Thanks again
Mike
Boss on February 7th, 2010
Hi again,
I’ve made an improvement in alignment, see for your self, maybe you can use it.
// Horizontal alignment – wrap point is set on the left side
case kHorizontalAlignment_Left:
x = aPoint.x + charsArray[charID].xOffset;
break;
// Horizontal alignment – wrap point is set in the middle
case kHorizontalAlignment_Middle:
x = aPoint.x + charsArray[charID].xOffset – (int)(stringWidth/2) ;
break;
// Horizontal alignment – wrap point is set on the right side
case kHorizontalAlignment_Right:
x = aPoint.x + charsArray[charID].xOffset – (int)(stringWidth) ;
break;
}
switch (aVerticalAlignment) {
// Horizontal alignment – wrap point is set on the left side
case kVerticalAlignment_Bottom:
y = aPoint.y + (commonHeight * scale) – (charsArray[charID].height + charsArray[charID].yOffset) * charsArray[charID].scale – (int)(10 / scale);
break;
// Horizontal alignment – wrap point is set in the middle
case kVerticalAlignment_Middle:
y = aPoint.y + (commonHeight * scale) – (charsArray[charID].height + charsArray[charID].yOffset) * charsArray[charID].scale – ((commonHeight * scale) /2) ;
break;
}
You also may need to calculate string width, but only once per render, therefore this is handy:
if (aHorizontalAlignment != kHorizontalAlignment_Left && stringWidth == 0) {
stringWidth = [self getWidthForString:aText];
}
I use enum for better orientation.
Any advices will be nice.
Have a nice codeday
Boss on February 7th, 2010
yey i tried to use code mark but seems that i am using it wrong, sorry :-)
Eric on February 13th, 2010
Did the kerning code get pulled out? I’m merging your latest with what I have and unless I’m makin’ some nutty perforce mistake it looks like all the kerning goodness is gone.
mike on February 13th, 2010
Hi Eric, No, your not missing anything. I did pull the kerning code as I and others were having performance problems. It’s something I want to add back in but I’ve just not had a chance to really look at it.
Once the book is finished I’ll go back and work on putting kerning support back in that does hopefully not have the same performance issues.
Mike
Rob Jones on February 13th, 2010
Is the kerning code still available somewhere? I’d really like to see it and compare to how I solved my kerning problems in OpenGL 3D coordinates.
mike on February 13th, 2010
I would need to hunt it down in my backups. It was implemented in the same way kerning is implemented in Cocos2d. Cocos2d took the bitmap font parser I created and added it to Cocos2d and adapted it for their needs. This included adding kerning support.
I’ve not checked lately, but you should be able to see how it was done there. I’ll also try to dig out my older code.
Mike
Rob Jones on February 13th, 2010
No need to hunt it down if it’s in Cocos2d. I’m just curious if I’m doing it correctly. My fonts look good, and that’s really what counts, but I haven’t tested a lot of fonts so I don’t know if I’ve missed some corner cases.
Real quick, my method in 3D is to detect when something special should happen, e.g. a ‘u’ tucking up under an ‘f’, and then adjust the z-coordinate so that the ‘u’ is slightly nearer to the viewer than the ‘f’. Since this leads to quads overlapping I have to use blending and transparent backgrounds for the letters, but I was doing that anyway.
There’s also a bit of trig involved to get the sizing right, since the quads are at different depths, but that was actually pretty easy.
mike on February 13th, 2010
OK, that does sound a little more complicated than using the information inside the fonts control file. Inside the control it is possible to output extra lines of kerning information. This information specifies the pairs of characters that need special spacing such as f and u.
The idea was to load this information into dictionaries and check to see if the string being processed contained these pairs. If so, then the spacing adjustments were made. The problem was that in my code I was using dictionaries which just were not fast enough. It would work better with hash tables or something like that.
I think using information from the .fnt file would end up being quicker that the calculations you are doing.
That said, if it works and you have the performance you need, why change it. Premature optimisation is the root of all evil ;o)
Mike
Rob Jones on February 13th, 2010
Yeah, my method could be slow, but I know it’s not a bottleneck in my code, and I’m not rendering enough fonts to care otherwise.
The calculations aren’t that difficult. If the width of a character is greater than the xOffset+xAdvance, then I adjust the depth and calculate x and y coordinates based on the new depth.
There are lots of ways this method could be optimized. I could always cache calculations for depths, or just pre-calculate the information. The same few depths tend to get used over and over. There wouldn’t be much of a storage penalty here.
What I’m more concerned about is that this simple method misses some important corner cases, or conditions I’m totally unaware of.
chrisboardman on March 3rd, 2010
Hi, a massive thank you for all your efforts in the tutorials this past year. They have all been a huge help in my OpenGL adventure, so thank you, it is very much appreciated.
I was wondering if you had any thoughts or information on the licensing of embedding fonts using this method? I’ve heard mixed reports about using fonts this way, even with a commercial license and it would be a shame to be held back by any legalities, given such a great implementation. It works so well!
Any thoughts would be great, and thanks again.
brianxu on March 12th, 2010
Hi Mike
Tons of thx to your work in the serial of tutorials! It is really brilliant! I am currently working on an Iphone 3GS project about 3d maps and I get lots of inspiration from your tutorials. As you know 3GS is using opengles 2.0 which is programmable pipeline and way different from 1.0. I have to implement a lot of functions such as glTranslate, glRotate myself. I am still learning the shading language. I’m trying to use your codes in my project. I am just wondering if you have any suggestion regarding migrating your codes to the 3GS platform?
Thx in advance!=)
Bruce on March 31st, 2010
Dear Mike,
I am so glad to find the tutorial in the web, and now I have used your latest bitmap font class in my iphone game. However, unfortunately I found there are still a lot leaks when I run the instruments of Xcode. I found the leaks code tracking display just like below:
CharDef *characterDefinition = [[[CharDef alloc] initCharDefWithFontImage:image scale:scale] retain];
[self parseCharacterDefinition:line charDef:characterDefinition];
// Add the CharDef returned to the charArray
charsArray[[characterDefinition charID]] = characterDefinition;
[characterDefinition release];
I have found you have get the others’s comments about the leak problem, and I found you have said the bug have been fixed and updated the files, actually I downloaded the latest project.
So, could plz tell me how this happened, is the error in your class or some thing else? is there any way to fixed?
AD on June 27th, 2010
I downloaded your project, but it is for a very old SDK version. Please can you upload a newer version?
Also, would it be possible to have an abstract Bitmap font framework posted?
mike on June 27th, 2010
Hi AD
I am tied up getting the final edits for the book finished at the moment, but as soon as that is done I am going to look to get the tutorials updated to the latest SDK. I’ll also look at creating an abstract bitmap font class. If you want to have a go an see how you get on, I’d be happy to look at any problems you come across.
Mike
keith on July 11th, 2010
Hi Mike,
Your tutors is great.
when I try to use BitMap front to display a random number , it give me some error, can you tell what i should change my code?
thank you very much
keith Chan
int random_number;
random_number1 = 100;
message= [NSString stringWithFormat:@"%d", random_number1];
messageX = 320; messageY=200;
messageWidth = [font1 getWidthForString:message];
mike on July 12th, 2010
Hi keith
What are the errors you are getting. Without the error it is hard to identify the problem.
Mike
Keith on July 12th, 2010
Dear Mike,
Thanks for reply, sorry have not give the details.
There is no error message when build the project, i place this function into a Starttouch screen event.
When I run the similator the program run ok, but it close the app. automaticlly when I click the similator screen .
When i replace the code
message= [NSString stringWithFormat:@"%d", random_number1]; ->
message = @”99″;
the app. work good.
I believed i make something wrong about pass a value into a point, not passing a address??
thanks
keith
keith on July 12th, 2010
Sorry for my poor english.
what i means is i just want use the bitmap font class to print out a random number, but the app. close itself with no error message. My code as following.
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
Int random_number1=random()%100;
message= random_number1;
messageX = 320; messageY=200;
messageWidth = [font1 getWidthForString:message];
}
keith on July 15th, 2010
dear Mike,
please help, as i find many web site and try many times, i also cannot get what i want.
only you can help me!
thanks
keith
mike on July 15th, 2010
Hi Keith
I’m not sure how you are defining your message ivar, but the way you are assigning it is incorrect. The getWidthForString method requires a parameter of type NSString. If you are defining the message ivar as an NSString, then just pointing it to a random will not work, you would need to use something like
message = [NSString stringWithFormat:@"%i", random];
I have use the exact code above with the correction in place and it works fine.
See if that helps.
Mike
keith on July 16th, 2010
Dear Mike,
thanks for your help first, your tutor really useful.
But i just download your tutor 4 coding,
and using simulator-3.0|Debug to run it.
Only replace the one line at EAGLView.m
but it give me the same problem, the app. close itself with no error message.
//message = @”This is a longer message to see if this is going to work….”;
message = [NSString stringWithFormat:@"%i", random];
Pubudu on July 16th, 2010
Hi Mike,
I’m kind of new to iphone programming and find ur tutorials very useful.Thanks for all of that.
I have this problem in my mind for sometime and hence thought of asking u directly.
Lets say the program I’m trying to write loads an image(a pattern with couple of colors). Then it’ll play a sounds if the user’s finger is inside image and will not do anything otherwise. Also it’ll change the sound (when finger is inside the image) depending on the color of the image at finger’s location.
I was successful in having an image loaded and accessing the finger location coordinates, playing sounds by wav files etc… Yet I couldn’t figure out following two issues,
1) How to detect whether the finger was in or out the image?
2) How to access the color value of a given pixel in a an image. (let say the loaded image is an bitmap to make things simple)
I tried to do this for couple of months but had no progress. I went through almost all apple tutorials and some of yours too…but still cldn’t resolve. I’d be very greateful to you if you cld guide me on this.
Thanks
Pubudu





Dan on April 14th, 2009
Mike thanks so much for this tut!! Hope you had a great weekend. Downloadin it now and can’t wait to watch after work. Enjoy the coffee. Thanks again for takin the time to put these together! Great job!!