Skip to content

Unity: Debug.Log console messages with timestamp

NOTE: this is a pretty old post, and by now Unity has added lots of great features in this area, including of course the much required timestamps. This makes the described workaround unnecessary!

While Unity is packed with professionalism all over, the one thing that is astonishingly basic is the output of debug messages to the console log. Until these days there is really not much you can configure or customize here. It is like it is. And that looks like that:

UnityConsoleWithoutTimestamp

What I really missed is at least a proper timestamp. For one it prevents the aggregation of identical message (I never understood what that would be good for, but maybe that’s just me…), furthermore you can check how current the latest entry in your log is. I think it is not even necessary to explain why that is useful. Every other log in the world would have timestamps for each entry, so we want our log to look like this:

UnityConsoleWithTimestamp

Actually this is pretty easy and not much more than a bit of routine work. Since I did it already, you don’t have to. Unitys Debug class is declared as sealed, so we can’t just inherit from it and overwrite what we need. Instead we just define a new class named Debug. It will cover the original one  and call ours (as long as someone didn’t fully qualify a call with the UnityEngine namespace, which you would normally not do anyway). We just prefix it with the desired format of a timestamp (the code contains already one alternative for you to play with) and call the original log function. And done.

But just wait: since your new Debug class will catch all functions of the original class, you have to do a bit more tedious work to mirror all of them and route them to the Unity implementation, if it should work seamlessly. Lucky you…I did that as well. So just download the C# class and drop it into your script directory, and you are instantly enjoying timestamped log messages without any additional work.

Using Unity Toolkit 2D and LeanTween

Even with Unity 4.3 and its latest 2D features, I am sure a lot of you still use the great 2D Toolkit on top of it. It has so much more great features that Unity does not offer (or only for Pro). For tweening, I use LeanTween, because it has great support and ― nomen est omen ― really operates very slick and lean. The one thing that I found a bit tedious is applying two of my most used tweens: alpha and scaling. Both can’t be applied in the regular fashion on the game object, but should rather be applied directly to the sprite component. So you need to fiddle around with LeanTween.value and write all the same code over and over to accomplish what it takes to do that properly. This also really messes up the code, so I created a nice helper that makes it really easy to do alpha and scale tweening directly on a sprite. All you need is the latest LeanTween (>= V2.031), since it offers some great new features that you’ll need to do that. If it’s not yet available on the Asset Store, you can always grab the latest version directly from Github.

Just create a new script named LeanTweenExtension.cs with the code below or download it ready-made. Then you can simply append setSprite() to your LeanTween alpha or scale statement, and the operation will be applied to the sprite component rather than to the gameobject. Everything else (looping, easing, …) works exactly the same as before. There are just two catches:

  • You can’t use your own onUpdate function on that tween, since it is already needed internally to do the magic
  • You can’t scale separate x/y/z values at the same time using LeanTween.scale. While you can (and need to) provide the scale as a Vector3 parameter, only the x component is used and applied to x and y at the same time. This is probably the 90% use case anyway, but if you need to do it separately, you can just use separate and consecutive LeanTween.scaleX/Y/Z  calls to achieve the same (The reason is that the LeanTween generic callback function that is used internally here just tweens on a single float, not a vector)

There are two signatures to call: setSprite() and setSprite(tk2dSprite sprite). The first will lookup the tk2dSprite component on the tweened gameobject, with the second you can provide it explicitly if you either tween on  a different gameobject that does not own the sprite, or if you simply have it at hand and want to save the additional lookup time. Call it like this:

  // shrink sprite to half size in one second
  LeanTween.scale(gameobject, new Vector2(0.5f, 0), 1f).setSprite();

  // pulse another sprite (fade in and out repeatedly) with a second frequency, but tied to the same tween gameobject  
  LeanTween.alpha(gameobject, 0, 1f).setSprite(anotherSprite).setLoopPingPong().setEase(LeanTweenType.easeInOutSine);

  // etc. ;-)

So as you can see, you can write very clean code even for complex tweens on your sprites. You can of course extend this helper extensions for other operations, but as far as I needed them up to now, these are the two that need special attention when used for sprite gameobjects.

And here is the beef:

using UnityEngine;
using System.Collections;

public static class LeanTweenExtension {

  // Extend LeanTween to operate on a tk2dsprite rather than the gameobject
  public static LTDescr setSprite(this LTDescr ltdescr, tk2dSprite sprite) {

    if (sprite == null) throw new UnityException("LeanTween.setSprite(): no sprite provided");

    switch (ltdescr.type) {

    case TweenAction.ALPHA:
      // set alpha directly on sprite color
      ltdescr.setFrom(new Vector3(sprite.color.a,0,0));
      ltdescr.setOnUpdate(delegate(float alpha, object tk2dspr) {
        Color col = ((tk2dSprite)tk2dspr).color;
        col.a = alpha;
        ((tk2dSprite)tk2dspr).color = col;
      }, sprite);
      break;

    case TweenAction.SCALE:
      // scale sprite only, not the game object!
      ltdescr.setFrom(sprite.scale);
      ltdescr.setOnUpdate(delegate(float scale, object tk2dspr) {
        ((tk2dSprite)tk2dspr).scale = new Vector3(scale, scale, sprite.scale.z);
      }, sprite);
      break;

    case TweenAction.SCALE_X:
      // scale sprite only, not the game object!
      ltdescr.setFrom(sprite.scale);
      ltdescr.setOnUpdate(delegate(float scale, object tk2dspr) {
        ((tk2dSprite)tk2dspr).scale = new Vector3(scale, sprite.scale.y, sprite.scale.z);
      }, sprite);
      break;

    case TweenAction.SCALE_Y:
      // scale sprite only, not the game object!
      ltdescr.setFrom(sprite.scale);
      ltdescr.setOnUpdate(delegate(float scale, object tk2dspr) {
        ((tk2dSprite)tk2dspr).scale = new Vector3(sprite.scale.x, scale, sprite.scale.z);
      }, sprite);
      break;

    case TweenAction.SCALE_Z:
      // scale sprite only, not the game object!
      ltdescr.setFrom(sprite.scale);
      ltdescr.setOnUpdate(delegate(float scale, object tk2dspr) {
        ((tk2dSprite)tk2dspr).scale = new Vector3(sprite.scale.x, sprite.scale.y, scale);
      }, sprite);
      break;

    default: throw new UnityException("LeanTween.setSprite(): action not yet supported " + ltdescr.type);
    }

    // make sure onUpdate is not set, otherwise object callback will not be called
    ltdescr.onUpdateFloat = null; 
    // change tween type to generic "value" callback
    ltdescr.type = TweenAction.CALLBACK;

    return ltdescr;
  }

  public static LTDescr setSprite(this LTDescr ltdescr) {
    if (ltdescr.trans == null) throw new UnityException("LeanTween.setSprite(): no gameobject set");
    tk2dSprite sprite = ltdescr.trans.GetComponent<tk2dSprite>();
    if (sprite == null) throw new UnityException("LeanTween.setSprite(): no sprite component found");
    return ltdescr.setSprite(sprite);
  }
}

Creating a button animation with UIToolkit (Unity)

If you played already Café International for iOS (and seriously: you should!), then you might have noticed a special user interaction feedback throughout the game: buttons and other touchable elements jump a bit up and come back with a nice spring effect, plus a little fancy glitter particle effect. It gives just another way of interaction feedback that I personally like very much (which is why I did it that way 😉 ).

I wanted to have that now also for something I am doing with Unity, and fortunately the very flexible free UIToolkit add-on makes it really very easy to implement the exact same behavior. Since such an element generally also behaves like a button, we simply derive a new class UIJumpingButton from UIButton. With such a modified button you can do everything you could also do with a normal button: add touch up/down/move events, attach a sound, etc. In addition, there is a new touch event OnTouched that will only fire once the animation is completed and the button is back in place. You can decide then whether you want to fire something straight on the first touch (via OnTouchDown) or rather when things are settled again.

To achieve the jump effect, we just add some code in the onTouchEnded handler. So if a user touches the button it get’s highlighted, but he can still move the finger outside the button and release without anything happening then. Just if he releases inside the button, it will fire. We then apply a chain of two animations: jumping up a bit by a certain percentage of the screen height, and then falling back to the previous position with a slight bounce. When all is done, we fire the OnTouched callback, if set. Furthermore, we don’t allow another touch animation while the ongoing animation isn’t completed yet. And that’s basically all the magic to it.

This is how you would use jumping buttons:

UIJumpingButton b1 = UIJumpingButton.create("start.png", "startSel.png", 0,0);
b1.positionFromTopLeft(0.5f, 0.33f, UIyAnchor.Center, UIxAnchor.Center);
b1.onTouched += ( button ) => Debug.Log("Start pressed");

UIJumpingButton b2 = UIJumpingButton.create("options.png", "optionsSel.png", 0,0);
b2.positionFromTopLeft(0.5f, 0.66f, UIyAnchor.Center, UIxAnchor.Center);
b2.onTouched += ( button ) => Debug.Log("Options pressed");

And this is a video of what you’ll get then:

I compiled this sample as a unity package for you to download and try it out, plus you can grab the UIJumpingButton.cs class file and the ButtonGlitter prefab for the particle push effect from this download. It does also contain the current UIToolkit runtime, so it should run right away in any Unity project. If you want to test it inside a project that already contains UIToolkit, please delete the UIToolkit folder from my sample.

To get it going, just import it into your current or a new unity project and load the included demo scene then.

You can change the glitter prefab to any other style you like. Just experiment with the settings or the particle image − it’s fun! Or just switch it off, it you don’t like it at all. When you want to test it in the editor, just pull the prefab into your scene, so you can easily have it emit with a simple mouseclick and watch your changes instantly. Make sure to design the particles at low-res (use a small editor window), because the settings are automatically scaled up for higher retina resolutions at runtime and are expected to be set for low-res in the prefab.

Using Bitmap Fonts for UIToolkit (Unity)

When you need to present text with UITookit for Unity − and for sure you’ll need that sooner or later − you have to use a bitmap font, so a fixed rendered image of all characters of a vector font (like a True Type font) you would normally use. First of all you need a tool to create such an image of a vector font. For Windows, there is a free tool from Angelcode, and for the Mac you can use the free Hiero. Both have it’s ups and downs, but hey − they are completely free. I did not get along too well with either of them, but maybe you have more patience to try them out.

If you are willing to pay a bit, then for the Mac there is the excellent but a bit more expensive Glyph Designer, and what I found lately and use now is the excellent and very easy to use BMGlyph tool (around $10), which I explain from here on.

Once you started BMGlyph, just select an already installed font or load a vector font explicitly by clicking the folder bottom right of font selector list. Now perform the following settings:

  1. Choose the font size to render. That should be the largest font size you plan to use, multiplied by four to create the largest resolution for iPad retina screens. So if your largest font is 24px, select 96 here. If you need a lot of much smaller font sizes also, you should consider to render an additional smaller font, because a small font will look better if rendered at that size, rather than just being scaled down.
    In the lower left character box, you can enter all characters you’ll need to support the different languages you need. You can preselect Basic Latin and Latin Supplement special characters (like German Umlaute), but you can also add them manually or edit the preselection.
    bmGlyph1
  2. bmGlyph3bmGlyph2Set the fill color to white, because otherwise you can’t change the color programmatically later on
  3. Try to not use fancy stuff like shadows, font outlines (stroke) or gradients, because they won’t scale well for lower resolutions or text sizes. So switch or leave off all the other settings if you don’t ned them badly. If you use them, check critically how your fonts look on all target platforms and in all final text sizes.
  4. Let the texture size be optimized through the Auto Size setting, because it will produce the smallest possible graphics, so we don’t waste too much space later on in our Texture Packer sprite sheet.
  5. Use Publish to start exporting the files we need. This is the hires texture rendering, plus three metadata files for the font information for each resolution. The first export setting called Default is already there. Set the target directory and name as you wish, and the suffix to 4x. Choose Unity as the format. Then select the Default entry and click the 50% and the 25% buttons on the lower left. For the 50% export, uncheck the texture export (only coordinates required) and set the suffix to 2x. For 25%, uncheck the texture also, and clear the extension.
    bmGlyph4
    bmGlyph5
    bmGlyph6
  6. Click Publish to export the hires image and three font files. The three font files need to go into a folder hierarchie below a Resource folder. For the image, remove the 4x extension again (I hope this won’t be necessary for future version of BMGlyph) and move it to your hires sprites for Texture Packer.
  7. Create a new set of spritesheets with Texture Packer, now containing also your bitmap font (I don’t explain using Texture Packer here, but it is explained on the UIToolkit page). It is important to have all your menu sprites and the font in one final texture to assure just one draw call for the GUI stuff, one of the major performance advantages of UIToolkit. Texture Packer will also create your 2x and lowres spritesheets also.
  8. To make your fonts (and other UIToolkit GUI sprites) look laser sharp and crystal clear, make sure to set all Unity spritesheet import settings to Texture Type: GUI and to Format: True Color (compressed will end up in very ugly, blurry artefacts).

Might look a bit confusing and like a lot to do, but the whole workflow is really easy, and once you did it for the first time, it is really a no-brainer.

Now all you need to do is load the font once with the UIText class, and with that you can create as many different UITextInstances as you like.

  // Some gameobject startup code
  void Start () {
    UIText fnt = new UIText("MarkerFelt", "MarkerFelt.png");
    UITextInstance text = fnt.addTextInstance("The quick brown fox", 20, 20);
    text.setColorForAllLetters(Color.yellow);
    UITextInstance text2 = fnt.addTextInstance("jumps over the lazy dog", 20, 60, 0.75f);
    text2.setColorForAllLetters(Color.red);
  }

Each text instance can have a different size by simply setting the textScale property (and better don’t scale it up − it’ll look ugly then if you use a valu larger than 1.0f). You can also change the color, and even each single letter inside of it can be set to a different color by providing a color array information. The right resolution will automagically be loaded from the spritesheets via the resolution extension mechanism (4x, 2x, -).

bitmapFont1

With these simple steps, you can now find your right font and use it in your Unity games. However, here is one final word of caution on free fonts: do read the licensing agreement of those fonts. Free fonts are rarely really free to use in games or other software. Usually they are only free to be used for producing printed or electronically published texts, sometimes also free to be used on websites. So make sure the license agreement explicitly lists usage in commercial software (like for the nice Junction font). And if specifically asked for, don’t forget the proper credits (like for the Stroke font). Missing to properly acknowledge the license type can get very expensive for you!

UIToolkit for Unity

I am playing around with Unity now for a while now, and  − as probably most of you sooner or later − needed to decide on how to do the good old plain and simple flat 2D UI stuff for menus etc. on top of fancy 3D graphics that you get out of the box with Unity. You can use of course the built-in GUI stuff, but you also probably heard that it’s not such a great idea if your target platform is mobile.

There are tons of frameworks for GUI around, ranging from free to paid, with partially all sorts of add-ons available and the more expensive ones (like NZGUI) probably the way to go if you are really doing high-end development. I, however, stumbled upon UIToolkit by Prime31, and I really found it great for my purposes. It is programmed in an extremely straightforward manner, with easy to understand concepts especially if you are coming from the Cocos2D world. It’s completely free, at the cost of a rather minimal documentation, though.

There is a lively community, but as usual around Unity, it mostly takes place in one endless thread in the Unity forum, and finding things or following a specific topic, or just checking if there are replies at all for a certain question, is not so great. But then, since all the source is available and has a very clean and easy to understand structure, you can almost always find what you need by just checking what happens inside of UIToolkit.

So for some of the solutions to things I wanted to achieve and that I found out either by code reading or by combining bits and pieces I wanted to start a small series of blogs to share those with you. Here you are:

CommentCast Review

One of the daily routines of developers and publishers is checking the AppStore for the latest ratings and reviews for their products. There are dozens of tools and websites around to support collecting that information, one of which is CommentCast for MAC OS X by Tyler Bindon. And it’s a bit different.

I found CommentCast in search of one tool that also takes ratings seriously. Most tools that I used before had a focus on reviews only. They collected them and notified me, made nice presentations of those new reviews coming up around the world…but they always ignored new ratings being awarded, i.e. reviews without a text, so to say. If you remembered the numbers from the past, you could do the math on your own, but most often you just missed it when new ratings where given somewhere around the world. With CommentCast, this won’t happen again!

Get Started

Selection

You start with selecting the products you want to monitor. This is not just software (iOS & MAC AppStore), but can be any media item from iTunes, like also music, films etc. You can enter a search term, an app ID or AppStore URL and will get a nicely ranked and tagged result list to choose what you want. That result is then added to the list of monitored products, which can be further categorized into subfolders, if you want.

With one click you can now read all worldwide reviews and ratings for a selected or simply all monitored products. When all stores around the world were checked, you get a comprehensive overview about the results in the main window of CommentCast.

Main

With some simple clicks you can now apply filters, e.g. just see values pertaining to the current version  of an app only, filter for number of stars, just show those added after the last poll or simply search within all of the shown. A green badge next to an entry in the app list shows you the number of changes since the last retrieval right away.

ReviewDeltaThe big value over other solutions is that CommentCast also considers simple ratings that don’t come with a review text (usually even the majority of feedback that you get). You get an excellent overview about how many stars you got since the last time you asked for it.

If you simply don’t get what one of your Japanese customers wrote, an integrated translation feature provides a rudimentary translation to a desired target language (Japanese to English translations are a fun entertainment on its own, BTW 😉 …)

On the top right of the main screen, a small worldmap indicates the distribution of reviews/ratings around the world. Clicking on it reveals a large view of the map, including an explanation of the color coding.

DistributionMap

Export

CommentCast allows you to export the filtered result list in a configurable HTML format, or simply as a CSV list for export into spreadsheets or other further processing. The HTML configuration is done directly in a template page with some dedicated tags for looping over the content and inserting individual result attributes.

Export

The export has to be done manually, since CommentCast does not yet support Apple script automation. Furthermore, you can not do programmatic filtering or conditional processing within that template, so the usecase for the export is limited. The developer is however looking into extending the functionality in theses areas. I found that feature handy anyway to provide a nice overview of selected reviews to others.

Conclusion

At $5 to be paid through PayPal, CommentCast is a bargain to buy and a must-have for every app developer. The additional consideration of ratings in addition to reviews is an outstanding feature that separates CommentCast from all it’s online and offline competitors. By now I couldn’t live without it any longer and use it at least twice a day! It will get the perfect final touch once the developer adds automation support.

You can download it from the CommentCast homepage.

Café International

The all-time classic board game Café International (Game of the Year 1989!) is finally coming to your iPhone, iPod and iPad. Enjoy an incredibly addictive gameplay, either alone in two challenging solitary modes, or as multiplayer game against friends and computer players. The unique funny original graphics (of course fully supporting iPad retina) and an awesome gameplay and handling will provide a great atmosphere and countless hours of fun and challenges.

Get it right here on the AppStore or watch the product trailer video:

SpriteSheets — The Movie

Initially I just wanted to provide original content in my blog and provide stuff that I couldn’t find elsewhere (or at least not the way I wanted or needed it), but this one is so cool that you should also be able to watch it if you browse my blog, courtesy of Andreas Loew of Code’n’Web, who provides the excellent and absolutely essential tool Texture Packer and Physics Editor (both of which I heavily use and recommend)!.

If you do for example Cocos2D programming, this is what you need to know about sprite sheets!

A Magnifying Glass for Cocos2D

UPDATED VERSION 12/5/2012

During development of our new iOS game, using Cocos2D, I needed a magnifying glass that you could move over the screen and enlarge the portions below it. I did something similar before with UIKit, and that wasn’t a big deal at all, however with OpenGL it is a different story. I experimented with CCLens3D effects, but wasn’t satisfied with it. Surfing around didn’t reveal much, except that there a) is a need, and b) no real complete solution around. At the end of this blog, you can download everything you need for a great magnifying glass Cocos2D solution!

Based on how to grab a screenshot to get the data that is currently shown (discussed e.g. here in the Cocos2D forum), I came up with a solution to simply take the UIKit technique I knew already: grab the data (in this case from the OpenGL buffer, but just the piece that I need), redraw it enlarged into an UIView (depending on a variable magnification scale) and then let that view hover on top of the OpenGL view. This solves at the same time the issue that any loupe graphics you use in Cocos2D would be part of the contents of the current OpenGL buffer and be enlarged as well, ending up with a funny mirror-in-mirror effect.

The UIView will be wrapped by a CCNode-derived class, so you can use it like other nodes within your scene. The solution is surprisingly lean and easy to use, does not eat much of your precious memory and is fast. In contrast to the well-known textedit loupe, I chose to not enlarge the area below your finger, but that below the center of the loupe. This turned out to be much more intuitive for my use-case, but of course you can do it differently by simply changing the grabbed rectangle coordinates.

The loupe will hover quite a bit above your finger, which is determined by setting the anchor position in the initialization. You can choose a different setting here, or make it even dynamic by adapting the anchor in setPosition (so the loupe will move around your finger when approaching the lower screen border — pretty nice effect).
For the loupe frame itself you can create whatever graphics you like. My example provides a very thin and simple rounded frame with a little 3D effect and shadow, but you can design whatever you like. For simplicity I chose a centered inner area, which makes rendering the magnified area very easy. If you need it, however, you can also go for a masked approach and mask out the inner part by providing an according masking image (and the required coding for it, of course. Ray Wenderlich provides some great samples for masking in his blog!).

Update 5/12/2012: For Cocos2D V2.0 it is no longer required to explicitly rotate the contents according to device orientation. The coding to download below  now checks the Cocos-Version and omits the rotation now automatically.

If you create your own loupe image with different proportions, you need to change the percentage of the inner part. You can compute that simply by dividing the diameter of the inner part by the total side-length of your image. Included you’ll find all required graphics for iPhone & iPad, ready & tested for standard and retina resolutions. I chose to have a relatively larger loupe for the iPhone/iPod, since magnification was even more required here than on the iPad, but it’s up to you what size of graphics you provide.

Once you imported the Magnifier class and the images into your project, you set the loupe up like that somewhere in your scene:

  Magnifier *magnifier = [[[Magnifier alloc] initWithMagnification: 2.0] autorelease];
  magnifier.position = <some position, e.g. touchLocation in ccTouchesBegan/Moved>;
  [self addChild:magnifier];

and there you go. Whenever you change the position, the magnified area will be updated as well automatically. If you want the magnified content to change dynamically also while you don’t move the loupe, you need to call the magnify method either periodically from a timer method, or at points in time where you know that content has changed. From my experience, though, having it updated on position changes is completely sufficient.

Update: Fix for iOS6 and above

There is nothing else to do for all iOS versions up to iOS 5.1. For newer versions of iOS in the future, however, you will need to tweak the way OpenGL handles graphics buffering, otherwise you will see a unicolor area only in the loupe. Fortunately, this is pretty easy to do: just replace this standard EAGLView initialization in your app delegate:

EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
	                       pixelFormat:kEAGLColorFormatRGBA8 // or whatever you use...
			       depthFormat:0];

with this one to make sure that OpenGL will retain it’s backbuffers:

EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
                               pixelFormat:kEAGLColorFormatRGBA8 // or whatever you use...
                               depthFormat:0
                        preserveBackbuffer:YES
                                sharegroup:nil
                             multiSampling:NO
                           numberOfSamples:0];

The Beef

The loupe graphics and Magnifier class ready for consumption can be downloaded here.

Prevent blurry fonts when using CCLabelTTF

Note: this in an updated blog (August 11th, 2012) to the original version, including a new download!

When using CCLabelTTF to use True Type fonts with Cocos2D, you might have noticed that they sometimes look pretty blurry, while in other places they appear reasonably sharp. What’s wrong here?

The rendering of the fonts tries to strictly consider the exact position of a font, which is given as a floating point number. If this position is not exactly hitting an actual pixel on the screen, the rendering is adjusted accordingly by introducing additional anti-aliasing, which makes the font appear to start exactly between two pixels. In theory. In reality, it simply looks blurry now, and I am sure in favor of a sharp font you would have rather adjusted a correct 0.3 pixel offset to the nearest pixel instead.

Fortunately, it is really not a big deal to do exactly this. We just need to derive a new class from CCLabelTTF and check (and possibly correct) the top left position each time it might change. Considering the complete node structure we are embedded in, we need to round the top left corner coordinates (in world/screen space) to a full pixel, and then recompute the position all the way back to our local coordinate system and fix the position parameter accordingly. Why the top left corner? Because the string is ultimately converted to a Cocos2D texture using the core NSString drawInRect method, which is based on the UIKit coordinate system and has its (0,0) coordinates always top left (rather than Cocos2D bottom left origin).
To make sure that rounding errors don’t sum up and make your label move uncontrolled, we always remember the originally intended position and adjust always referring to that position.

As a reward for reading this blog, you can simply download a ready-made class SharpLabelTTF right away! You can use it instead of CCLabelTTF and integrate it exactly like that. You don’t need to consider anything else.

Using CCLabelTTF:

Using SharpLabelTTF:

And here is the beef:

@implementation SharpLabelTTF
- (void) fixPosition {

    CGSize dim = self.texture.contentSize;    
    
    [super setPosition:intendedPosition_];
    if (scaleX_ < 0.3 || scaleY_ < 0.3) return;

    // compute world (= screen) coordinate of top left position of label  
    CGPoint worldSpaceTopleft = [self convertToWorldSpace: ccp(0,dim.height)];

    // align origin on a pixel boundary on screen coordinates
    worldSpaceTopleft.x = roundf(worldSpaceTopleft.x * CC_CONTENT_SCALE_FACTOR()) / CC_CONTENT_SCALE_FACTOR();
    worldSpaceTopleft.y = roundf(worldSpaceTopleft.y * CC_CONTENT_SCALE_FACTOR()) / CC_CONTENT_SCALE_FACTOR();

    // transform back into node space
    CGPoint nodeSpaceTopleft = [self convertToNodeSpace:worldSpaceTopleft];

    // adjust position by the computed delta
    CGPoint delta = ccpSub(nodeSpaceTopleft, ccp(0,dim.height));
    CGPoint newPos = ccpAdd(position_, delta);

    // finally set the position data
    [super setPosition:newPos];      
}

// capture modification calls to adjust position
- (void)onEnter {
    [self fixPosition];
    [super onEnter];
}

- (void)setParent:(CCNode *)parent {
    [super setParent:parent];
    [self fixPosition];
}

- (void)setString:(NSString *)str {
    [super setString:str];
    [self fixPosition];
}

- (void)setPosition:(CGPoint)position {
    intendedPosition_ = position;
    [self fixPosition];
}

-(void)setRotation:(float)rotation {
    [super setRotation:rotation];
    [self fixPosition];
}

@end