Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

1 decade ago by mkreitler

Hi all,

I'm porting my impact game (http://www.freegamersjournal.com/boxt) from vanilla impact to iOSimpact, and I'm running into the following problem.

I have a class called "UserFont" that extends the Font class. UserFont calls the Font constructor to load its texture, then overrides the widthMap, indices, and first character variables with parameters sent to the constructor. The game correctly tries to create my UserFont class and calls into the base class constructor. Once there, the call to glGenTextures() always returns 0, which prevents proper initialization of the JavaScript object.

Here is the declaration of JS_UserFont (in JS_UserFont.h):
@interface JS_UserFont : JS_Font {
}

Here is it's constructor (note that I'm not yet overriding the widthMap, indices, or first character, yet, but that's irrelevant because the [super init] call fails to create a valid JavaScript object):

- (id)initWithContext:(JSContextRef)ctx object:(JSObjectRef)obj argc:(size_t)argc argv:(const JSValueRef [])argv {
	if( self  = [super initWithContext:ctx object:obj argc:argc argv:argv] ) {
            // Override widthMap, indices, and firstChar here...
	}
	return self;
}

Here is how I invoke the object from my game.js module:
	numberFont:		 new native.UserFont('media/boxt/images/fat_font.png',
                                         [40, 22,  31,  33,  32,  32,  34,  30,  39, 37],
                                         [ 9, 64, 105, 154, 202, 250, 298, 350, 393, 448],
                                         48),

Tracing the code in the debugger, I see the UserFont getting created and calling into the base class constructor.

My understanding is that glGenTextures usually fails because it has an invalid graphics context. Am I somehow not passing the correct context to the base class constructor?

Any help is appreciated...

Thanks,

Mark

1 decade ago by mkreitler

Update:

Looking at the console log, I discovered that my fonts were being constructed before the graphics context was initialized. Moving the creation of the UserFonts into game.init() fixed this problem, but that has led me to another question.

Creating a font like this:
	systemFont:		 new ig.Font('media/04b03.font.png'),

seems to defer creation by caching the font request with the resource loader. Creating a font like this:

	font:			new native.UserFont('media/boxt/images/font_clean.png',
                                         [16, 13, 16, 17, 17, 17,  17,  16,  16,  16],
                                         [ 0, 18, 36, 54, 72, 89, 108, 126, 144, 162],
                                         48),

creates it immediately -- and before the graphics context has been initialized.

I assume this has to do with creating a native class vs a JavaScript class.

So, how does one add a new, non-native JavaScript class to iOSimpact when that class extends an existing engine class?

1 decade ago by mkreitler

OK...got it all figured out.

The full process for extending an existing impact class is too involved to detail here, but the main steps are:

1) Add user_font.js to the ios/plugins directory. This file is identical to font.js in the same folder, except for the native class it instantiates on load:
ig.module(
	'plugins.ios.user_font'
)
.requires(
	'game.graphics.user_font'
)
.defines(function(){


UserFont.inject({
	load: function( loadCallback ) {
		if( this.loaded ) {
			if( loadCallback ) {
				loadCallback( this.path, true );
			}
			return;
		}
		else if( !this.loaded && ig.ready ) {
			this.loadCallback = loadCallback || null;
			this.data = new native.UserFont( this.path, this.onload.bind(this), this.widthMap, this.indices, this.firstChar );
		}
		else {
			ig.addResource( this );
		}
		ig.Image.cache[this.path] = this;
	},

	onload: function( width, height ) {
		this.width = width;
		this.height = height;
		this.loaded = true;
		
		if( this.loadCallback ) {
			this.loadCallback( this.path, true );
		}
	},

	draw: function( text, x, y, align ) {
		if( !this.loaded ) { return; }
		ig.system.context.drawFont(this.data,text.toString(),x,y,(align||0));
	}
});


});

2) Add JS_UserFont.h and JS_UserFont.m to the XCode project. JS_UserFont extends JS_Font and handles parsing of extra arguments, which are then passed on to the UserFont class (defined in UserFont.h and UserFont.m).

JS_UserFont.m
#import "JS_UserFont.h"
#import "UserFont.h"

@implementation JS_UserFont

- (id)initWithContext:(JSContextRef)ctx object:(JSObjectRef)obj argc:(size_t)argc argv:(const JSValueRef [])argv {
	if( self  = [super initWithContext:ctx object:obj argc:argc argv:argv] ) {
		JSStringRef widthMapStr = JSValueToStringCopy(ctx, argv[2], NULL);
		JSStringRef indicesXstr = JSValueToStringCopy(ctx, argv[3], NULL);

        NSArray* newWidthMap = [self JSStringToArray1D:widthMapStr];
        NSArray* newIndicesX = [self JSStringToArray1D:indicesXstr];
        
        int newFirstChar = (int)(JSValueToNumber(ctx, argv[4], NULL) + 0.0001);
        UserFont* userFont = (UserFont*)texture;
        
        [userFont overrideWidthMap:newWidthMap];
        [userFont overrideIndicesX:newIndicesX];
        [userFont setFirstChar:newFirstChar];
	}
	return self;
}

- (id)JSStringToArray1D:(JSStringRef)stringRef {
    int length = JSStringGetLength(stringRef);
	const JSChar * jsc = JSStringGetCharactersPtr(stringRef);
    NSArray* arrayOut = nil;
    
    NSString* string = [[NSString alloc] initWithCharacters:jsc length:length];
    arrayOut = [string componentsSeparatedByCharactersInSet:[NSCharacterSet punctuationCharacterSet]];
    
    [string release];
    
    return arrayOut;
}

- (id)allocTexture {
    NSLog(@"Loading UserFont: %@", path );
    
    return [[UserFont alloc] initWithPath:[Impact pathForResource:path]];
}

@end

Note that, to make this work, I introduced the allocTexture method in the base JS_Font.m class, like so:

#import "JS_Font.h"


@implementation JS_Font

@synthesize texture;

- (id)initWithContext:(JSContextRef)ctx object:(JSObjectRef)obj argc:(size_t)argc argv:(const JSValueRef [])argv {
	if( self  = [super initWithContext:ctx object:obj argc:argc argv:argv] ) {
		path = [JSValueToNSString( ctx, argv[0] ) retain];
		texture = [self allocTexture];
		
		if( texture.textureId ) {
			JSObjectRef func = JSValueToObject(ctx, argv[1], NULL);
			JSValueRef params[] = {
				JSValueMakeNumber(ctx, texture.width),
				JSValueMakeNumber(ctx, texture.height)
			};
			[[Impact instance] invokeCallback:func thisObject:NULL argc:2 argv:params];
		}
	}
	return self;
}

- (id)allocTexture {
    NSLog(@"Loading Font: %@", path );
    
    return [[Font alloc] initWithPath:[Impact pathForResource:path]];
}

- (void)dealloc {
	[texture release];
	[path release];
	[super dealloc];
}

@end

3) Add the UserFont class in UserFont.h and UserFont.m. My user font class takes explicit arrays for width and index spacing along the x-axis, as well as a variable first character (which required changes in Font.m -- see above).

Here's UserFont.m:

#import "UserFont.h"


@implementation UserFont

// UserFont only overrides Font behaviors in JavaScript.
// The Objective C implementations are identical.
- (id)initWithPath:(NSString *)path {
    return [super initWithPath:path];
}

- (void)overrideWidthMap:(NSArray*)newMap {
    for (int i=0; i<[newMap count]; ++i) {
        NSString* strVal = (NSString*)[newMap objectAtIndex:i];
        widthMap[i] = [strVal intValue];
    }
}

- (void)overrideIndicesX:(NSArray*)newIndices {
    for (int i=0; i<[newIndices count]; ++i) {
        NSString* strVal = (NSString*)[newIndices objectAtIndex:i];
        indices2d[i][0] = [strVal intValue];
    }
}

- (void)setFirstChar:(int)newFirstChar {
    firstChar = newFirstChar;
}

@end

Note how JS_UserFont.m parses out the array as a string, which it then separates into an array of values. This array passes into UserFont, which extracts the intValue of each element and overwrites the widthMap and indeces2d[0] entries.

1 decade ago by yatayata

thanks for taking the time to document this.

can you give the reason for what you were doing that's unqiue?
I'm using impacts bitmap fonts in ios impact without any problems (of course canvas text doesnt work)

btw have you looked at the appMobi open source stuff? they have a lot of things implemented there that iOS impact doesn't, but in a similar way. the appmobi codebase is fairly huge, but cherry picking specific features you need to add to iOS impact seems a good approach (eg to enable ajax)

1 decade ago by mkreitler

@yatayata,

Thanks for the tip about appMobi. I haven't checked it out, but I will.

As for what I'm doing that's unique: my extension to the font class lets me define the kerning in a separate array and to set a non-standard starting character. With these features, I can do things like define a "numbers only" font whose characters have radically different spacings.

There's probably a more elegant way to do this -- something like use the default bitmap font class and then overwrite the starting character and spacing array values. But making the UserFont class was easy with vanilla -- it was just porting it to iOSimpact that took some time.
Page 1 of 1
« first « previous next › last »