iPhone Gaming Framework: Stage 2 Tutorial

NOTE:: I am still looking for a good way to turn this into a template and release it for anyone wishing to install this for the base of their game. If you know of a good resource for doing so, please let me know, as google has been fairly unhelpful as of this point.

Welcome back everyone! Did you all finish Stage 1 of the tutorial? If not, please do that, as we are going to be building off stage 1 in our next tutorial. Stage 1 can be found HERE

Okay, now that stage 1 is done, you have a working screen management system… except you don’t have an input manager to help you with input management! Remember my note from previous tutorials, there are MANY solutions to a problem, and this is just one of many. Our input class works as follows: If a touch is detected, the GLViewController will send a message to the input manager, and that message will be stored in an input “state” called “queryState.” Before the view controller updates any of the game screens, it will send a message to the input manager to update the input, and this is when the input is changed from the “queryState” to “currentState” and the current state is then set to the previous state.

So lets look at the code, yeah? First the InputManager.h file

//	
//	The input manager is a helper class that houses any input
//	that can be obtained by the game engine. As a touch is detected
//	on screen, it will send a message to the input helper. The input
//	helper will then hold onto that message and filter it into the 
//	current state on the next game loop. The current state is moved
//	to the previous state, and any incoming touches are then put
//	back into the query state, waiting to be updated.
//
//	This method of updating lets the game filter updates to the top-most
//	game screen and not have to worry about un-focused screens handling
//	input that was not intended for them.
//
//  Created by Craig Giles on 1/3/09.
//

#import <Foundation/Foundation.h>
#import "InputState.h"

@interface InputManager : NSObject 
{	
@private
	bool isLandscape;
	InputState *currentState;
	InputState *previousState;
	InputState *queryState;
}

@property (nonatomic, readwrite) bool isLandscape;
@property (nonatomic, readonly) InputState *currentState;
@property (nonatomic, readonly) InputState *previousState;

- (void) update:(float)deltaTime;

//
//	Touch events
//
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event InView:(UIView *)view  WithTimer:(NSTimer *)deltaTimer;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event InView:(UIView *)view  WithTimer:(NSTimer *)deltaTimer;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event InView:(UIView *)view  WithTimer:(NSTimer *)deltaTimer;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event InView:(UIView *)view WithTimer:(NSTimer *)deltaTimer;

//
//	Helper Methods
//
- (void) update:(float)deltaTime;
- (bool) isButtonPressed:(CGRect)rect;
- (bool) isButtonHeld:(CGRect)rect;
- (void) convertCoordinatesToLandscape;

//
//	Class methods
//
+ (bool) doesRectangle:(CGRect)rect ContainPoint:(CGPoint)point;

@end

And the .m file

//	
//	The input manager is a helper class that houses any input
//	that can be obtained by the game engine. As a touch is detected
//	on screen, it will send a message to the input helper. The input
//	helper will then hold onto that message and filter it into the 
//	current state on the next game loop. The current state is moved
//	to the previous state, and any incoming touches are then put
//	back into the query state, waiting to be updated.
//
//	This method of updating lets the game filter updates to the top-most
//	game screen and not have to worry about un-focused screens handling
//	input that was not intended for them.
//
//  Created by Craig Giles on 1/3/09.
//

#import "InputManager.h"


@implementation InputManager

//
//	Getters / Setters
//
@synthesize isLandscape;
@synthesize currentState;
@synthesize previousState;

//
//	Initialization
//
- (id) init
{
	self = [super init];
	if (self != nil) 
	{
		//
		//	Allocate memory for all of the possible states
		//
		currentState = [[InputState alloc] init];
		previousState = [[InputState alloc] init];
		queryState = [[InputState alloc] init];
		
		//
		//	Set the initial coords for the touch locations.
		//
		currentState.touchLocation = CGPointMake(0, 0);
		previousState.touchLocation = CGPointMake(0, 0);
		queryState.touchLocation = CGPointMake(0, 0);
	}
	return self;
}

//
//	Deallocation
//
- (void) dealloc
{
	[currentState release];
	[previousState release];
	[queryState release];
	[super dealloc];
}

//
//	Update the input manager. This method will take the
//	values in updateState and apply them to the current state. 
//	in essence, this method will "query" the current state
//
- (void) update:(float)deltaTime
{
	//	Sets previous state to current state
	previousState.isBeingTouched = currentState.isBeingTouched;
	previousState.touchLocation = currentState.touchLocation;
	
	//	Sets the current state to the query state
	currentState.isBeingTouched = queryState.isBeingTouched;
	currentState.touchLocation = queryState.touchLocation;
	
	//	converts the coordinate system if the game is in landscape mode
	[self convertCoordinatesToLandscape];
}
	
//
//	Touch events
//
//	These events are filtered into the input manager as they occur.
//	Since we want to control how our input, we are going to use a 
//	queryState as the "Live" state, while our current and previous
//	states are updated every loop. For this reason, we are always
//	modifying the queryState when these touch events are detected,
//	and the current state is only modified on [self update:deltaTime];
//
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event InView:(UIView *)view  WithTimer:(NSTimer *)deltaTimer
{		
	queryState.isBeingTouched = YES;
	queryState.touchLocation = [[touches anyObject] locationInView:view];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event InView:(UIView *)view  WithTimer:(NSTimer *)deltaTimer
{	
	queryState.isBeingTouched = YES;
	queryState.touchLocation = [[touches anyObject] locationInView:view];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event InView:(UIView *)view  WithTimer:(NSTimer *)deltaTimer
{	
	queryState.isBeingTouched = NO;
	queryState.touchLocation = [[touches anyObject] locationInView:view];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event InView:(UIView *)view WithTimer:(NSTimer *)deltaTimer
{
	queryState.isBeingTouched = NO;
}

//
//	When in landscape mode, the touch screen still records input
//	as if it were in portrait mode. To get around this, if we
//	are writing a game in landscape we need to adjust the coordinate
//	system on the touchscreen to match that of our world.
//
- (void) convertCoordinatesToLandscape
{
	//	If we are not in landscape mode, don't convert anything
	if ( !isLandscape )
		return;
	
	//	Otherwise, we will need to take the current touch location
	//	swap the x and y values (since in landscape, the portrait
	//	coordinate system means x will point up / down instead of
	//	left / right) and move the y position to match our origin 
	//	point of (0,0) in the upper left corner. 
	int x = currentState.touchLocation.y;
	int y = (currentState.touchLocation.x - 320);
	
	//	make sure we take the absolute value of y, since if we didn't
	//	y would always be a negative number.
	y = abs(y);
	
	//	Since we were converting the current state, we need to update
	//	the current touch location
	currentState.touchLocation = CGPointMake( x, y );
}

//
//	Looks at the previous state, and the current state to determine
//	weather or not the button (rectangle) was just pressed and released.
//
- (bool) isButtonPressed:(CGRect)rect
{
	//
	//	If the current state is not being touched
	//	and the previous state is being touched, the
	//	user just released their touch. This is 
	//	personal preference really, But i've found with
	//	my XNA programming, that its better to detect if
	//	a button was just released rather than if it was
	//	just pressed in when determining a button press.
	//	
	if ( !currentState.isBeingTouched &&
		 previousState.isBeingTouched )
	{
		
		return 	[InputManager doesRectangle:rect ContainPoint:currentState.touchLocation];
	}
	
	return NO;
}

//
//	Looks at the previous state, and the current state to determine
//	weather or not the button (rectangle) is being held down.
//
- (bool) isButtonHeld:(CGRect)rect
{
	//
	//	If the current state and the previous states
	//	are being touched, the user is holding their 
	//	touch on the screen.
	//	
	if ( currentState.isBeingTouched &&
		 previousState.isBeingTouched )
	{
		return 	[InputManager doesRectangle:rect ContainPoint:currentState.touchLocation];
	}
	
	return NO;
}

//
//	Helper method for determining if a rectangle contains a point.
//	Unsure if this will stay in the input helper or be moved to some
//	sort of "RectangleHelper" class in the future, but for now this
//	is a good spot for it. Remember, this works with our current coord
//	system. If your engine uses a different coord system (IE: (0,0) is at
//	the bottom left of the screen) you'll need to modify this method.
//
+ (bool) doesRectangle:(CGRect)rect ContainPoint:(CGPoint)point
{
	//
	//	If the rectangle contains the given point, return YES
	//	Otherwise, the point is outside the rectangle, return NO
	//	
	if (point.x > rect.origin.x &&
		point.x < rect.origin.x + rect.size.width &&
		point.y > rect.origin.y &&
		point.y < rect.origin.y + rect.size.height)
	{
		return YES;
	}
	
	return NO;
}

@end
&#91;/sourcecode&#93;

InputState just contains 2 values, and I have chosen to set them as the following:

&#91;sourcecode language="cpp"&#93;
#import <Foundation/Foundation.h>

@interface InputState : NSObject 
{
	bool isBeingTouched;
	CGPoint touchLocation;
}

@property (nonatomic, readwrite) bool isBeingTouched;
@property (nonatomic, readwrite) CGPoint touchLocation;

@end

//
//  Implementation of InputState
//
@implementation InputState

@synthesize isBeingTouched;
@synthesize touchLocation;

- (id) init
{
	self = [super init];
	if (self != nil) {
		isBeingTouched = NO;
		touchLocation = CGPointMake(0, 0);
	}
	return self;
}

@end

It should be a good time to point out, that the input manager changes the coordinates of the actual touch, depending on if you’re playing your game in landscape mode or normal mode. One of the problems I was having, before I realized what was really happening, was my coordinates for the touch screen and coordinates for the game world were different. For example, my touch screen coordinates, if held landscape, (0,0) was in the lower left corner, while in the world coordinates, (0,0) was in the upper left corner. This gave me problems, as you can imagine.

In order to change this, I wrote a method that will convert the coordinates system if the screen is detected in landscape mode. It should be pointed out, that if you’re using a different origin point for your world (IE: Lower left corner instead of upper left) you’re conversions will have to be done differently.

Another method that is currently in the input manager class, is the [self doesRectangle:rect ContainPoint:touchLocation]; Inside this method, you’ll see that both current and previous states are being used in order to detect if a touch is being “touched” or “held.” This can be done differently, but the way I’ve chosen to implement it, is as follows;

if the button was just RELEASED (current state is not pressed, but previous state was) than the button was just “pressed.”

This is important to note, because it means if the user touches the screen, and holds their finger down, it won’t register as a “button press” until they release their finger. Some people prefer to do the opposite, meaning if the current state was just pressed and the previous state was un-pressed. Use what you feel comfortable with.

So now all we need to do is add a game screen or two, right? Well, I told you that I would help you get up a paused screen. This will help you learn how to get any sort of game screen up and running. First, lets take a look at the PausedScreen.h file

//
//	The paused screen is just an overlay popup screen that is
//	displayed to the user when the game is paused.
//
//  Created by Craig Giles on 1/5/09.
//

#import <Foundation/Foundation.h>

#import "GameScreen.h"
#import "InputManager.h"
#import "Texture2D.h"

@interface PausedScreen : GameScreen 
{
	Texture2D *pausedText;
	double alphaValue;
}

@property (nonatomic, retain) Texture2D *pausedText;

- (void) loadContent;
- (void) unloadContent;

- (void) handleInput:(InputManager *)input;
- (void) update:(float)deltaTime	OtherScreenHasFocus:(bool)otherFocus	CoveredByOtherScreen:(bool)coveredByOtherScreen;
- (void) draw:(float)deltaTime;

@end

If you notice, several of these methods are in “GameScreen.h” right? You’ll also notice the LACK of touch event classes. All input is handled by the input manager, and will be called from the view controller (the [self handleInput:input] method) Aah now you are getting why I went the touch manager route instead of re-writing every single “touchEvent” method in each game screen!

There are several methods that every game screen needs in order to function properly. It will need a loadContent / unloadContent methods, handleInput method, and the update / draw methods. Also keep in mind, every game screen will need to call the [super update:deltaTime…], and every screen (other than the paused screen, which is a popup screen) will need to call the [super draw:deltaTime]; within its draw method.

So now you have the basics of the game screen, let me show you the .m file.

//
//	The paused screen is just an overlay popup screen that is
//	displayed to the user when the game is paused.
//
//  Created by Craig Giles on 1/5/09.
//

#import "PausedScreen.h"


@implementation PausedScreen

@synthesize pausedText;

//
//	Initialize the pause menu screen
//
- (id) init
{
	self = [super init];
	if (self != nil) 
	{
		//	flag that there is no need for the game to transition
		//	off when the pause menu is on top of it
		self.isPopup = YES;
		
	}
	return self;
}

//
//	Load all content associated with the paused screen
//
- (void) loadContent
{
	//
	//	Since this is a popup screen, we will over-ride the 
	//	view controllers transition time and set this to instantly
	//	transition on and off.
	//
	self.transitionOnTime = 0;
	self.transitionOffTime = 0;
	
	//	set the paused text
	pausedText = [[Texture2D alloc] initWithString:@"Paused\nTap screen to unpause"
										dimensions:CGSizeMake(self.viewport.size.width, self.viewport.size.height)
										 alignment:UITextAlignmentCenter
										  fontName:@"Zapfino"
										  fontSize:32];
	
	//	The alpha value of the background. below 
	//	the paused text.
	alphaValue = 0.75f;
}

- (void) unloadContent
{
	//TODO: all unload content goes here
	[pausedText release];
	[super unloadContent];
}


- (void) handleInput:(InputManager *)input
{
	//	If the 'tap here to resume' button was pressed
	//	resume game
	if ([input isButtonPressed:self.viewport])
	{
		[self exitScreen];
	}
}

- (void) update:(float)deltaTime	OtherScreenHasFocus:(bool)otherFocus	CoveredByOtherScreen:(bool)coveredByOtherScreen
{
	//TODO: Update logic goes here
	
	//	Update the base class
	[super update:deltaTime OtherScreenHasFocus:otherFocus CoveredByOtherScreen:coveredByOtherScreen];
}

- (void) draw:(float)deltaTime
{	
	//	Darken the screen to alert the player that it has been paused
	[self.controller fadeBackBufferToBlack:alphaValue];
	
	[pausedText drawInRect:CGRectMake(self.viewport.size.width / 4, 
									  self.viewport.size.height / 4,
									  self.viewport.size.width / 2,
									  self.viewport.size.height / 2)];
}
@end

One thing to take note, since the PausedScreen is a popup screen, it has overwritten the transition on and transition off values sent to it by the view controller. Any game screen can do the same, for example if you are transitioning to a screen that you’ve made your own custom transition, turn the view controllers transition off and let the screen transition itself. (IE: a battle screen.)

So there you have it! I don’t remember if textures are drawing upside down with this coord system, but if they are, all you have to do is edit the Texture2D.m file to fix that. If you don’t know how, send me an email and I’ll show you how I did it, but there are several ways of doing it.

I hope you enjoyed my heavily commented code for the screen manager, input manager, and game screen classes. This should give you the basic knowledge to get up and running with your own iPhone OpenGL applications. If you found these useful, let me know and I’ll write up some more when I have the time. I’m still learning all of this myself, but sharing my knowledge will lead to more interesting games for everyone! Keep in mind these are very basic systems and can be heavily expanded on by creative programmers. Can’t wait to see what you guys come up with!

Happy coding all!

37 comments so far

  1. Andrew Wilson on

    Hi Craig

    Thanks a bunch for those two tutorials, this is really going to help when I get my teeth into the iPhone. Currently i’m finishing up an xna project and simultaniously reading through a book on iPhone development(oddly enough it’s the one you reccomended a few posts back)(boy is it different to c#) but after I get both of then done then I’m going to start with the iphone stuff straight away, and this will come in very useful. While I’m here, do you know of any good tutorials for OpenGLes, especially 2D stuff? I’m mainly looking for basic moving/animating sprites including rotating scaling etc, or is this covered in the book.

    Anyway, thanks again for the tutorials.

  2. Craig on

    Good book eh? It teaches you the basics, but isn’t really meant for games or anything. If you look at the sections talking about CoreAnimation, Quartz, or OpenGL, you’ll notice they’re kinda quick.

    Learning OpenGL es was tough for me… the initial learning curve is pretty high. I would suggest starting with the nehe tutorials at gamedev.net, and search Jeff’s blog for his “converting Nehe tutorials to the iPhone” since OpenGL != OpenGL ES in a few ways.

    The commands you’ll want to get familiar with is glOrthof (if you look in the screen manager setup, its there.. that sets up your screen for you) which will use a 2d projection. glRotatef(value, x, y, z) will rotate an object by value on the x, y, or z plane. I have not played around with scale yet, but there is a command for that too.

    If you have any specific questions when you get into the iPhone, let me know and i’ll see if i can dig up some info. Trying to learn OpenGL was very hard, but once it “clicked” enogh to where I could write a small game (which I am in the process now) … its glorious :)

  3. Andrew Wilson on

    thanks for the info, i’m sure it’ll come in useful.

  4. Sam Green on

    Thanks for the writeups!

    Found the following sites for Template info:

    http://www.macresearch.org/custom_xcode_templates
    http://briksoftware.com/blog/?p=28
    http://guides.macrumors.com/Creating_Xcode_project_templates

    Hopefully one of those will help you. :]

    • Craig on

      Thanks Sam, I’ll look into those and see if any helps! If i can get it up and running (probably sometime after this weekend) i’ll release the base screen manager / input manager / etc as a template for anyone wishing to start with it. Could also make your own tweaks to it (obviously since the input manager is very basic at this point, compared to what you could do with it)

  5. Josh on

    Hey

    I sent you an email and I don’t know if you got it. I’m really looking forward to this being worked on further. I agree that it probably won’t be the best, at least in the beginning, but I’ve been having a lot of problems finding help for game-specific iPhone programming. I tried using cocos2d, sio2, and also oolong. These three have been heavily recommended, but I couldn’t get my head around them. I’m really looking forward to seeing what you do with this project.

    I have to mention as well, you said that you’re really heavy on commenting code. That’s great, the libraries I mentioned above don’t contain enough of it. I would deeply suggest you continue this even if you do decide not to. It really helps.

    Thanks Craig

    • Craig on

      My work and class schedule has gotten me a little bit behind on emails.. I have read them all but have not yet been able to reply. I, like you, also checked out Cocos2D, SIO2, and Oolong… and they are powerful for what they do, but hell if I knew how to use them :) although Cocos would be the easiest to learn, since it has a very active community.

      I hope the screen / input / gamescreen classes in my first two tutorials were helpful for you. Were you able to get anything up and running? I was sent a few links to some template information, so I’ll try to put the workings of a “Base Game Framework” in a template for anyone wanting to use or expand on whats been talked about here.

      Commenting code has been a great thing for me.. Obviously the comments on the screen manager were excessive, but when I code its not far from what you’ll find in my private code as well. If I don’t get a chance to work on a project for a few weeks, I want to know everything that the code does (including my thought process as to why it does it) I remember my first RPG project for Dream Build Play… I went back to look at that engine and was surprised that… I didn’t know what anything did! Never again! From now on, all of my code will be fully commented :) and I always recommend the same for everyone.. it will help you out in the long run, guaranteed!

  6. Josh on

    Thanks for the quick reply.

    I totally understand you being busy. I’m actually off this week from work and I really want to get the backbone of my game built.

    I wasn’t able to do too much with it because I guess you never had us write a TitleScreen.m/h file structure, which caused some errors. Although it seems like those are the only issues.

    I do have a question, I don’t know a lot of OpenGL. Will I need to know a certain amount of it to do what you’re trying to do here?

    Thanks again

    • Craig on

      Any game screen should be pretty easy.. just need to make sure each gamescreen implements some features (IE: All game screens need to inherit from the GameScreen.m class, and should also call the super’s update and draw methods.)

      Here are the methods in my TitleScreen.h , they are generally the ones you’ll use for most of your game screens.

      – (void) loadContent;
      – (void) unloadContent;

      – (void) handleInput:(InputManager *)input;
      – (void) update:(float)deltaTime OtherScreenHasFocus:(bool)otherFocus CoveredByOtherScreen:(bool)coveredByOtherScreen;
      – (void) draw:(float)deltaTime;

      If you need help populating those methods, let me know… although it shouldn’t be too difficult. As for OpenGL? You don’t need to know a “lot” of it, but you should udnerstand what its doing. If you’re doing 2d games, you’ll just want to make a texture2d instance, (IE Texture2D *sprite;) and you’ll do mostly [sprite drawInRect:spriteBoxGoesHere];

      But as always, if you run into trouble.. just post here. I found a few bugs in the screen manager that I’ll address if i can get a template up and running… nothing major, things like not needing to [screen retain] and [screen release] in the add and release methods (apparently adding an instance to an NSMutableArray retains it autmatocially… which shows just how much of a mac programmer i am :D )

  7. Josh on

    Thanks for the quick replies.

    That’s good to hear, because most people are telling me to use OpenGL. As per their instructions, I also have the sample code for Touch Fighter and CrashLanding, but they haven’t helped as much. Although CrashLanding should help me a little bit.

    I could possibly need help with that. I’m relatively new to programming; don’t let that mistake me for a complete beginner. I know the background of C in general and I have enough friends to guide me through harder problems.

    I seriously think that as soon as you get that template ready, I’ll be very excited (no pressure :]). I have to hand it to you, you seem very experienced with this type of thing so I’m happy that I’ll have the chance to see what you make of this.

    That’s the one issue though, basically I’m a slow learner, if you show me code that hasn’t really been commented on, or it’s just very plain, I’m not able to learn anything from it.

    But thanks a lot.

  8. mc on

    This is good stuff Craig. I’m working my way through this tutorial and am learning alot. However, I was wondering how to import 3ds max files into an iphone app. When googling it, it didn’t seem like there is a ‘best’ solution.

    • Craig on

      I don’t know the best solution either, its not a problem I’ve had to solve yet :( I would look into Jeff’s blog at http://iphonedevelopment.blogspot.com/ as I know he was working on an object importer.. although I do not know the status of his project.

  9. Paul on

    This demo is very helpful, but I am having a little trouble getting it up and running, and although I have been programming for decades now, programming for the Mac and the iPhone is new to me (like everyone else it seems).

    For starters, for whatever the reason I get warnings about the locations of the frameworks when I run. It all compiles, but I get output in the console that it is looking for UIKit, OpenGLES, and CoreGraphics in /System/Frameworks rather than in /Developer/Platforms tree. I am not sure this is causing a problem or not.

    Also, whenever I run I crash with a runtime error “*** -[GLViewController drawView:]: unrecognized selector sent to instance 0x525df0”, which seems to happen right after it exits the application delegate class applicationDidFinishLaunching function.

    Any ideas?

    • Craig on

      Are you able to compile and run the basic framework given by Jeff from iphonedevelopment?
      If you look at your frameworks folder in xcode, does it include the CoreGraphics.framework / OpenGLES / UIKit / QuartzCore / Foundation.frameworks ?

      I’ve never received that runtime error before.. although it sounds like a framework isn’t hooked up right. I’d make sure you can compile Jeff’s framework first, and if you can, then build on top of that.

      If you’re using the OpenGL application framework provided by apple, you can still do this… but you’ll be doing somewhat different steps to get it working. I just used Jeff’s template because it was easier for me.

  10. Paul on

    I can compile and run the template. It does not do anything other than give me a blank screen but there are no errors in the console.

    When I run it with your modifications, I get the above error on the line:

    [controller drawView:self];

    in GLView drawView function.

    • Craig on

      I just posted an amendment to the tutorial series on the front page, should help with your problem. There is no drawView method with those parameters in our controller, we needed to adjust that to call the correct draw and update methods for our controller. There are a few other things in the amendment as well, should take a look at it :)

  11. Jonathan on

    Hi,

    Thanks for this very interesting tutorial, i have managed to get everything sorted however i have one problem. When i compile i get the following error, have you got any idea what is going wrong with this, i am guessing their is some sort of duplicate reference but i just dont understand whats wrong.

    Thanks for any help

    cd “/Users/badwolf/In Development/iGame”
    setenv MACOSX_DEPLOYMENT_TARGET 10.5
    setenv PATH “/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin”
    /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/gcc-4.0 -arch i386 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk “-L/Users/badwolf/In Development/iGame/build/Debug-iphonesimulator” “-F/Users/badwolf/In Development/iGame/build/Debug-iphonesimulator” -filelist “/Users/badwolf/In Development/iGame/build/iGame.build/Debug-iphonesimulator/iGame.build/Objects-normal/i386/iGame.LinkFileList” -mmacosx-version-min=10.5 -framework Foundation -framework UIKit -framework CoreGraphics -framework OpenGLES -framework QuartzCore -o “/Users/badwolf/In Development/iGame/build/Debug-iphonesimulator/iGame.app/iGame”
    ld: duplicate symbol .objc_class_name_InputState in /Users/badwolf/In Development/iGame/build/iGame.build/Debug-iphonesimulator/iGame.build/Objects-normal/i386/GLViewController.o and /Users/badwolf/In Development/iGame/build/iGame.build/Debug-iphonesimulator/iGame.build/Objects-normal/i386/iGameAppDelegate.o
    collect2: ld returned 1 exit status

    • Craig on

      I’m not quite sure, if its a code error i could spot it but I’m really bad at errors like this. It doesn’t even compile correct? I would try it one step at a time.. first get the framework from Jeff’s blog, then compile… then add a few modifications and Compile… see what is breaking for you.

  12. Eskema on

    Awesome tutorials, but for now, i cant understand how to manage different windows, i dont see any function to swap beetwen windows, how can i change from menu windows to options windows,etc,etc?
    You call exitscreen to kill actual screen, but how to change to another?
    thanks :)

    • Craig on

      Think of this system as a stack like system. You can see and interact with the top most object on the stack, but in order to dig deeper and interact with the 2nd object, you need to pop the top object off the stack.

      That being said, this system wasn’t built as a stack for the reason you mentioned… its possible to modify it to make a screen a ‘focal point’ of input so you can interact with any screen that is set as the focal point. How you would do that is up to you, i can think of a few ways off the top of my head that would take advantage of the multi-touch interface of hte iPhone (such as 2 to 3 finger swipes that could swap between screens)

      I would suggest draw out what you want to do on paper, and find a way to do it in code. If you want to interact with one screen at once, how do you make that screen the focus of your program? What if you want to change the focus from one screen to another? Its doable, and could be quite interesting in the right developers hands.

  13. Eskema on

    I asked you because i dont understand your stack system, i never used anything like that. I always use a simple statemanager in my main file.
    So, while statemanager->isrunning == true{
    switch states->getstate(){
    case state->menu: loadmenu(), etc, etc.
    But as i said i dont understand this method how it works, because i dont see any method, or value to select wich screen i wanna draw.
    I keep looking your source trying to understand how it works :)

  14. Craig on

    That is one way to do it, and I did that for one of my early projects.. but I found it to be too cumbersome :(

    Think of the screen manager like this; You are in the MainGame of your program, and you open up an InventoryScreen. There are now 2 screens on the stack;

    InventoryScreen
    MainGame

    Lets say in that inventory screen, you select an item that you want to equip… a message pops up saying “Do you wish to equip this item? Y/N” That msg is its own screen called MessageBoxScreen. You now have 3 windows;

    (notice new windows get added to the top)
    MessageBoxScreen
    InventoryScreen
    MainGameScreen

    The way the screen manager handles this, is the following. In your view controllers update function, it will go through the stack from the top to the bottom and update all the screens logic (animations, timers, etc) and give the TOP MOST SCREEN the opportunity to handle input (so message box will work with any input, but inventory screen and MainGame will NOT… but those two will still update their logic)

    With me so far? This makes it so if you’re game uses something like a “tap to move” function, and you tap the screen saying “Yes, get rid of this message box” your character won’t move to that location too :) But when you have all of your inventory screens open and all that, your game still updates itself like animations and timers and what not… because what if your screen doesn’t take up the entire menu? Do you really want your game to pause itself?

    Okay so thats the update function.. but what about the draw function? This one is quite easy if you look at the code… because it will just go through the entire stack bottom to top (not top to bottom) drawing each screen. The reason it goes bottom to top is like an artist, drawing the base first, then anything on top of it later. If you drew the message box first, then you drew the main game after it, the message box would be hidden behind the main game.

    Thats the basics… make sense? So the short answer to your question; all game screens will get drawn, and all game screens will get updated.. but if they’re the top-most screen they’re given the chance to update input.

  15. Eskema on

    Thanks a lot, another question if dont mind…. I have the mainscreen, now if i tap the screen i load another screen, but i dont delete the actual screen, so new screen should be added to screenmanager. Now if i tap the new screen with [self exitscreen], i should go back to my mainscreen, but the app delete the actual screen and crash, instead of return to my mainscreen, it seems like the mainscreen doesnt exist anymore. maybe im doing something wrong, any point?

  16. Cooslick on

    I was getting the same error as Jonathan and manged to overcome this by spliting the InputState.h file into a .h and .m file.

    Great tutorial Craig, i’ve been looking for something like this for a few weeks now. I’ve managed to get the framework up and running and have a basic screen. Hope to now begin developing on this further.

    Thanks!

    • Craig on

      @Cooslick
      Awesome to hear man. Good luck on your projects! Keep me informed with what you come up with :)

      @Eskema
      I would have to see the code Eskema. My suggestion is to set a brakepoint and trace the code, see what its doing. Maybe its getting released unexpectedly? I would have to know more information.

  17. Eskema on

    Actually i get 3 warnings in gamescreen.m
    -removescreen (twice) and fadetoblack.. are giving me a warning, telling that those methods doesnt exit.
    So when i use [self exitscreen] i get the “EXC_BAD_INSTRUCTION” error. I think the screen isnt released properly.
    Maybe you can show us how to move beetween 2 screens, so that way we can figure out what are we doing bad.
    Thanks :)

  18. Craig on

    To add a screen, you do what was done in the screen manager; create the screen, and then call the [controller addScreen:screen];

    if you’re calling [self exitScreen]; you should be inside the screen, since you’re telling the screen “hey, i want to die now” .. is this correct?

    again i can’t determine what is going on with just a few code samples. I would suggest running a break point at various points and really figure out what is going on. It will help you long term too, since you’ll understand the controller much better and be able to debug things that crop up.

  19. Eskema on

    Ok here we go, in glviewcontroller i have my titlescren call. So the app starts and show that screen :) , now i created my titlescreen.m and in there, in handleinput function i put the same call to create another window
    InGame *InGameScreen = [[InGame alloc] init];
    [controller addScreen:InGameScreen];
    [InGameScreen release];

    Now i created a new window and when i tap the titlescreen, my ingame screen shows, so i assume that i have 2 screens right now:
    ingame screen
    title screen
    right?
    Now ingame screen adds the pausedscreen, again the same call in handleinput
    PausedScreen *InPauseScreen = [[PausedScreen alloc] init];
    [controller addScreen:InPausedScreen];
    [InPausedScreen release];

    Actually im in the ingame screen, so i tap the screen and the pausescreen popsup, again if code its correct i have 3 screens

    pausescreen
    ingamescreen
    titlescreen

    Now if i tap the pausescreen, it will dissapear and return to ingame screen, but the app closes and get an exception in [self exitscreen].

    Here i am stuck with this, the breakpoints tells that the problem its in exitscreen, but you show us the code works, so im unable to find out any point.

  20. Eskema on

    Ohh crap, nevermind Craig, i feel really stupid, i found my error. Im releasing the screen just after i add it to screens, so obviously the screen doesnt exits.
    Thanks for all your help :)

  21. Eric on

    Eskema you may have hit the same bug I encountered. Your release call after adding the screen is correct because the screen is being retained by the controller array. The bug is that if you don’t have a memory leak (extra retain or no release) then you will likely crash in the releaseScreen code. And that is because the screen is removed from the screensToUpdate array after being removed from the screens array. Problem there is it will autodestruct after being removed from screens if it’s not in the screensToUpdate array. However, looking at the code it should never be in the screensToUpdate without being in the screens array, so my fix is just to reorder the removeObject calls:

    //
    // Release all game screens from memory, that have had their content
    // unloaded. This will release all screens themselves, as well as remove
    // them from the screens arrays.
    //
    – (void) releaseScreen:(GameScreen *)screen
    {
    // remove the screen from all screen arrays
    [screensToUpdate removeObject:screen];
    [screens removeObject:screen];
    }

    • Craig on

      I actually found a few bugs in regards to that… chalk it up to not knowing Obj-C well enough before putting together the project eh? :) Glad to hear you got it sorted out though Eric.

  22. Josh on

    Is the framework/project template itself coming together? It’ll be interesting to see what you can make of it Craig, and then use it.

    • Craig on

      I’ve actually been spending more time in C++ than Obj-C.. mostly because I taught myself C# when I started to program, then moved to Obj-C with the iPhone SDK, but now my classes are heavily focusing on C++ (instead of Java, which I’m thankful for.. i am not a fan of Java)

      I am working on a project I’ve dubbed the “djinn engine” that will be a good C++ engine for the iphone.. but my current class schedule is pretty brutal :( I’m still looking into setting up a template, but if anyone knows how to do it, I can just as easily just pass the code over. I’m sure there needs to be some tweaks in the code anyway (bug fixes and such)

  23. Jeff on

    I have gone through the first 2 tutorials but i believe i am missing the Texture2D class and implementation. Is this available somewhere?

    thanks
    jeff

    • Craig on

      I got it from the “CrashLanding” app that was in a previous version of the SDK.. you can still download it if you look in your SDK documentation (i forget which version it was in though)

      if you can’t find it, i’ll try to host it on code.google.com for ya. I have a few versions, one that I converted to C++ code, and one that I changed the coordinate system around to work with my coordinates (with 0,0 being upper left corner)

  24. CK on

    Hi Craig,

    Thanks for the tutorial. It is awesome.

    I am new to programming so am a bit confused about the titlescreen.h/m. Do you have an example?

    • Craig on

      The title screen (or any game screen you create for that matter) is a derived class from GameScreen (which is the base class) so what you will be doing is building on top of what is already established with the GameClass screen. Most likely, you will be overriding the following functions:

      – (void) loadContent;
      – (void) unloadContent;

      – (void) handleInput:(InputManager *)input;
      – (void) update:(float)deltaTime OtherScreenHasFocus:(bool)otherFocus CoveredByOtherScreen:(bool)coveredByOtherScreen;
      – (void) draw:(float)deltaTime;

      And from there, you can put in your own code for what the title screen or anything does. If you look at the paused screen interface it should give you a better idea on what you can do.. since the paused screen is just a derived class from gamescreen as well.


Leave a comment