iPhone SDK: Learning About Touch Events & Basic Game Animation

iPhone SDK: Learning About Touch Events & Basic Game Animation

Tutorial Details
  • Technology: iPhone SDK 3.2
  • Difficulty: Intermediate
  • Completion Time: 30 - 45 Minutes

One of most common actions used for an iPhone or iPad application is the touch event. An event is not useful if it doesn’t trigger a meaningful action. This tutorial covers how to attach a touch event to a custom button and how to trigger the animation of an object. In the end, the result is a simple on-screen game controller, which can be extended to the limits of your imagination.

Step 1: Create a New Project

Open Xcode and start a new project. Select View-Based Application. Name the app “jumping,” so you can follow along easily.

Step 2: Import the Project Resources

After downloading the attached project file, open the “Images” folder. Here you will find all the image resources we need for this project, including three sprite images of Ryu from Streetfighter as created by PanelMonkey. You will need to copy the images into the project resource folder by dragging them from the folder in Finder and into the “Groups & Files” pane in Xcode.

In the Xcode prompt that will appear, be sure to check the box labeled “Copy items into destination group’s folder (if needed).”

All the images needed for the project should now be copied to the same folder where the project file is. Now, to keep the resource folder clean, let’s group all of the images we just imported together. Select all the images by holding down command and clicking on each file. Now left-click or ctrl + click and select “Group” from the resulting menu. Name the group whatever you wish. I named mine “images.”

Build and run the app in the current state. There should be no compiling errors and the simulator should show a plain, grey screen.

Step 3: Modify jumpingAppDelegate.m

Inside jumpingAppDelegate.m, modify the didFinishLaunchingWithOptions method by adding the following line:

// Allocate a new view, add this line:
self.viewController = [jumpingViewController alloc];
[window addSubview:viewController.view];

This will allocate a new view controller. This step is necessary because we won’t be using Interface Builder to create our view for us.

Step 4: Modify jumpingViewController.h

In the view controller header file (jumpingViewController.h), inside the interface, declare a new property by adding:

UIImageView *player;

Then, before @end, add:

@property (nonatomic, retain) UIImageView *player;

This will allow us to use “player” as a class property for an image.

The header file should now look like this:

#import <UIKit/UIKit.h>
@interface jumpingViewController : UIViewController {
	UIImageView *player;
}
@property (nonatomic, retain) UIImageView *player;
@end

Step 5: Modify jumpingViewController.m

Our next step is to add the graphical and interface elements to this view.

Open the jumpingViewController.m file and delete the existing commented methods. Leave the ones that are not commented.

At the top, after @implementation jumpingViewController, add:

@synthesize player;

Update the dealloc method to the following:

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

Next, insert the following method:

- (void)addButton {
    UIButton *button = [[UIButton buttonWithType:UIButtonTypeCustom]
                                         initWithFrame:CGRectMake(240, 150, 50, 50)];
    // Set the button's image
    [button setBackgroundImage:[UIImage imageNamed:@"button.png"]
                 forState:UIControlStateNormal];
    // Attach an event
    [button addTarget:self action:@selector(buttonPressed)
                 forControlEvents:UIControlEventTouchUpInside];
    // Add the button to the view
    [self.view addSubview:button];
}

The addButton method will be called later in order to, you guessed it, add the button to the view. The first thing to notice is that this is a custom button. Secondly, buttonPressed is the name of a method that will be called when the touch event is fired.

Go ahead and define a temporary buttonPressed method by inserting the following:

- (void)buttonPressed {
	NSLog(@"Button pressed");
}

NSLog() will send a message to the console, which can be accessed from the menu, under Run (cmd + shift + R).

Our new button should appear on screen after the view has loaded. In order for this to occur, add the following method:

- (void)loadView {
    // Allocate the view
    self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    // Set the view's background color
    self.view.backgroundColor = [UIColor blackColor];
    // Create the background image
    UIImageView *bg = [[UIImageView alloc] initWithFrame:CGRectMake(0 , 0, 572, 206)];
    [bg setImage:[UIImage imageNamed:@"bg.jpg"]];
    [self.view addSubview:bg];
    // Create the button
    [self addButton];
}

This method comes by default for view controllers. We’re adding a background image and sending the addButton message.

Save your work and then build and run the project. You should see the background image and the red button. If the console window is open, touching the button should display a message in the console via NSLog().

Step 6: Adding a Character to the Screen

In order to initialize our UIImageView object, add the following in jumpingViewController.m, above addButton method:

- (void)initPlayer {
	self.player = [[UIImageView alloc] initWithFrame:
				   CGRectMake(10, 100, 77.0, 94.0)];
	[self normalStance];
	// opaque for better performance
	self.player.opaque = YES;
	[self.view addSubview:self.player];
}
- (void)normalStance {
	[self.player setImage:[UIImage imageNamed:@"ryu.png"]];
}

This code will both initialize a UIImageView object and add it to the main view. The normalStance method will turn out to be useful later.

We will now send the initPlayer message to the main view. We do this by modifying the loadView method as follows:

- (void)loadView {
    // Allocate the view
    self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    // Set the view's background color
    self.view.backgroundColor = [UIColor blackColor];
    // create the background image
    UIImageView *bg = [[UIImageView alloc] initWithFrame:CGRectMake(0 , 0, 572, 206)];
    [bg setImage:[UIImage imageNamed:@"bg.jpg"]];
    [self.view addSubview:bg];
    //create the button
    [self addButton];
    // now initialize the player
    [self initPlayer];
}

Build and run. A character appeared on the screen, right?

Step 7: Making Our Character Jump

Now for the fun part. Above addButton, add:

- (void)cleanStance {
    [self.player setImage:nil];
    self.player.animationImages = nil;
}
-(void)jumpStance {
    [self cleanStance];
    NSArray *imageArray = [[NSArray alloc] initWithObjects:
                                                   [UIImage imageNamed:@"jump1.png"],
                                                   [UIImage imageNamed:@"jump2.png"], nil];
        self.player.animationImages = imageArray;
        self.player.animationDuration = 0.3;
        self.player.contentMode = UIViewContentModeBottomLeft;
        [self.view addSubview:self.player];
        [self.player startAnimating];
}

The first method added removes any images associated to the player object. We use this to clean the previously used animation frames. The second method adds a simple animation to our player object. This will appear while the object is moving.

After the jumpStance method, add:

- (void)jump:(UIImageView *)image {
    [self jumpStance];
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration: 0.3];
    [UIView setAnimationCurve: UIViewAnimationCurveEaseOut];
    [UIView setAnimationBeginsFromCurrentState:YES];
        image.transform = CGAffineTransformMakeTranslation(0, -40);
    [UIView commitAnimations];
}

This method actually moves the player object. First it changes the image set, then moves it by 40 pixels vertically.

The line:

[UIView setAnimationCurve: UIViewAnimationCurveEaseOut];

Adds an easing effect to the animation, so it looks more realistic.

Change the buttonPressed method to look like this:

- (void)buttonPressed {
    [self jump:self.player];
}

Build and run. By pressing the button now, the character will jump, but will remain frozen in mid air. Good progress! Let’s get him down now.

Step 8: Finalizing the Character Animation

Above the previously added jump method, add:

-(void)fall:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
    [self cleanStance];
    [self normalStance];
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationCurve: UIViewAnimationCurveEaseOut];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:0.2];
        self.player.transform = CGAffineTransformMakeTranslation(0, 0);
    [UIView commitAnimations];
}

This method sets the character back to the normal state and moves it back to the initial position.

Now, change the jump method to look like:

- (void)jump:(UIImageView *)image {
    [self jumpStance];
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration: 0.3];
    [UIView setAnimationCurve: UIViewAnimationCurveEaseOut];
    [UIView setAnimationBeginsFromCurrentState:YES];
    // Execute fall after the animation ended
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(fall:finished:context:)];
    image.transform = CGAffineTransformMakeTranslation(0, -40);
    [UIView commitAnimations];
}

Build and run. Press the red button now and our character should jump and land back on the ground. That’s it!

Conclusion

Now you should have a basic understanding of some of the most used classed like: UIButton, NSLog, UIImageView and how to execute animations. A similar approach can be applied to add other buttons on the screen, which can trigger different events, so your target object can perform other actions. Do feel free to contact or follow me on Twitter @tudorizer.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://butenas.com Ignas

    Nice, I need to try this! Thanks :)

  • http://twitter.com/patelaxit Axit Patel

    Hey Tudor, really nice tutorial!

  • Harshal

    Just one thing :

    The below line is giving me the error as the button is initializing two times . other wise nice tutoral
    step by step .

    # UIButton *button = [[UIButton buttonWithType:UIButtonTypeCustom]
    # initWithFrame:CGRectMake(240, 150, 50, 50)];

    • http://tudormunteanu.com Tudor
      Author

      @Harshal, thank you for pointing that out.

      • http://www.juliogutierrez.net Lex

        I got the same error. What I did was setting the frame on another line, like:

        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button setFrame:CGRectMake(240, 150, 50, 50)];

        Worked like a charm after that. Nice tutorial, and very inspiring!

  • Thiago Ogoshi

    Tudor,

    Hello! I had success doing this tutorial, but I got 2 warnings, in this lines:

    [self normalStance]; // says: ‘jumpingViewController’ may not respond to ‘-normalStance’
    [self jump:self.player]; // says: ‘jumpingViewController’ may not respond to ‘-jump’

    Though these warnings, the program runs okay.

    And I have on question: how to make this view rotate as the device rotate? In this example, the background image is fixed on top of the screen; I’m wondering how to make it “fullscreen” so I can see the entire image.

    Great tutorial! Thank you!

    • Barry

      Thaigo.

      I had the same problem with;

      [self normalStance]; // says: ‘jumpingViewController’ may not respond to ‘-normalStance’

      Its a really simple solution, just ensure that

      - (void)normalStance {

      [self.player setImage:[UIImage imageNamed:@"ryu.png"]];

      }

      is declared before

      - (void)initPlayer {

      }

      problem solved. I havent gotten as far as your second error yet, but it looks like it may be the same issue.

      Hope that helps.

      • http://thiago.ogoshi.com.br Thiago Ogoshi

        Thanks a lot Barry!

        So, it’s just a problem of order of declaration?

  • http://kpbowler.co.uk Kev

    Hi,

    Thanks for the guide. However, I was getting run-time errors using the code you provided. The error was occurring in the addButton method, and seemed to be to do with the call to initWithFrame.

    I used the ADC site to find some sample code for creating a button, and got the button to display by using the following code:

    CGRect frame = CGRectMake(240, 150, 50, 50);
    UIImage *buttonImage = [UIImage imageNamed:@"button.png"];
    UIButton *button = [[UIButton alloc] initWithFrame:frame];
    [button setTitleColor:[UIColor blackColor] forState:UIControlEventTouchDown];
    [button setBackgroundImage:buttonImage forState:UIControlStateNormal];

    Hope this helps someone else out there.

  • http://maniacdev.com Maniacdev

    Fix is simple.. change the line:

    UIButton *button = [[UIButton buttonWithType:UIButtonTypeCustom]

    initWithFrame:CGRectMake(240, 150, 50, 50)];

    To:

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

    button.frame = CGRectMake(240, 150, 50, 50);

    Nice tutorial for beginners.. I will be linking to it from my own site with the fixed code… just in case it never gets fixed over here.

    • http://thinkdiff.net Mahmud Ahsan

      Nice tutorial and Thanks Maniacdev, without this part

      UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

      button.frame = CGRectMake(240, 150, 50, 50);

      when I tried the applications throws exception. Now its working for me.

    • Barry

      Great simple fix, cheers. This one had me stuck!

  • Joseph

    Hi,

    I was able to do your great tutorial. But in writing my pinball program, I am stuck with animating the flippers when trying to touch the view. Hitting the view anywhere does make one of the flippers animate. But, this will not help, when trying to touch the view where the flipper controls actually are.

    I have tried may different ways to do this and I am pulling my hair out. Been to hundreds of sites, but haven’t been able to finds this.

    Any help would be greatly appreciated.

    Thanks, Joe..

  • http://www.weber.com Weber

    Wow ! , Nice tutorial .. i will try it right now

  • Patricio

    When i tried to build and run this project…
    This 14 errors were found.
    “_OBJC_CLASS_$_UIColor”, referenced from:
    objc-class-ref-to-UIColor in jumpingViewController.o
    “_OBJC_CLASS_$_UIScreen”, referenced from:
    objc-class-ref-to-UIScreen in jumpingViewController.o
    “_OBJC_CLASS_$_UIView”, referenced from:
    objc-class-ref-to-UIView in jumpingViewController.o
    “_OBJC_CLASS_$_UIButton”, referenced from:
    objc-class-ref-to-UIButton in jumpingViewController.o
    “_OBJC_CLASS_$_NSArray”, referenced from:
    objc-class-ref-to-NSArray in jumpingViewController.o
    “_OBJC_CLASS_$_UIImage”, referenced from:
    objc-class-ref-to-UIImage in jumpingViewController.o
    “_OBJC_CLASS_$_NSAutoreleasePool”, referenced from:
    objc-class-ref-to-NSAutoreleasePool in main.o
    “_objc_msgSendSuper2″, referenced from:
    -[jumpingAppDelegate dealloc] in jumpingAppDelegate.o
    -[jumpingViewController didReceiveMemoryWarning] in jumpingViewController.o
    -[jumpingViewController dealloc] in jumpingViewController.o
    “_OBJC_METACLASS_$_UIViewController”, referenced from:
    _OBJC_METACLASS_$_jumpingViewController in jumpingViewController.o
    “_OBJC_CLASS_$_NSObject”, referenced from:
    _OBJC_CLASS_$_jumpingAppDelegate in jumpingAppDelegate.o
    “__objc_empty_vtable”, referenced from:
    _OBJC_METACLASS_$_jumpingAppDelegate in jumpingAppDelegate.o
    _OBJC_CLASS_$_jumpingAppDelegate in jumpingAppDelegate.o
    _OBJC_METACLASS_$_jumpingViewController in jumpingViewController.o
    _OBJC_CLASS_$_jumpingViewController in jumpingViewController.o
    “_OBJC_CLASS_$_UIImageView”, referenced from:
    objc-class-ref-to-UIImageView in jumpingViewController.o
    “_OBJC_METACLASS_$_NSObject”, referenced from:
    _OBJC_METACLASS_$_jumpingAppDelegate in jumpingAppDelegate.o
    _OBJC_METACLASS_$_jumpingAppDelegate in jumpingAppDelegate.o
    _OBJC_METACLASS_$_jumpingViewController in jumpingViewController.o
    “_OBJC_CLASS_$_UIViewController”, referenced from:
    _OBJC_CLASS_$_jumpingViewController in jumpingViewController.o
    Can you help me?
    Thank you!

  • mac__monkey

    Hi,

    I’ve tried coding this tutorial and all I get is a successful build but nothing/black screen in the iphone when I run the actual program. I’ve even copied and pasted the code (I dont normally but thought i’d typed something wrong).

    Thanks in advance.

    mac__monkey

  • Gary

    Wow, my first animation. works great. got here from maniacdev.

  • Zakir

    It’s a nice tutorial. I hope this tutorial will extended to a complete a game. Thanks a lot..

  • subakar prabhu

    Thanks for the tutorial, it would be fine, if we make it through interface builder…..

  • HappyDroid

    Awesome tutorial! Can you update it for SDK 4.0? I’m getting a lot of errors when inputting the code, when putting in “retain” for example… Wish I knew enough to alter the code and get it working, but that’s why I’m here, to learn. Thanks!

  • subbu

    Hi,

    I Am download your source code and run in my system its not working. Its showing exe-bad access.

    Thanks,
    Subbu.

    • subbu

      Hi,

      Its working fineeeeee…… I am changing the button frame in saparate lineeee….. know its working fineeee.

      Nice tutoriallllllll.

      Thanks,
      Subbu…

  • Diego

    Fix for iOS 5

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    viewController = [[ViewController alloc] init];
    [[self window] setRootViewController: [self viewController]];
    [self.window makeKeyAndVisible];
    return YES;
    }

  • FRANCOIS EVERHARD

    How would you use an animation block in this case?

  • David

    This is a nice tutorial, unfortunately, the download source code didn’t work on xcode 4.2, it throw the SIGABRT error at main.m, any idea what may be wrong? Thanks.

  • Arun HS

    Awesome Post, it was really super cool,,,,thanks a lot.