1 / 0

Simple Game Animation – iPhone Programming Exercise 3

Simple Game Animation – iPhone Programming Exercise 3. CSE 391 Fall 2012 Tony Scarlatos. Putting the pieces together.

cisco
Télécharger la présentation

Simple Game Animation – iPhone Programming Exercise 3

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Simple Game Animation – iPhone Programming Exercise 3

    CSE 391 Fall 2012 Tony Scarlatos
  2. Putting the pieces together In the past few lectures we’ve covered a lot of ground. We learned how to create animation blocks, and move objects around in the view based on variable values generated by a random number generator. We learned how to load sounds. We learned how to populate an array of images using a for loop and to associate that array with an image object. We also learned how to move an object based on a touch event, and how to detect if the object’s bounding box intersected with another object. The purpose of this demo is to put some these skills together to make a simple animated game.
  3. The Grave Game In honor of Halloween I decided to make a demo that would involve a character moving through a graveyard and jumping over tombstones, and maybe dodging flying bats. In other words, a simple “dodging” game. From this basic structure more complex games could be built, where, say, the character moves back and forth and collects tokens as well as avoids hazards. For my demo, I envisioned a character that would stay in place, while the background moved behind him, and the obstacles moved toward him. In the film industry, if the camera travels along with the actor, it is called a “trucking shot”. The character does not move in the frame, but the background appears to move. In the game industry this is common in traditional “side-scrolling” games.
  4. Game design I had to think of the elements I would need A spooky sky background picture Background elements like a scary tree or a mound of tombstones Obstacles and hazards to avoid, like a tombstone to jump over, or an animated bat to duck I needed a character who could Walk (the default behavior) Jump (once, based on some event, then return to the default) Duck (once, based on some event, then return to the default) Maybe fall down (based on a collision with an obstacle) The game would have 2 views in landscape mode The game view The game over view Although the game would use 2D sprites, I wanted it to have a 3D look (sometimes called 2 ½ D).
  5. Creating the game assets The character’s different behaviors were animated using the Poser 3D animation tool, and rendered as a sequence of .png images with transparent (alpha) backgrounds. They were rendered at much larger scale than needed for the game (about 200%). The image sequences were further cropped, edited, and scaled using the video layer tools in Photoshop. The other elements were also created in Photoshop, and I set up a mock view of the game to help me get the proper scale for the elements. The view was 460 X 320 (the aspect ratio of the iPhone screen in landscape mode.) Frame from the jumping sequence Mock view of the game Frame from the bat animation
  6. Setting up the Xcode project I chose the Utility Application project template because it provides two views – the main view (for the game) and the flipside view (for the game over screen). I imported the folders of my graphics assets by dragging and dropping them on the Resources folder in the Xcode project window. I also imported a theme music file (.aif format) by dropping it on the project icon. I also had to import the AVFoundation framework by control-clicking on the frameworks folder, and by then choosing Add > Existing Frameworks… > AVFoundation.framework.
  7. The header file The header file for the main view (MainViewController.h) is where the objects I needed were created, their properties declared, and custom methods were declared. This was a necessary first step so that I could make connections between objects and methods in Interface Builder. I also had to import the AVFoundation class, so that I could use its methods to play the theme music for the game. Although I have not implemented all the features of the game as of now, I included all the ones I could think of, but commented some of them out. The significant code is on the next couple of slides, the project itself is on the file server in the Xcode exercises folder of the Mobile Applications group folder.
  8. Image views, image arrays, timers, and an audio player @interfaceMainViewController : UIViewController <FlipsideViewControllerDelegate> { IBOutletUIImageView *bones;//the container for the skeleton animations NSMutableArray *walkAnim; NSMutableArray *jumpAnim; //NSMutableArray *duckAnim; //NSMutableArray *dieAnim;//all the different things the character does //IBOutletUIImageView *bat;//the window for the bat animation //NSMutableArray *batAnim; IBOutletUIImageView *tombs;//will switch between a couple of images IBOutletUIImageView *bkgd;//will switch between a couple of background elements NSTimer *bkgdTimer;//moves the background image across the view NSTimer *tombsTimer;//moves the tombstones across the view //NSTimer *batTimer;//moves the bat across the screen NSTimer *tombsCollisionTimer;//checks for collision with tombs //NSTimer *batCollisionTimer;//checks for collision with bat AVAudioPlayer *themePlayer;//will play various theme songs //AVAudioPlayer *gameOverPlayer;//plays a sound when a collision is detected }
  9. A button action andthe custom methods // custom methods (IBAction) jump; (void) startSound; (void) setupArrays; (void) startTimers; (void) moveBkgd; (void) moveTombs; //- (void) moveBat; (void) checkTombsCollision; //- (void) checkBatCollision; (void) moveUp; - (void) moveDown; - (void) showWalk;
  10. A couple of other items In the MainViewController.m file I had to find the code block beginning with: (BOOL)shouldAutorotateToInterfaceOrientation: And then I added a few lines of code which we already discussed in the Sprite Animation lecture and demo. Then I launched Interface Builder by clicking on the MainView.xib file.
  11. Building the interface The View window was rotated to the landscape mode, and its Background color was set to black in the Attributes tab of the Inspector window. The info button in the View (which is used to flip to the second the view) was deleted, because that action will be determined by detecting a collision between the obstacle and the character. Several Image Views were added – for the sky, for the background element (a tree), for the obstacle (a tombstone), and for the character. Those graphics were added using the dropdown list for the Image property. Because the frames of the character animations are not the same aspect ratio (e.g. the jump animation is taller) that image view’s Mode property was changed from the default Scale to Fill to Bottom Right in the Attributes tab of the Inspector window. To catch the “tap” event that triggers the jump animation a Rounded Rectbutton was dropped over the character image object and scaled to cover the character completely (if I had wanted the entire screen to respond to a touch I could have scaled it to fill the whole view). To make the button invisible its Type was set to Custom in the Attributes tab.
  12. Making connections In the Document window File’s Owner was selected. In the Connections tab of the Inspector window there were 3 Outlets: bones (for the character), background (for the tree), and tombs (for the headstone). A connector was dragged to each of their respective Image Views in the View window. There was one Received Action, jump, which was connected to the button. The IB file was saved and I went back to Xcode to implement my methods.
  13. Implementation In the MainViewController.m file the first thing to do was to synthesize all the properties I declared in the .hfile. Then I added some code to the viewDidLoad method so I could invoke some methods when the app starts. They were: [selfstartSound];//loads the theme music and other game sounds [selfsetupArrays];//loads the images into the arrays [selfstartTimers];//gets the animation and collision detection timers going
  14. Implementation (sound) Right after the viewDidLoad method, I set up the sound to play. The first line of the method provides the path to the sound resource, the third associates that resource with an instance of the AVAudioPlayer, and gives it the instance name themePlayer. The fourth line starts the song at its beginning, and the last line tells it to play. The code is below: (void) startSound{ NSString *themeSongPath = [NSStringstringWithFormat:@"%@%@", [[NSBundlemainBundle] resourcePath], @"/gravehop.aif"]; NSError *err; themePlayer = [[AVAudioPlayeralloc] initWithContentsOfURL: [NSURLfileURLWithPath:themeSongPath] error:&err]; themePlayer.currentTime = 0; [themePlayerplay]; }
  15. Implementation (timers) Right after the startSound method, I implemented the 3 timers I needed. bkgdTimer animates the background image across the screen every 10 seconds. Its selector (the method called when the timer expires) is moveBkgd, a method defined further down in the code. tombsTimeranimates the obstacle, but it fires off every .01 seconds, because it needs to move the object incrementally (which I’ll explain a bit later). Its selector is moveTombs. tombsCollisionTimer fires off almost as fast, every .03 seconds, and it is polling for an overlap (collision) of the image object of the obstacle and the character. Its selector is checkTombsCollision. All the timers were set to repeat indefinitely (repeats:YES). The background and the obstacle are then set to startAnimating. The code is on the next slide.
  16. Timer code (void) startTimers{ bkgdTimer = [[NSTimerscheduledTimerWithTimeInterval:10target:selfselector:@selector(moveBkgd) userInfo:nilrepeats:YES] retain]; [selfmoveBkgd]; [bkgdstartAnimating]; tombsTimer = [[NSTimerscheduledTimerWithTimeInterval:0.01target:selfselector:@selector(moveTombs) userInfo:nilrepeats:YES] retain]; tombsCollisionTimer = [[NSTimerscheduledTimerWithTimeInterval:0.03target:selfselector:@selector(checkTombsCollision) userInfo:nilrepeats:YES] retain]; [tombsstartAnimating]; }
  17. Implementation (image arrays) Next, I populated two image arrays, walkAnim and jumpAnim. First, the arrays were created by allocating memory for them and initializing them. A for loop was used to iterate through all the images, setting the value of i to 1, incrementing i, and stopping when the value of i reached the last image. The value of i was then substituted for the format specifier%d and concatenated with text of the image file’s name (as in w1.png) and put into a string. The string was then used to add the images to the array. The walk cycle array, walkAnim, was then associated with the image object bones, given an animation duration of .5 seconds, and set to start animating. Code for populating the array and for starting the walk cycle animation is on the next slide.
  18. Walk cycle animation (void) setupArrays { walkAnim = [[NSMutableArrayalloc] init]; // a loop that lists the walk cycle images for (inti = 1; i < 16; i++){ NSString *pic = [NSStringstringWithFormat:@"w%d.png", i]; UIImage *img = [UIImageimageNamed:pic]; if (img) [walkAnimaddObject:img]; } //set the default animation for the view - the walk cycle [bonessetAnimationImages:walkAnim]; [bonessetAnimationDuration:.5]; [bonesstartAnimating]; }
  19. Switching the animations When the character jumps he needs to return to the default walking animation after one jump. The change is triggered by a tap on an invisible button floating above the character, so the method is defined in an IBAction (called jump). The image object bones also needs to move up, out of the way of the obstacle, and then return to its previous position. Switching the animation was easy – one image array was exchanged for another in the bones object. But to switch back a timer had to be set, equal to the length of the jump animation, so that when it expired it would switch the animations back. The timer, of course, was set to run only once in response to the button press. The selector for the timer was set to a method called showWalk. The animation block that moves the bones object up is part of the jump method. It uses a setAnimationDidStopSelector method to call a method moveDown that moves the bones object back down. The time allocated to the animation is half the duration of the jump animation, moveDown takes the other half. The code for the jump method is on the next slide, showWalkand moveDownare on the following slide.
  20. The jump method (IBAction) jump{ [bonessetAnimationImages:jumpAnim]; [bonessetAnimationDuration:2]; [bonesstartAnimating]; [NSTimerscheduledTimerWithTimeInterval:1.9target:selfselector:@selector(showWalk) userInfo:nilrepeats:NO]; // move the skeleton up bones.frame = CGRectMake(bones.frame.origin.x, bones.frame.origin.y, 121, 180); [UIViewbeginAnimations:nilcontext:nil]; [UIViewsetAnimationDuration:1]; [UIViewsetAnimationDelegate:self]; [UIViewsetAnimationDidStopSelector:@selector(moveDown)]; bones.frame = CGRectMake(bones.frame.origin.x, bones.frame.origin.y - 85, 121, 180); [UIViewcommitAnimations]; }
  21. showWalk and moveDown (void) showWalk{ [bonessetAnimationImages:walkAnim]; [bonessetAnimationDuration:.5]; [bonesstartAnimating]; } (void) moveDown{ bones.frame = CGRectMake(bones.frame.origin.x, bones.frame.origin.y, 121, 180); [UIViewbeginAnimations:nilcontext:nil]; [UIViewsetAnimationDuration:.5]; [UIViewsetAnimationDelegate:self]; bones.frame = CGRectMake(bones.frame.origin.x, bones.frame.origin.y + 85, 121, 180); [UIViewcommitAnimations]; }
  22. Implementation (move method) Moving the background image across the screen was simple. It used the standard animation block methods for the UIView. The first line gives the starting key frame position and size of the object. The third line gives the animation duration. The fifth line gives the final key frame coordinates and size. The last line commits the animation. The code is on the next slide.
  23. Move method code // moves the background steadily across the screen (void) moveBkgd { bkgd.frame=CGRectMake(460,0,113,163); [UIViewbeginAnimations:nilcontext:nil]; [UIViewsetAnimationDuration:10.0]; [UIViewsetAnimationDelegate:self]; bkgd.frame=CGRectMake(-100,0,113,163); [UIViewcommitAnimations]; }
  24. Fire and forget The moveBkgd animation code is fine if we don’t need to know where the object is in the view between the first and last key frames – the system takes care of that. That kind of method is called “fire and forget”, like a self-guided missile. But if we want to detect a collision with another object then we need to know explicitly where the object is in each “frame” of the animation. To force the system to report the position x or y of the image object as it moves across the screen we had to amend the code to use a variable to describe the object’s x and y values as shown below. The tombsTimer is firing off very rapidly, constantly incrementing the animation based on the previous value of x or y. One more thing about the moveTombsmethod – the obstacle has to be reset to its original position (which is actually offscreen to the right) every time it animates off the left side of the screen. To do that a conditional statement was used to check to see what the x position of the obstacle object (tombs) was. If it was less than -100 pixels, then the object was placed back at the right, at 600 pixels. The code for the moveTombsmethod is on the next slide.
  25. The moveTombs method (void) moveTombs { tombs.frame=CGRectMake(tombs.frame.origin.x,tombs.frame.origin.y,50,78); [UIViewbeginAnimations:nilcontext:nil]; [UIViewsetAnimationDelegate:self]; tombs.frame=CGRectMake(tombs.frame.origin.x - 2,tombs.frame.origin.y,50,78); [UIViewcommitAnimations]; if(tombs.frame.origin.x < -100){ tombs.frame=CGRectMake(600,222,50,78); } }
  26. Checking for collision Now that the animations were set, all I had to do was check to see whether the bounding box (frame) of the obstacle object (tombs) is overlapping the bounding box of the character object (bones). This check is called every .03 seconds by the checkTombsCollision timer in the startTimers method described earlier. A conditional statement tests to see if a collision is detected, and if it is a method will be executed to stop the theme music from playing, and to flip the view over to the “game over” screen. The code is on the next slide. The flipsideViewController method was copied from the code generated by the Utility Application project template when the project was created. It was originally attached to an info button in the main view which was deleted in Interface Builder.
  27. The checkTombsCollision method // checks for collision and takes necessary action when a collision is detected (void) checkTombsCollision{ if(CGRectIntersectsRect(bones.frame, tombs.frame)){ // stop playing the theme song [themePlayerstop]; // flip the view FlipsideViewController *controller = [[FlipsideViewControlleralloc] initWithNibName:@"FlipsideView"bundle:nil]; controller.delegate = self;controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;[selfpresentModalViewController:controlleranimated:YES]; [controller release]; } }
  28. The flipside view So when the obstacle collides with the character, that session of the game is done and the game view flips over to the game over view. I did not do much to the FlipsideView.xib file in Interface Builder. I changed the title to “Good try!” and left the default “Done” button to go back to the game (though I could have renamed the button label “Replay”). However, it’s important to reset the game assets to their starting state once the game over view flips back. The custom methods in the flipsideViewControllerDidFinish method reset the theme music to the beginning and start it playing, and also reset the position of the background image and the obstacle. The code is on the next slide.
  29. flipsideViewControllerDidFinish (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller { [selfdismissModalViewControllerAnimated:YES]; themePlayer.currentTime = 0; [themePlayerplay]; bkgd.frame=CGRectMake(460,0,113,163); tombs.frame=CGRectMake(600,tombs.frame.origin.y,50,78); }
  30. Success!
  31. So what’s next? First, some simple instructions should be added to the game. That could be done with a label on the main view, or it could be a good use of the currently empty panel of the flipside view, in which case code would have to be written to start the game in the flipside view mode instead of the main view. The button label in the flipside view would simply be “Play” to start the game session. To make the game more interesting, each time the view flips the game could toggle between hazards (the flying bat or the tombstone). The background image could toggle between the tree and the grave mound. The music could alternate between a few randomly selected themes. The dodging animation and the falling sequence of the character would have to be implemented. A game over sound could implemented when a collision is detected. Keeping score might be nice, and the display of the score could be in the flipside view along with the game rules. Saving the score to a .plist, so it persists between game sessions might also be a good idea. A login that saves to an array of players and their scores would be cool. Some students have suggested that the target bounding box of the character is too big. That could be addressed by creating a smaller target that floats above the character like the jump button does. Collision would then be checked between the obstacle and the smaller target. Let’s see how it goes by the end of the semester!
More Related