Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all 4374 articles
Browse latest View live

iOS 7 Blur Effects with GPUImage

$
0
0
TutorialLogo

iOS Blurs using GPUImage

Among the many visual changes of iOS 7, one of the more appealing is the subtle use of blurs throughout the OS. Many third-party apps have already adopted this design detail, and are using it in all sorts of wonderful and creative ways.

This tutorial will walk you through several different techniques for implementing iOS 7 blur effects, all with the assistance of a framework called GPUImage.

Created by Brad Larson, GPUImage is a framework which, by taking advantage of the GPU, makes it incredibly easy to apply different effects and filters to both images and videos, whilst maintaining great performance; often more performant than any of the built-in methods provided by Apple’s APIs.

Note: This tutorial requires a physical device to build the sample project; it won’t work in the simulator. You’ll also need an iOS Developer account. If you don’t have a developer account, you can sign up for one here. There are a bunch of awesome perks of being a registered developer, such as being able to develop using physical devices, early access to betas, and a ton of developer resources.

Getting Started

Download the starter project here and extract it to a convenient location on your drive.

Open Video Blurring.xcodeproj in Xcode and run it on your device. It will look similar to the following:

Start

Tap the Menu in the upper-left of the screen (the three horizontal stripes). You’re presented with two options: record a new video or play back an existing video.

Notice how all of the user interface elements have a gray backdrop; that’s rather dull. You’ll be replacing those dull gray backgrounds with some nice iOS 7 blur effects instead.

Why Use Blurs?

Beyond looking cool, blurs communicate three important concepts to the users of your apps:
depth,context and focus.

Depth

Depth provides cues to the user that the interface is layered, and helps them understand how to navigate your app. Previous iOS versions communicated depth with three-dimensional bevels and glossy buttons reflecting an emulated light source, but iOS 7 communicates depth using blurs and parallax.

The parallax effect is evident when you tilt your iOS 7 device from side-to-side. You’ll notice the icons appear to move independently from the background. This provides cues to the user that the interface is composed of different layers, and that important elements sit on top of other less important interface elements — which leads into the next concept: context.

Context

Context allows a user to get a sense of bearing within your app. Animated transitions provide excellent context; instead of having a new view instantly appear when you tap a button, animating between the views gives the user a moment to understand where the new view originates from, and how they may get back to the previous one.

Blurs allow you to show the previous view in the background, albeit out of focus, to give the user even more context as to where they were a moment ago. The Notification Center is a great example of this; when you pull it down, you can still see the original view in the background whilst you work on another task in the foreground.

Focus

Focusing on selective items removes clutter and lets the user navigate quickly through the interface. Users will instinctively ignore elements that are blurred, focusing instead on the more important, in-focus elements in the view.

You will implement two different types of blurs in this tutorial: Static Blur and Dynamic Blur. Static blurs represent a snapshot in time and do not reflect changes in the content below them. In most cases, a static blur works perfectly fine. Dynamic blurs, in contrast, update as the content behind them changes.

It’s much more exciting to see things in action than to talk about them, so head right into the next section to get started on adding some iOS 7 blur effects!

Adding Static Blur

The first step in creating a static blur is converting the current on-screen view into an image. Once that’s done, you simply blur that image to create a static blur. Apple provides some wonderful APIs to convert any view into an image — and there are some new ones in iOS 7 to do it even faster.

These new APIs are all part of Apple’s new snapshot APIs. The snapshotting APIs give you the ability to capture not just a single view, but also the entire view hierarchy. That means if you instruct it to capture a view, it will also capture all the buttons, labels, switches and various views that are placed on top of it.

You’ll implement this capture logic as a category for UIView. That way, you can quickly and easily convert any view and its contained view hierarchy into an image — and get some code reuse to boot!

Creating your Screenshot Category

Go to File/New/File… and select the iOS/Cocoa Touch/Objective-C category, like so:

NewCategory

Name the category Screenshot and make it a category on UIView, as shown below:

CategoryInfo

Add the following method declaration to UIView+Screenshot.h:

-(UIImage *)convertViewToImage;

Next, add the following method to UIView+Screenshot.m:

-(UIImage *)convertViewToImage
{
    UIGraphicsBeginImageContext(self.bounds.size);
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
 
    return image;
}

The method above starts with a call to UIGraphicsBeginImageContext() and ends with UIGraphicsEndImageContext(). These two lines are bookends for what is known as the image context. A context can be one of several things; it can be the screen, or as in this case, an image. The net effect of these two lines is an off-screen canvas on which to draw the view hierarchy.

drawViewHierarchyInRect:afterScreenUpdates: takes the view hierarchy and draws it onto the current context.

Finally, UIGraphicsGetImageFromCurrentImageContext() retrieves the generated UIImage from the image context, which is the object returned by this method.

Now that you have a category to hold this logic, you’ll need to import it in order to use it.

Add the following import to the top of DropDownMenuController.m, just below the other import statements:

#import "UIView+Screenshot.h"

Add the following method to the end of the same file:

-(void)updateBlur
{
    UIImage *image = [self.view.superview convertViewToImage];
}

Here you ensure you capture not just the view but its superview as well. Otherwise, you’d just capture the menu alone.

Testing Your Capture with Breakpoints

To test out your code, add a breakpoint on the line directly below the call to convertViewToImage. The program will halt execution when it hits the breakpoint and you can view the captured image to make sure your code is functioning properly.

To add a breakpoint, click in the margin to the left of the line and Xcode with mark the spot with a blue arrow, as shown below:

AddBreakpoint

The only thing left to do before you test things out is to call your new method.

Scroll up to show and add a call to updateBlur, directly below addToParentViewController:

-(void)show {
    [self addToParentViewController];
 
    [self updateBlur]; // Add this line
 
    CGRect deviceSize = [UIScreen mainScreen].bounds;
 
    [UIView animateWithDuration:0.25f animations:^(void){
        _blurView.frame = CGRectMake(0, 0, deviceSize.size.height, MENUSIZE);
        _backgroundView.frame = CGRectMake(0, 0, _backgroundView.frame.size.width, MENUSIZE);
    }];
}

Build and run the app; tap the menu button and you’ll see that Xcode stops at your breakpoint, as shown below:

BreakpointInfo

To preview the image, select image in the lower left hand pane of the debugger, then click the Quick Look icon as indicated below:

ImagePreview

There’s your captured image, as expected.

Displaying the Captured Image

Now it’s a simple matter to display the captured image in the background of your menu.

You’d usually use an instance of UIImageView to display an image, but since you’ll be using GPUImage to blur the images, you’ll need to use an instance of GPUImageView instead.

The GPUImage framework has already been added to the project; you just need to import the header.

Add the following import to the top of DropDownMenuController.m, just below the others:

#import <GPUImage/GPUImage.h>
Note: As GPUImage is included as a framework, you’ll need to use angle brackets instead of quotes in your import statement.

There’s currently an instance of UIView— _blurView — that gives the menu its gray background.

There’s a UIView instance variable named _blurView, which is used to provide the gray background of the menu. Change the declaration so _blurView is an instance of GPUImageView instead, as shown below:

@implementation DropDownMenuController {
    GPUImageView *_blurView;
    UIView *_backgroundView;
}

You’ll notice that Xcode is giving you a warning: you’re initialising an instance UIView, not GPUImageView, as it expects.

Fix that now. Update the variable assignment in viewDidLoad to the following:

_blurView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, deviceSize.size.height, 0)];

Add the following two lines directly below it, removing the line that sets the background color:

_blurView.clipsToBounds = YES;
_blurView.layer.contentsGravity = kCAGravityTop;

clipToBounds ensures the contents of the view stay inside the view, while contentsGravity fixes the image to the top of the image view.

Since _blurView is already being used as the background, you don’t have to write any extra code to make it visible.

You also need to declare the filter that you will use for blurring.

Add the following declaration to the @implementation block of DropDownMenuController.m:

GPUImageiOSBlurFilter *_blurFilter;

Find the breakpoint you added earlier, right click it and select Delete Breakpoint, as follows:

DeleteBreakpoint

Now would be a great time to initialize your blur filter. Add the following code to DropDownMenuController.m:

-(void)updateBlur
{
    if(_blurFilter == nil){
        _blurFilter = [[GPUImageiOSBlurFilter alloc] init];
         _blurFilter.blurRadiusInPixels = 1.0f;
 
    }
 
    UIImage *image = [self.view.superview convertViewToImage];
}

Note that the blur radius is set to only a single pixel; you’re temporarily setting this to a low value so that you can ensure the image is properly positioned. Once you’re happy you’ll increase it later.

Now you need to display the image in the GPUImageView. However, you can’t simply add an instance of UIImage to a GPUImageView as you would with a UIImageView — you first need to create a GPUImagePicture.

Add the following line to the bottom of updateBlur:

GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image];

At this point, you now have your image, blur filter and image view.

Add the following code to the bottom of updateBlur:

[picture addTarget:_blurFilter];
[_blurFilter addTarget:_blurView];
 
[picture processImage];

These statements act as the glue which bonds everything together. You add the filter as a target of the picture, and then the image view as a target of the filter.

The really neat part of all this is that the image processing is taking place on the GPU; this means that the user interface won’t stall while the blur is calculated and displayed. The final result will simply show up in the image view when the processing is complete. Typically this doesn’t take much time at all, but it never hurts to let the GPU do the heavy lifting when appropriate.

Build and run your app; click on the menu and you’ll see something similar to the following:

HalfImage

That looks a little odd, doesn’t it? What you’re seeing now is the resized image shrunk to fit inside the menu view. To correct this, you’ll need to specify what part of the image you want to show inside the GPUImageView — namely, the top half of your captured and processed view.

Setting contentsRect

Modify show in DropDownMenuController.m as follows:

-(void)show
{
    [self addToParentViewController];
 
    [self updateBlur];
 
    CGRect deviceSize = [UIScreen mainScreen].bounds;
 
    [UIView animateWithDuration:0.25f animations:^(void){
        _blurView.frame = CGRectMake(0.0f, 0.0f, deviceSize.size.height, MENUSIZE);
        _backgroundView.frame = CGRectMake(0.0f, 0.0f, _backgroundView.frame.size.width, MENUSIZE);
        _blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, MENUSIZE / 320.0f); // Add this line!
    }];
}

By specifying the contentsRect you define the rectangle, in the unit coordinate space, that indicates the portion of the layer’s contents that should be used.

Build and run your app, tap the menu button, and…

HalfBox

Uh, that still doesn’t look right! You’re using the correct part of the image, but it’s still scaling inappropriately! The piece you’re missing is the correct content scaling.

Add the following line to show, at the bottom of the animation block:

_blurView.layer.contentsScale = (MENUSIZE / 320.0f) * 2;

The contentsScale property defines the mapping between the logical coordinate space of the layer (measured in points) and the physical coordinate space (measured in pixels). Higher scale factors indicate that each point in the layer is represented by more than one pixel at render time.

Build and run your app, hit the Menu button and check if the scaling works properly:

FullBlur

Yeah — that looks much better! Now close the app, re-open it, and…uh-oh, what’s happened?

HalfBlackBox

Well that’s a bit problematic, to say the least. If you reset the contentScale back to 2.0 before you animate the view, it fixes the half bar problem.

Add the following line to show in DropDownMenuController.m, just above the animation block:

_blurView.layer.contentsScale = 2.0f;

Build and run your app; tap Menu, close the menu, and then tap Menu again. What does your menu look like now?

FullBlackBox

The half-size black box is no longer a problem — but now you have a full-size black box to contend with!

Resetting Blur Filters

This issue comes about the second time you calculate the blur; the proper way to solve this is to remove all of the targets from the blur which resets the filter. If you don’t then the filter call outputs nothing at all.

Update updateBlur as shown below:

-(void)updateBlur
{
    if(_blurFilter == nil){
        _blurFilter = [[GPUImageiOSBlurFilter alloc] init];
        _blurFilter.blurRadiusInPixels = 1.0f;
    }
 
    UIImage *image = [self.view.superview convertViewToImage];
 
    GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image];
    [picture addTarget:_blurFilter];
    [_blurFilter addTarget:_blurView];
 
    [picture processImageWithCompletionHandler:^{
        [_blurFilter removeAllTargets];
    }];
}

Here you’ve replaced processImage with processImageWithCompletionHandler:. This new method has a completion block that runs once the image processing is complete. Once the image is completely processed, you can safely remove all of the targets.

Build and run your app; tap the Menu and see if the black box issue is gone for good:

FinalGrayButton

Open and close the menu a few more times to make sure you’ve squashed that bug for good.

Look closely at the blur effect as the menu opens — something doesn’t look quite right. To get a closer look, slow down the animation to see what’s happening in slow motion.

Update the duration of the animation block in show to 10.0f.

Build and run your app, tap Menu, and watch the menu appear in slow motion:

MisalignedBlur

Ah, now you can see what’s wrong. The blurred image is sliding in from the top. You really want it to appear as if the blur effect itself is sliding down the screen.

Aligning the Background Image

This is where you need to play some tricks with the static blur. When the menu comes down, you need to align the blur with the backdrop instead. So instead of shifting the image view down, you need to expand it, starting at size zero and expanding to full size. This ensures the image will stay in place as the menu opens.

You already have the menu open to the full size in show — you just need to set the height of contentRect to zero when the image view is first created and when it is hidden.

Add the following code to viewDidLoad in DropDownMenuController.m, just below where you initialize _blurView and set it’s initial properties:

_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f);

Still in the same file, add the following line to the bottom of the animation block in :

_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f);

The contentRect property can be animated as well; therefore the original and updated rects will be interpolated automatically during the animation.

Build and run your app. Fortunately you still have the animation running slowly, so it’s easy to check if you’ve fixed the issue:

HalfView

That looks much more natural. You now have a slide-in menu with a blurred background.

Before moving on, re-adjust the little bits of code you changed for testing purposes.

Go to the show method and change the duration of the animation block to 0.25.

Next, in updateBlur, change the value of _blurFilter.blurRadiusInPixels to 4.0f.

Build and run your app; pop open the menu a few times to see what it looks like now:

FinalBlur

Live Blurring

Live blurring is a technically difficult issue to solve. In order to do live blurring effectively you need to capture the screen, blur it and display it, all whilst maintaining 60 frames per second. Using GPUImage, blurring images and displaying them at 60 frames per second is no problem.

The real tricky bit? How to capture the live screen images, believe it or not.

Since you are working with capturing the main user interface, you must use the main thread of the CPU to capture the screen and convert it into an image.

Note: The human eye generally can’t notice anything faster than 46 frames per second. This helps us out as developers because modern processors can get a lot of work done between each of those frames!

A Brief Branch on Threading

When you run a program, you are executing a list of instructions. Each list of instructions runs inside its own thread, and you can run multiple lists of instructions concurrently in separate threads. An app starts on the main thread and new threads are created and executed in the background as necessary. If you haven’t had to manage multiple threads before, you’ve likely always written your apps to run exclusively on the main thread.

The main thread handles interactions and updates to the user interface; it’s critical to make sure that it stays responsive. If you overload the main thread with tasks, you will see the user interface start to stutter or freeze up completely.

If you’ve ever scrolled through the Twitter or Facebook app on your phone, then you’ve seen background threads in action. Not all profile pictures show up immediately as you scroll; the app launches background threads to fetch the image, and once the image has been retrieved it then shows up on-screen.

Without background threads, the scrolling table view would freeze while it tried to retrieve each and every profile image; since retrieving an image can take several seconds, it’s best passed off to a background thread to keep the user interface smooth and responsive.

So how does this affect your app? The UIView snapshot APIs covered earlier must be run on the main thread. This means that each time they run, the entire interface will freeze for a moment before it continues on.

For static blurs, this action happens so fast that you don’t notice it. You only need to capture the screen once. However live blur effects need to capture the screen 60 times a second. If you performed live captures like this on the main thread, animations and transitions would become choppy and stuttered.

Even worse, as the complexity of your user interface increased, the time it would take to capture the interface would also increase, and your app would stutter even more!

What to do, dear reader?

Potential Live Blur Solutions

One solution which many open source live blur libraries use is to slow down the capture frame rate. So instead of capturing the interface every 60 seconds, you capture it maybe 20, 30 or 40 times a second. Even though it doesn’t seem like much of a difference, your eye will pick up the delay. You’ll notice that the blur is out of sync with the rest of the app — and it sometimes looks worse than doing no blurring at all.

Apple handles live blur in some of their apps without issue — but unfortunately they haven’t made this API public yet. The UIView snapshot APIs of iOS 7 are a huge improvement over the old ways of doing things, but they aren’t quite fast enough for live blurring.

Some developers have seized on the blurring features of the UIToolbar to do their dirty work. Yes, it works, but it’s strongly advised that you do NOT use that in your production apps. Sure, it isn’t a private API, but it is an unsupported feature and Apple may reject your app should you use it. This means there are zero guarantees or promises that it will continue to work this way in your app under future versions of iOS 7.

Apple could modify the UIToolbar at any point and break your app in ugly ways. In the iOS 7.0.3 update, Apple modified the effect in the UIToolbar and UINavigationBar and some developers reported that the effect stopped working altogether. Don’t fall into this trap!

A Compromise — Blurring Live Video

All right, you might have to concede that live blurring in your apps isn’t possible at the moment. So what is possible right now, given the limitations on live blurring?

Static blurring is an acceptable compromise in many situations. In the previous section, you modified the view to make it appear as if the view was actually blurring the image behind it with a bit of visual trickery. As long as the view behind it doesn’t move, a static blur usually fits the bill. You can also achieve some nice effects by fading in the blurred background.

Do some experimenting and see if you can find some effects to minimize your inability to perform live blurs.

One thing you can do is blur live video. The bottleneck again is capturing the screen, but GPUImage is very powerful and capable of blurring videos, either live video from the camera or pre-recorded video.

Blurring Video With GPUImage

The process for blurring videos with GPUImage is very similar to blurring an image. With images, you take an instance of GPUImagePicture, send it to a GPUImageiOSBlurFilter and then send that on to a GPUImageView.

In a similar fashion, you’ll take an instance of GPUImageVideoCamera or GPUImageMovie, send it to a GPUImageiOSBlurFilter and then send it to a GPUImageView. GPUImageVideoCamera is used for the live camera on the iPhone, while GPUImageMovie is used for prerecorded videos.

Instances of GPUImageVideoCamera and are already set up in the starter project. Your job is to replace the gray backgrounds for the play and record buttons with a live blurred view of the videos.

The first thing to do is convert those instances of UIView providing the gray background to instances of GPUImageView. Once that is done, you’ll need to adjust the contentRect for each view based on the frame of the view.

This sounds like a lot of work for each view. To make things a little easier, you’ll create a subclass of GPUImageView and put your custom code in there so it can be reused.

Go to File/New/File…, and select iOS/Cocoa Touch/Objective-C class, as below:

NewClass

Name the class BlurView and make it a subclass of GPUImageView, like so:

Screen Shot 2014-01-27 at 21.20.01

Open ViewController.m and add the following import to the top of the file:

#import "BlurView.h"

Still working in ViewController.m, find the declarations for _recordView and _controlView right after the @implementation declaration and modify them to instantiate BlurViews instead, like so:

    BlurView *_recordView; //Update this!
    UIButton *_recordButton;
    BOOL _recording;
 
    BlurView *_controlView; //Update this too!
    UIButton *_controlButton;
    BOOL _playing;

Modify viewDidLoad as follows:

    _recordView = [[BlurView alloc] initWithFrame:
                    CGRectMake(self.view.frame.size.height/2 - 50, 250, 110, 60)]; //Update this!
    //_recordView.backgroundColor = [UIColor grayColor]; //Delete this!
 
    _recordButton = [UIButton buttonWithType:UIButtonTypeCustom];
    _recordButton.frame = CGRectMake(5, 5, 100, 50);
    [_recordButton setTitle:@"Record" forState:UIControlStateNormal];
    [_recordButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    [_recordButton setImage:[UIImage imageNamed:@"RecordDot.png"] forState:UIControlStateNormal] ;
    [_recordButton addTarget:self 
                      action:@selector(recordVideo) 
            forControlEvents:UIControlEventTouchUpInside];
 
    [_recordView addSubview:_recordButton];
    _recording = NO;
 
    _recordView.hidden = YES;
    [self.view addSubview:_recordView];
 
 
    _controlView = [[BlurView alloc] initWithFrame:
                     CGRectMake(self.view.frame.size.height/2 - 40, 230, 80, 80)]; //Update this!
    //_controlView.backgroundColor = [UIColor grayColor]; //Delete this!

Now you need to create the blurred image to show in those image views. Head back to the @implementation block and add the following two declarations:

GPUImageiOSBlurFilter *_blurFilter;
GPUImageBuffer *_videoBuffer;

You already know what GPUImageiOSBlurFilter does; what’s new here is GPUImageBuffer. This takes the video output and captures one frame so you can easily blur the image. As an added bonus, this also helps improve the performance of your app!

Normally you would send the output of the video through the blur filter and then onto the background view where it will be displayed. However, when you use a buffer, you send the output of the video to the buffer which then splits it into the background view and the blur filter. Doing this smoothes the video output display.

Add the following code to the very top of viewDidLoad, just below the call to super:

_blurFilter = [[GPUImageiOSBlurFilter alloc] init];
 
_videoBuffer = [[GPUImageBuffer alloc] init];
[_videoBuffer setBufferSize:1];

Still working in the same file, add the highlighted statements to useLiveCamera:

-(void)useLiveCamera
{
    if (![UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No camera detected" 
                                                        message:@"The current device has no camera" 
                                                       delegate:self 
                                              cancelButtonTitle:@"Ok" 
                                              otherButtonTitles:nil];
        [alert show];
        return;
    }
 
    _liveVideo = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720
                                                     cameraPosition:AVCaptureDevicePositionBack];
    _liveVideo.outputImageOrientation = UIInterfaceOrientationLandscapeLeft;
 
    [_liveVideo addTarget:_videoBuffer];           //Update this
    [_videoBuffer addTarget:_backgroundImageView]; //Add this
    [_videoBuffer addTarget:_blurFilter];          //And this
    [_blurFilter addTarget:_recordView];           //And finally this
 
    [_liveVideo startCameraCapture];
 
    _recordView.hidden = NO;
    _controlView.hidden = YES;
}

This results in a blurred background for the recording controls.

You’ll need to do something similar for the play controls.

Add the following code to loadVideoWithURL:, just below the _recordedVideo.playAtActualSpeed = YES; statement:

[_recordedVideo addTarget:_videoBuffer]; 
[_videoBuffer addTarget:_backgroundImageView];
[_videoBuffer addTarget:_blurFilter];
[_blurFilter addTarget:_controlView];

Build and run; bring up the recording controls and see how things look:

FullImageButton

The good news is that it works — mostly. The bad news is that the entire screen has been scaled down inside the button. This sounds like a similar problem to what you had before. You’ll need to set the contentRect appropriately for the BlurView.

Open BlurView.m and replace the boilerplate initWithFrame: with the following code:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        CGRect deviceSize = [UIScreen mainScreen].bounds;
        self.layer.contentsRect = CGRectMake(frame.origin.x/deviceSize.size.height, 
                                             frame.origin.y/deviceSize.size.width, 
                                             frame.size.width/deviceSize.size.height, 
                                             frame.size.height/deviceSize.size.width);
        self.fillMode = kGPUImageFillModeStretch;
    }
    return self;
}

Each argument of the contentRect must be be between 0.0f and 1.0f. Here you simply take the location of the view and divide it by the size of the screen to get the numbers you need.

Build and run your app, and take a look at your new improved controls:

FinishedBlur

Congratulations! You have successfully implemented a static blur and a live video blur into your project. You are now fully armed with the knowledge to be able to add these iOS 7 blur effects into your own apps!

Where to Go from Here?

You can download the completed project here.

This tutorial has taught you not only about using iOS 7 blur effects in your app, but also how to make use of the GPUImage framework, which as I hope you’ve seen is a very powerful and capable framework. Importantly, we also touched on why blur, when used properly, is a key aspect of the new iOS 7 design language. Hopefully Apple will provide access to the same API’s they’re using in a future update to the SDK, but until that happens GPUImage is a cracking substitute.

Blurring is just the very beginning of what to you’re able to do using GPUImage. The process discussed in this tutorial can be applied to a wide range of different filters and effects. You can find all of them in the documentation.

Go forth and blur!

Hope you enjoyed the tutorial! If you have any questions or comments, please join the forum discussion below!

iOS 7 Blur Effects with GPUImage is a post from: Ray Wenderlich

The post iOS 7 Blur Effects with GPUImage appeared first on Ray Wenderlich.


Protected: Tech Editor Tryout

Top 10 iOS Conferences in 2014

$
0
0
Learn about the best iOS conferences in 2014!

Learn about the best iOS conferences in 2014!

Conference season is upon us!

It’s a great year to be an iOS developer. Besides initiatives like Cocoaheads, which organizes monthly meetings around the world, there are many cool conferences where you can meet iOS developers and speakers from around the world.

Many members of the raywenderlich.com team are regular speakers, or attendees, at conferences — both to stay up-to-date and meet new people. It’s one of the reasons you can count on our publications to be fresh and relevant.

Here’s our list of the top iOS 10 conferences in 2014. The list comes from on our team members’ firsthand experiences, which means at least one of the team has either attended or been (or will be) a speaker. For each one, you’ll find a short description, commentary about the past edition from our team members, and who you might find at each event in 2014.

Get ready for the list of conferences – plus a handy flowchart to help you choose which to attend!

The Conferences

Without further ado, here’s the list! Note they are not in any particular order.

1) WWDC

WWDCIf you can afford it (and are lucky enough to snag a ticket), this is THE iOS conference to attend.

WWDC is organized by Apple to as a conference for iOS developers. Typically, they announce the new version of iOS and its new features (last year they announced iOS 7). The conference is usually five days long, and there are numerous sessions presented by Apple engineers, where you can learn details about the upcoming versions iOS and Mac OS X.

The real bonus is that you can attend labs, where you can often speak to the author of the frameworks you use day to day! Prepare your questions, they are ready to answer.

WWDC is in San Francisco, the capital of the tech scene. Tickets are usually around $1,600 + travel expenses (and tickets are tricky to get), but well worth it if you’re a serious developer. During that week, the city is literally invaded by developers, so you’ll be in good company. It’s probably the best opportunity of the year to socialize with designers, developers, bloggers and pretty much anybody in the mobile ecosystem that you talk to via email or Twitter. During this special week you can meet them face to face!

Since local companies and meetups are aware of the Apple invasion during that week, there’s usually “satellite events” (like altwwdc in 2013), where you can mingle with some of the sharpest guys and gals in the community.

Looking to let your hair down? There are probably more parties during that week than on New Year’s Eve!

Companies that make products for the mobile industry, even if not headquartered near the Bay Area, typically organize a party in the style of Apple’s Beer Bash. It’s another opportunity to get to know your fellow developers in a more casual setting.

2) 360iDev

360idev conference

If you’re an indie developer you have probably heard of 360iDev. It’s a pretty big conference that’s been going for five years, so the organization is rock solid. Here is a list of videos from previous editions.

Some thoughts from our team members:

“The 360 conferences were friendly, well-run, a good assortment of topics, a great location and really good food.” -John Nyquist

“360iDev was the highlight of 2013 for me. I really enjoyed getting to hang out and meet a ton of awesome iOS developers. 360iDev is great because the conference is packed with great sessions for all levels, but it doesn’t end there. John does a fantastic job of organizing not just sessions, but extracurricular events to make sure the mingling doesn’t stop at five.” -Ryan Poolos

3) NSConference

NSNSConference is one the longest-running conferences in Europe, this year being the sixth edition. The speaker line-up is top notch.

Here’s the lowdown from the team:

“NSConference was the first iOS conference I ever attended and I had a blast. There were about 100 attendees from all over the industry, from famous developers to beginners. I think I chatted with at least half of them. The atmosphere was great and the talks were a good mix between technical and business topics. There’s plenty of time between talks to socialize, and it’s easy to make new friends. The city of Leicester isn’t that impressive but the venue, an old theater, more than makes up for it. If you’re in the U.K. or Europe, it’s definitely worth going, but even if you’re from the U.S. or elsewhere in the world it’s worthwhile making the trip.” -Matthijs Hollemans

“This was my favourite conference I’ve ever been to. The talks were iOS specific, but were not all about technical stuff. Some were about how to use the right tools and some were about life as a developer in general. I mostly liked the people there though. I got to meet a lot of awesome people and have stayed in touch with a lot of them since. It was just a very enjoyable and fun time. The location isn’t the best place in the U.K., but the city was cheap to stay. The venue itself was fantastic. It’s in a converted cinema. There are lots of small tables to sit around rather than rows and rows of seats like other conferences.” -Matt Galloway

4) Cocoaconf

ccCocoaconf is not just any old conference, it’s on wheels! There are usually two tours, one in the spring and one in the fall. The conference comes through key cities across the U.S. This makes it easy to attend even if you don’t have much of a budget.

The style is familiar and cozy. Usually, you’ll find the event in a hotel, with meals served in a classic banquet setting to encourage good conversation between attendees.

Some commentary from the team:

“CocoaConf is always a great experience. The speaker to attendee ratio is fantastic because you get a great opportunity to really interact with the speakers in a way no other conference lets you.” -Ryan Poolos

“I really liked this conference. Definitely my favorite of last year. They were really attentive and the show was really well run. They presented iOS specific tech although there were some sessions on design as well as a few on related services. It was a smaller conference which was a nice way to meet other attendees as well as the presenters.” -Brian Moakley

“CocoaConf is a regional conference in the US which host four or five 2 day long conferences in a Spring and Fall tour. The upcoming Spring tour takes place from March into May. CocoaConf draws speakers from all over the US(on occasion other parts of the world too), sharing information on technology, design, and business related to Cocoa development. The vast majority of the talks focus specifically on iOS development and are a healthy mix of beginner and intermediate topics.

Since CocoaConf is a frequently given regional conference it becomes a great local networking event, drawing in people outside the boundaries of the local Cocoaheads. The conferences are typically kept on the smaller side and there is a lot of one on one interaction opportunity with speakers. With some very high caliber speakers present it can be a great chance to get some direct advice from some of the big names in iOS and Mac development. -Kyle Richter

5) Cingleton

cing

Cingleton is one-of-a-kind conference. You probably won’t find any technical presentations, but if you’re looking for inspiration it’s definitely a worth attending. Check out last year’s videos to get an idea.

“The third installation of Cingleton was held in Montreal, where it’s always been. There were a lot of big-name speakers that gave talks on the conference theme of Core Values. The sessions are one-tracked and usually geared more towards the community topics instead of getting technical. The very limited number of tickets (about 150) sold out in about 30 minutes!” -Ryan Nystrom

6) mdevcon

Mdevcon

mdevcon is not specific to iOS, but it’s about everything mobile. 2014 is its third edition.

Here are the notes from one of our our team members:

“mdevcon is a nice little conference, held in a classy movie theater in Amsterdam. The talks are in English and the audience is very diverse. There were two tracks, covering both iOS and Android. It was a two day conference, with workshops on the first day, but I only went the second day. There wasn’t really a lot of time to chat with other attendees in between the talks, which was a bit of a shame, and no vegetarian food. But you’re in the center of Amsterdam so finding food isn’t a big problem, and everyone heads out to nearby bars afterwards for socializing. I’d recommend this conference if you’re in the neighborhood (getting to Amsterdam from most places in Western Europe is pretty easy).” -Matthijs Hollemans

“As an iOS developer, I am entirely focused on the new things coming from Apple every year. However, it’s worth keeping an eye on the other players in the mobile development area. Mdevcon is a great event for mobile developers of all persuasions to meet each other . There is a friendly exchange of ideas and content. As an iOS developer, I can wander into an Android talk and follow along. Likewise, developers from other platforms can see what is required of iOS developers to create quality apps. This cross pollination of information will help you no matter which platform for which you develop apps. And not all talks are platform specific either, as some topics, such as security, design, marketing and user experience apply to everyone.
Mdevcon has one of the best venues and cities of any conference I’ve attended. The Tuschinski Theater is a fantastic open space to attend talks, and the lobby is cosy enough for everyone to talk about the sessions afterward. The Tuschinski Theater is also right in the middle of Rembrandtplein in the heart of Amsterdam, so the sights and sounds of Amsterdam are right outside. The event hosts at Egeniq also introduce a bit of classic Dutch cuisine for you at lunch time. Be sure to grab a bitterball before they are gone. And don’t forget the after event drink up at the Three Sisters pub just down the street!
If you want a friendly environment to learn what is going on in other mobile platforms, in an amazing venue and a terrific world city, then mdevcon should be on the top of your list of events to attend.”
-Saul Mora

7) iOSDevUK

iosdevukBanner_rev4

iOSDevUK is a three day conference that is very focused on iOS, usually with two tracks: one technical and one on business/design.

Here’s what a couple of our team members had to say about it:

“I attended, and gave a talk at, iOSDevUK ’13. And I thoroughly enjoyed the whole experience. The organisers where incredibly friendly, as were all the attendees. There was a good mix of beginner, intermediate and advanced sessions, with even an Arduino workshop thrown in for good measure. The accommodation was great, and since you stayed on the university campus you were only a short walk from the lecture theatres were the sessions were held. ’13 had a great lineup of speakers, including a keynote from Alan Cannistraro of Facebook and both a session and workshop on Core Data with Mr Core Data himself, Marcus Zarra.” -Mic Pringle

“iOSDevUK is held at the rather remote location of Aberystwyth, Wales (U.K.), which takes quite a long time to reach regardless of how you travel, or where you’re traveling from! However, those that attend are rewarded with a very sociable conference where people live together, have breakfast together and enjoy some really high quality talks and presentations. Considering the very low cost, this is definitely a conference I will be returning to next year.” -Colin Eberhardt

8) UIKonf

uikonf

UIKonf is a very nice, new conference. 2013 was the first edition and the organizers made the commitment to bring in new speakers every year. Another peculiarity of UIKonf is that you can vote on the proposals. See the website for more info.

Here’s what I thought about the event:

“UIKonf was really well organized. The single track helped the attendees to stay focused on presentations without having to choose one track. In spite the fact that it was the first edition, it was very well organized, with pre-conference lightning talks and a post-conference hackthlon. Berlin is pretty easy to reach from anywhere in Europe and the iOS community is pretty vibrant. Definitely a conference to check out if you can.” -Cesare Rocchi

9) GDC

gdc

Games, finally! GDC is not a specific conference for iOS, but if you’re a game developer you should consider buying a ticket. Not convinced? Read on…

“If you’re a game developer, GDC is the best conference to attend bar none. The talks are super high quality and are on a breadth of topics, from technical aspects to design to marketing and the business of making games. There are so many great talks going on at once you feel like a kid in a candy store, not to mention getting to meet and hang out with other game developers in the industry and even show-off your game if you want to get a booth. Highly recommended!” -Ray Wenderlich

“2013 was my first year attending GDC. I was going to be speaking in the iOS Games in a Day track of the conference along with Ray Wenderlich, Rod Strougo, Gareth Jenkins and others. The show is enormous with multiple tracks running in parallel. If your involved in games in any way then there is something at this conference for you. For me the technical tracks were the most interesting. Listening to how specific elements of both indie AAA games were developed, how they solved problems and even approached problems was fantastic. I particularly enjoyed a talk by the guys at CodeMasters on how they wrote the AI for the F1 racing game. I was able to enhance some autonomous steering code straight after their talk in my talks demo code which was most useful. While GDC is not focused on iOS, if your developing games then the energy and ideas flying around is something you have to experience at least once.” -Mike Daley

  • When: March 17th-21st
  • Where: San Francisco
  • Team members planning to attend: None this year unfortunately!
  • Website: http://www.gdconf.com

10) NSSpain

nsspainNSSpain was the first edition in 2013 and it’s a spin-off of a popular meetup in Spain. The speaker line up from the first edition was impressive. Keep this one on your radar.

“It was a fun conference with lots of upcoming passionate talkers for around Europe, where a lot of the action is coming from lately. It was held during a wine festival in Rioja, which came with its own advantages. The food was out of this world. I spoke there last year, and I’ll be doing my damnedest to be there in 2014.” -Orta Therox

Which Should I Choose?!

If you’re unsure which to choose, here’s my advice – in a handy flowchart!

conference_diagram_2014_lowres

Unfortunately, it’s impossible to be objective when judging a conference. What we think is spectacular could turn out to be a disappointment in your book. This post sums up the opinions of our team, based on firsthand experience as either speakers or attendees.

We’d love to hear what you thought of these conferences last year, and of course any advice you have on the best accommodations, eats and nightlife.

Finally, please fill out this poll to let us know which conference(s) you’re planning on attending this year:

Note: There is a poll embedded within this post, please visit the site to participate in this post's poll.

We hope to see you at some iOS conferences this year! :]

Top 10 iOS Conferences in 2014 is a post from: Ray Wenderlich

The post Top 10 iOS Conferences in 2014 appeared first on Ray Wenderlich.

Platformer Game Starter Kit Second Edition (Sprite Kit Version) Now Available!

$
0
0
Fully updated for iOS 7 and Sprite Kit!

Fully updated for iOS 7 and Sprite Kit!

Good news – Jake Gundersen has just released a major new second edition to his Platformer Game Starter Kit. It is now fully updated for iOS 7 and Sprite Kit!

This was a ton of work. Since Sprite Kit is a completely different game engine, so almost every page needed to change, and the starter kit needed to go through our editing process all over again.

Most other sites would have charged you again for a major update of this nature, but we are making this completely free of charge to existing customers, to thank you for supporting all we do on this site!

How To Grab Your Copy

If you’ve previously purchased the Platformer Game Starter Kit, you can download the latest version for free on your My Loot page. If for some reason the book does not show up on your My Loot page, fill out this form or contact me.

If you don’t have the Platformer Game Starter Kit yet, it’s a great time to grab a copy!

Jake and I hope you all enjoy the second edition!

Platformer Game Starter Kit Second Edition (Sprite Kit Version) Now Available! is a post from: Ray Wenderlich

The post Platformer Game Starter Kit Second Edition (Sprite Kit Version) Now Available! appeared first on Ray Wenderlich.

Sprite Kit Tutorial: How to Make a Platform Game Like Super Mario Brothers – Part 1

$
0
0
Learn how to make a game like Super Mario!

Learn how to make a game like Super Mario!

Update 2/1/14: I have updated this tutorial for Sprite Kit to celebrate the launch of the second edition of the Platformer Game Starter Kit, which is fully updated for Sprite Kit. Enjoy!

For many of us, Super Mario Brothers was the first game that made us drop our jaws in gaming excitement.

Although video games started with Atari in many ways, the intuitive and visceral controls of Super Mario Brothers and fun level design were such a dramatic improvement that it felt like something completely new – and we couldn’t put it down for hours!

In this tutorial, you’re going to recapture the magic and create your own platform jumper game – but since the hero will be a Koala instead of a plumber, we’ll call it “Super Koalio Brothers!” ;]

Also to keep things simple instead of adding enemies we’ll just have a simple challenge — avoid the spiky floors. That way you’ll be able to focus on learning how to implement the heart of a platformer game: the physics engine.

This tutorial assumes you are already familiar with the basics of Sprite Kit development. If you are new to Sprite Kit, check out some of the other Sprite Kit tutorials on this site first.

Do you have the necessary koala-fications? Then let’s get hopping!

Getting Started

To get started, go ahead and download the starter project for this tutorial.

Once you’ve downloaded the file, unzip it, open the project in Xcode, and build and run. You should see the following appear on the screen:

Starter Project for Super Mario Tutorial

That’s right – just an empty screen for now! :] You’ll be filling this out in the rest of the tutorial.

This starter project is pretty bare bones – the main point of giving it to you is so that you’d have all of the images/sounds you’ll need for the project pre-integrated. Take a look through the project and you’ll see it contains the following:

  • Game Art. Inside Resources\TMX Files\tileSet.png and Resources\sprites.atlas, you’ll find the Koalio free game art pack from Ray’s wife Vicki.
  • Level Map. Inside Resources\TMX Files\level1.tmx, you’ll find a level map I put together using Tiled, based on the SMB level 1-1 you know and love. You’ll learn more about this later in this tutorial.
  • Gratuitous Music and Sound Effects. Inside Resources\Audio, you’ll find some fun background music and sound effects. This is a raywenderlich.com tutorial after all! :]
  • An SKSceneLayer subclass. The scene that appears on startup is called GameLevelScene, which is empty as a drum right now; it’s waiting for you to come! In this tutorial, you’ll be adding some code to create a physics engine and gameplay into this class.
  • An SKSpriteNode subclass. Finally, you’ll see an empty class called Player, which will contain the Koala’s logic. It’s waiting for you to make it fly away! (Sorry for all the jokes Norah!)

Once you’ve had a chance to look through the project and fully understand what’s there, keep reading and we’ll discuss some philosophy about physics engines!

The Tao of Physics Engines

A platform game revolves around its physics engine, and in this tutorial you’ll be creating your own physics engine from scratch.

There are two main reasons why you’ll be rolling your own, instead of using Sprite Kit’s built-in physics engine:

  1. Fine tuning. To get the right feel for a platformer game, you need to be able to fine-tune the feel and response of the engine. In general, platformers created using pre-existing physics engines don’t feel like the Mario/Sonic/Contra games that you’re used to.
  2. Simplicity. Sprite Kit’s built-in physics engine has a lot of capabilities your game engine doesn’t really need, so your homebrew engine will end up being less resource-intensive overall.

A physics engine does two important things:

Forces acting on Koalio.

Forces acting on Koalio.

  1. Simulate movement. The first job of a physics engine is to simulate resistive forces like gravity, and applied forces like running, jumping, and friction.
  2. Detect collisions. The second job of a physics engine is to finds and resolve collisions between the player and other objects in the level.

For example, in your Koalio game you’ll apply an upward force to the Koala to make him jump. Over time, the force of gravity will act against that initial jumping force, which will give you that nice classic parabolic jump pattern.

And as for collision detection, you’ll use that to keep the Koala from falling through the ground, and to detect when our poor Koala collides with some spikes (ouch!)

Let’s see how this will work in practice.

Physics Engineering

In the physics engine you’ll create, the Koala will have its own movement-describing variables: current velocity (speed), acceleration, and position, among others. Using these variables, every movement you apply to the Koala will follow this algorithm:

  1. Is the jump or move action selected?
  2. If so, apply a jump or movement force to the Koala.
  3. Also apply gravity to the Koala.
  4. Compute the resulting velocity for the Koala.
  5. Apply this resulting velocity to the Koala and update his position.
  6. Check for collision between the Koala and other objects.
  7. If there’s a collision, resolve it either by moving the Koala back enough so that the collision is no longer occurring, or by causing damage to the poor Koala.

Forces fighting over Koalio.

You’ll run these steps for every frame. In the game world, gravity is constantly pushing the Koala down and through the ground, but the collision resolution step will put him back on top of the ground in each frame. You can also use this feature to determine if the Koala is still touching the ground, and if not, disallow a jump action if the Koala is already in mid-jump or has just walked off a ledge.

Steps 1-5 will occur solely within the Koala’s object. All the necessary information is contained there, and it makes sense to let the Koala update its own variables.

However, when you reach the sixth step — collision detection — you need to take all of the level features into consideration, such as walls, floors, enemies, and other hazards. The collision detection step will be performed in each frame by the GameLevelScene – remember, that’s the SKScene subclass that will do a lot of the physics engine work.

If you allowed the Koala’s class to update his own position, he would move himself into a collision with a wall or ground block, and the GameLevelScene would then move him back out, repeatedly — which would make him look like he was vibrating. (Had a little too much coffee, Koalio?)

So, you’re not going to allow the Koala to update his own state. Instead, the Koala will have a new variable, desiredPosition, that he will update. The GameLevelScene will check if this desiredPosition is valid by detecting and resolving any collisions, and then the GameLevelScene will update the Koala’s position.

Got it? Let’s try it out and see what it looks like in code!

Loading the TMXTiledMap

I’m going to assume you’re familiar with how tile maps work. If you aren’t, you can learn more about them in this tutorial. Currently, Sprite Kit doesn’t include support for TMXTileMaps, but not to worry, you’ll be relying on the JSTileMap open source project for this tutorial, which is already added to your starter project.

Let’s take a look at the level. Start up your Tiled map editor (download it if you don’t have it already) and open level1.tmx from your project directory. You’ll see the following:

A platformer game level made with Tiled

If you look in the sidebar, you’ll see that there are three different layers:

  • hazards: This layer contains the things the Koala needs to avoid touching to stay alive (gulp).
  • walls: This layer contains tiles the Koala cannot move through, which mostly consist of floor tiles.
  • background: This layer contains things that are there for aesthetics only, such as clouds or hills.

Now time to code! Open up GameLevelScene.m and add the following import:

#import "JSTileMap.h"

This imports the header file for the JSTileMap library that adds support for TMX files in Sprite Kit. Next add a private interface with the following private property right before the @implementation:

@interface GameLevelScene()
@property (nonatomic, strong) JSTileMap *map;
@end

This adds a private instance variable for the tile map into your class.

Next you’ll load this map into your layer by loading it in the initWithSize: section. Add the following code to initWithSize: in the if (self = [super initWithSize:size]) {:

self.backgroundColor = [SKColor colorWithRed:.4 green:.4 blue:.95 alpha:1.0];
 
self.map = [JSTileMap mapNamed:@"level1.tmx"];
[self addChild:self.map];

First, you set a background color to the scene, which will be the blue sky. The next two lines of code simply load the tile map (a JSTiledMap) and add it to the layer.

Next, in GameLevelScene.m, add the import for Player.h:

#import "Player.h"

Still in GameLevelScene.m, add the following property to the @interface section:

@property (nonatomic, strong) Player *player;

Then add the Koala to the level with the following code in initWithSize:

self.player = [[Player alloc] initWithImageNamed:@"koalio_stand"];
self.player.position = CGPointMake(100, 50);
self.player.zPosition = 15;
[self.map addChild:self.player];

This code loads the Koala sprite object, gives it a position, and adds it to the map object.

You may be wondering why you added the Koala to the map object instead of the scene. Well, you want to control exactly which TMX layers are in front of and behind the Koala sprite, so the Koala needs to be a child of the map. You want your hero Koalio in front, so you give him a zPosition of 15. Also, this makes it so that if you scroll the tile map, the Koala still stays in the same relative position within the tile map.

OK, let’s check it out! Build and run and you should see the following:

Player and Tilemap

It looks like a game, but Koalio is defying gravity! It’s time to kiss him goodbye and bring him down – with a physics engine :]

The Gravity of Koalio’s Situation

The gravity force is always on!

To build a physics simulation, you could write a complex set of branching logic that takes the Koala’s state into account and decides which forces to apply based on that state. But, this would quickly become very complicated — and it isn’t really how physics works. In the real world, gravity is always pulling things towards the earth. So you’ll add the constant force of gravity to the Koala in every frame.

Other forces don’t just switch on and off. In the real world, a force is applied to an object and the momentum continues to move the object through space until some other force acts on that object to change the momentum.

For example, a vertical force like a jump doesn’t turn gravity off; it momentarily overcomes gravity, but gravity slows the ascent, and ultimately brings the object back to the ground. Similarly, a force that pushes an object is countered by friction, which gradually slows down the object until it stops.

This is how you’ll model your physics engine. You won’t constantly check whether your Koala is on the ground and decide whether to apply gravity; gravity will always be applied in this world.

Playing God

I control all the forces!

The logic of your physics engine will mean that when a force is applied to an object, the object will continue to move until another force counteracts it. When Koalio walks off a ledge, he’ll continue to move down at an accelerating rate until he collides with something else. When you move Koalio, he won’t stop moving as soon as you stop applying force; friction will slow him down gradually until he stops.

As you proceed with your platform game, you’ll see that this logic will make it easier to handle complex situations, such as an ice floor where the Koala doesn’t stop on a dime, or a free-fall over a cliff. This model of cumulative forces will make for a fun, dynamic-feeling game.

It will also make the implementation easier, because you won’t have to constantly query the state of your objects – they will just follow the natural laws of your world and their behavior will emerge from the application of those laws!

Sometimes, you do get to play God! :]

The Law of the Land: CGPoints and Forces

Let’s define a few terms:

  • Velocity describes how fast an object is moving in a given direction.
  • Acceleration is the rate of change in velocity – how an object’s speed and direction change over time.
  • A force is an influence that causes a change in speed or direction.

In a physics simulation, a force applied to an object will accelerate that object to a certain velocity, and that object will continue moving at that velocity, until acted upon by another force. Velocity is a value that persists from one frame to the next and only changes by the application of new forces.

You’re going to be representing three things with CGPoint structures: velocity (speed), force/acceleration (change in speed), and position. There are two reasons for using CGPoint structures:

  1. They’re 2D. Velocity, force/acceleration, and position are all 2D values for a 2D game. “What?” you might say. “Gravity only acts in one dimension!” However, you could easily imagine a game with changing gravity where you’d need the second dimension. Think Super Mario Galaxy!
  2. It’s convenient. By using CGPoints, you can rely on the various well established functions that deal with CGPoints. In this tutorial, you’ll be using the SKTUtils library that was built by the Tutorial Team for our book iOS Games By Tutorials. You’ll be making heavy use of functions such as CGPointAdd (add two points), CGPointSubtract (subtract them), and CGPointMultiplyScalar (multiply a point by a float to scale it up or down). This will make your code much easier to write — and debug!

Your Koala object will have a velocity variable that will be acted upon in each frame by a number of forces, including gravity, forward/jumping forces supplied by the user, and friction, which will slow and stop the Koala.

In each frame, you’ll add all these forces together, and the accumulated force that results will be added to the previous velocity of the Koala object. That will give you the current velocity. The current velocity will be scaled down to match the fractional time amount between each frame, and finally, that scaled value will be used to move the Koala’s position for that frame.

Note: If any of this still sounds confusing, Daniel Shiffman wrote an excellent tutorial on vectors that explains the accumulation of forces structure that you’ll be using. The tutorial is designed for Processing, a language for creative designers similar to Java, but the concepts are applicable in any programming language. It’s a great and accessible read and I highly recommend you check it out!

Let’s start with gravity. Set up the run loop, where you’ll be applying these forces. In the GameLevelScene.m, add a new property that will help you keep track of the time that passes in between each frame, in the @interface section:

@property (nonatomic, assign) NSTimeInterval previousUpdateTime;

Then, add the following method:

//1
- (void)update:(NSTimeInterval)currentTime
{
  //2
  NSTimeInterval delta = currentTime - self.previousUpdateTime;
  //3
  if (delta > 0.02) {
    delta = 0.02;
  }
  //4
  self.previousUpdateTime = currentTime;
  //5
  [self.player update:delta];
}
  1. First, the update method is built into every SKScene object and all you need to do is implement it and every frame you will get called before the scene is rendered. It provides an NSTimerInterval value that represents the current timestamp of your program.
  2. By subtracting the current time from the last time stamp, you will get the delta time, or the interval, since the last time the method was called. This time interval will be used to scale movement and other forces (like gravity) in order to achieve smooth, consistent animations.
  3. Sometimes delta may spike. This occurs at the beginning of the game (for the first few frames as things are still being loaded into memory) and occasionally when something else happens on the device (like when a system notification comes in). By capping it at .02, you reduce the chance of getting a time step that is too large (which can result in the physics engine behaving in unexpected ways, like Koalio moving through an entire tile).
  4. Here you set the previousUpdateTime to the currentTime which will be used next frame to determine delta.
  5. Finally, you call a method on Player called update. You’ll implement that next.

Next open up Player.h and add modify it to look like the following:

#import <SpriteKit/SpriteKit.h>
 
@interface Player : SKSpriteNode
@property (nonatomic, assign) CGPoint velocity;
- (void)update:(NSTimeInterval)delta;
@end

Next add the implementation to Player.m:

#import "Player.h"
//1
#import "SKTUtils.h"
 
@implementation Player
//2
- (instancetype)initWithImageNamed:(NSString *)name {
  if (self == [super initWithImageNamed:name]) {
    self.velocity = CGPointMake(0.0, 0.0);
  }
  return self;
}
 
- (void)update:(NSTimeInterval)delta {
  //3
  CGPoint gravity = CGPointMake(0.0, -450.0);
  //4
  CGPoint gravityStep = CGPointMultiplyScalar(gravity, delta);
  //5
  self.velocity = CGPointAdd(self.velocity, gravityStep);
  CGPoint velocityStep = CGPointMultiplyScalar(self.velocity, delta);
  //6
  self.position = CGPointAdd(self.position, velocityStep);
}
 
@end

Let’s go through the above code section by section.

  1. First, you import SKUtils. You’ll need those CGPoint convenience methods frequently here.
  2. Next, you create a new initWithImageNamed method and initialize the velocity variable to 0.0.
  3. Here you declared the value of the gravity vector (vector meaning the change in position). For each second in time, you’re accelerating the velocity of the Koala 450 points towards the floor. If the Koala starts from a standstill, at the one second mark he’ll be moving at 450 points/second, at two seconds he’ll be moving at 900 points/second, and so forth. Clear enough!
  4. Here, you used the CGPointMulitplyScalar to scale the acceleration down to the size of the current time step. Recall that CGPointMulitplyScalar multiplies a CGPoint’s values by a float value, and returns the CGPoint result. Even when you’re faced with a variable frame rate, you’ll still get consistent acceleration.
  5. Here once you’ve calculated the gravity for the current step, you add it to your current velocity. With the new velocity you’ve calculated, you then got the velocity for a single timestep. Again, you’re doing these calculations in order to get consistent velocity, no matter what the frame rate is.
  6. Finally, with the velocity you calculated for this single step, you use the CGPointAdd function to get the updated position for the Koala.

Congratulations! You are well on your way to writing your first physics engine! Build and run now to see the result!

Gravity

Whoops — Koalio is falling through the floor! Let’s fix that up.

Bumps In The Night – Collision Detection

Collision detection is a fundamental part of any physics engine. There are many different kinds of collision detection, from simple bounding box detection, to complex 3D mesh collision detection. Lucky for you, a platformer like this needs only a very simple collision detection engine.

In order to detect collisions for your Koala, you’ll need to query the TMXTileMap for the tiles that directly surround the Koala. Then, you’ll use a few built-in iOS functions to test whether your Koala’s bounding box is intersecting with a tile’s bounding box.

Note: Forgot what a bounding box is? It’s simply the smallest axis-aligned rectangle that a sprite fits inside. Usually this is straightforward and is the same as the frame of the sprite (including transparent space), but when a sprite is rotated it gets a little tricky. Don’t worry – Sprite Kit has a helper method to calculate this for you :]

The functions CGRectIntersectsRect and CGRectIntersection make these kinds of tests very simple. CGRectIntersectsRect tests if two rectangles intersect, and CGRectIntersection returns the intersecting CGRect.

First, you need to find the bounding box of your Koala. Every sprite loaded has a bounding box that is the size of the texture and is accessible with the frame property. However, you’ll usually want to use a smaller bounding box.

Why? The texture usually has some transparent space near the edges, depending on the Koala sprite. You don’t want to register a collision when this transparent space overlaps, but only when actual points start to overlap.

Sometimes you’ll even want the points to overlap a little bit. When Mario is unable to further move into a block, is he just barely touching it, or do his arms and nose encroach just a little bit into the block?

Let’s try it out. In Player.h, add:

-(CGRect)collisionBoundingBox;

And in Player.m, add:

-(CGRect)collisionBoundingBox {
  return CGRectInset(self.frame, 2, 0);
}

CGRectInset shrinks a CGRect by the number of points specified in the second and third arguments. So in this case, the width of your collision bounding box will be four points smaller — two on each side — than the bounding box based on the image file you’re using.

Heavy Lifting

Now it’s time to do the heavy lifting. (“Hey, are you calling me fat?” says Koalio).

You’re new method will need to perform a number of tasks in your GameLevelScene in order to accomplish the collision detection:

  • Return the tile map coordinates of the eight tiles that surround the current location of the Koala.
  • Determine which, if any of these eight tiles is a collision tile. Some of your tiles won’t have physical properties, like clouds in the background, and therefore your Koala won’t collide with them.
  • A method to resolve those collisions in a prioritized way.

You’ll create two helper methods that will make accomplishing the above methods easier.

  • A method that looks up the GID of a tile at a given coordinate (more on GIDs later).
  • A method that takes a tile’s coordinates and returns the rect in pixel coordinates.

Tackle the helper methods first. Add the following code to GameLevelScene.m:

-(CGRect)tileRectFromTileCoords:(CGPoint)tileCoords {
  float levelHeightInPixels = self.map.mapSize.height * self.map.tileSize.height;
  CGPoint origin = CGPointMake(tileCoords.x * self.map.tileSize.width, levelHeightInPixels - ((tileCoords.y + 1) * self.map.tileSize.height));
  return CGRectMake(origin.x, origin.y, self.map.tileSize.width, self.map.tileSize.height);
}
 
- (NSInteger)tileGIDAtTileCoord:(CGPoint)coord forLayer:(TMXLayer *)layer {
  TMXLayerInfo *layerInfo = layer.layerInfo;
  return [layerInfo tileGidAtCoord:coord];
}

The first method finds the pixel origin coordinate by multiplying the tile coordinate by the tile size. You need to invert the coordinate for the height, because the coordinate system of Sprite Kit/OpenGL has an origin at the bottom left of the world, but the tile map coordinate system starts at the top left of the world. Standards – aren’t they great?

Why do you add one to the tile height coordinate? Remember, the tile coordinate system is zero-based, so the 20th tile has an actual coordinate of 19. If you didn’t add one to the coordinate, the point it returned would be 19 * tileHeight.

The second method just needs to drill down into the TMXLayerInfo object property of the layer object. The TMXLayer class contains a method to find a GID based on pixel coordinates, called tileGIDAt:, but you need to find the GID by the tile coordinate, so you need to access the TMXLayerInfo object, which has such a method. This is particular to the JSTileMap implementation, if you use another implementation of the TMXTileMap standard, this method will likely exist, but it may be accessed in another way.

I’m Surrounded By Tiles!

Now move on to the first part of your physics engine which will retrieve the surrounding tiles. In this first part you’ll be iterating through the tiles in a particular order to inspect the CGRects for intersection with the Player CGRect. You’ll be looking up the GID as well.

A GID is a number that represents the index of the image from the tileset. Every TMX layer has a tileset that has a bunch of images arranged in a grid. The GID is the position in the grid for a particular image. In this implementation, the first image at the top left of the tileset is GID 1, the second (top, to the immediate right of the first tile) is tile 2, etc. Each coordinate position in the map has either a 0, for no tile, or a number that represents the position of the image in the tileset.

You’ll be arranging the order of tiles adjacent to the Koala’s position by priority. For example, you want to resolve collisions for the tiles directly left, right, below, and above your Koala before you resolve any collisions on the diagonal tiles. Also, when you resolve the collision for a tile below the Koala, you’ll need to set the flag that tells you whether the Koala is currently touching the ground.

Add the following method, still in GameLevelScene.m:

- (void)checkForAndResolveCollisionsForPlayer:(Player *)player forLayer:(TMXLayer *)layer {
  //1
  NSInteger indices[8] = {7, 1, 3, 5, 0, 2, 6, 8};
  for (NSUInteger i = 0; i < 8; i++) {
    NSInteger tileIndex = indices[i];
 
    //2
    CGRect playerRect = [player collisionBoundingBox];
    //3
    CGPoint playerCoord = [layer coordForPoint:player.position];
    //4
    NSInteger tileColumn = tileIndex % 3;
    NSInteger tileRow = tileIndex / 3;
    CGPoint tileCoord = CGPointMake(playerCoord.x + (tileColumn - 1), playerCoord.y + (tileRow - 1));
    //5
    NSInteger gid = [self tileGIDAtTileCoord:tileCoord forLayer:layer];
    //6
    if (gid) {
      //7
      CGRect tileRect = [self tileRectFromTileCoords:tileCoord];
      //8
      NSLog(@"GID %ld, Tile Coord %@, Tile Rect %@, player rect %@", (long)gid, NSStringFromCGPoint(tileCoord), NSStringFromCGRect(tileRect), NSStringFromCGRect(playerRect));
      //collision resolution goes here
    }
 
  }
}

Phew – there’s a lot of code here! Don’t worry, we’ll go over it in detail.

Before we go section by section, note that you’re passing in a layer object here and the Player object. In your tiled map, you have the three layers we discussed earlier – hazards, walls, and backgrounds.

Having separate layers allows you to handle the collision detection differently depending on the layer.

  • Koala vs. hazards. If it’s a collision with a block from the hazard layer, you’ll kill the poor Koala (rather brutal, aren’t you?).
  • Koala vs. walls. If there’s a collision with a block on the wall layer, then you’ll resolve that collision by preventing further movement in that direction. “Halt, beast!”
  • Koala vs. backgrounds. If the Koala collides with a block from the background layer, you’ll do nothing. A lazy programmer is the best kind, or so they say ;]

There are other ways to distinguish between different types of blocks, but for your needs, the layer separation is efficient.

Passing in the Player object as an argument here is something that I do so this code can be extended. I won’t cover it here in this tutorial, but if you wanted to add other moving creatures to your game, you can use this same collision detection routine for them as well.

OK, now let’s go through the code above section by section.

  1. The first step it to create an array of indices that represent the positions of the tiles that surround the Koala. You then iterate through the 8 tile indices, storing the current index in the tileIndex variable.

    The Koala is less than two tile widths (a tile is 16 points) high and two tile widths wide. This means that the Koala will only every be encroaching on a 3×3 grid of tiles that directly surround him. If his sprite were larger you’d need to look beyond that 3×3 grid, but to keep things simple, I’m keeping him small.

    The index numbers represent the order in which the tiles will be resolved. I’ll cover this in more detail a little later, but tile index 7, for example, is the tile directly beneath the Koala and this tile is the tile that you want to resolve first, because it determines if the Koala is on the ground or not. The information about him being on the ground becomes important when he tries to jump.
  2. In step two, you retrieve the CGRect (in points) that will trigger a collision with the Player.
  3. Next, you find the tile coordinate of the player’s position. This is the starting place from which you’ll find the eight other tile coordinates for the surrounding tiles.
  4. This is the key section, by starting from the player’s coordinate, you perform a divide on the tile index to find a row value and a modulo on the index to find a column value. The, using these row and column values, you find a tile coordinate that is around the player’s position.

    Note: For example, if the tileIndex is 3, the value in tileColumn would be 0 (3 % 3 = 0) and the value in tileRow would be 1 (3 / 3 = 1). If the player’s position was found to be at tile coordinate 100, 18, then the surrounding tile at tileIndex 3 would be 100 + (0 – 1) and 18 + (1 – 1) or 99, 18, which is the tile directly to the left of the Koala’s tile position.

  5. In this step, you use the method you created earlier to look up the GID value for the tile at the tile coordinate found based on the index.
  6. If the GID is 0, it means that for that layer, there is no tile at that coordinate, it’s blank space, and you don’t need to test for a collision with blank space.
  7. If there is a value in GID, then the next step is to get the CGRect for that tile in points.
  8. At this point, you are ready to log the results of the method so far to validate that you are correctly detecting and retrieving tiles and their positions.
    In the next code section, you’ll add collision detection and resolution.

Note: You only need information for eight tiles, because you should never need to resolve a collision with the tile space in the center of the 3 by 3 grid.

You should always have caught that collision and resolved it in a surrounding tile position. If there is a collidable tile in the center of the grid, Koalio has moved at least half his width in a single frame. He shouldn’t move this fast, ever – at least in this game!

Often in the case of the tile directly under the Koala, resolving the collision will also resolve the collisions for the diagonal tiles. See the figure to the right. By resolving the collision beneath Koalio, shown in red, you also resolve the collision with block #2, shown in blue.

Your collision detection routine will make guesses about how to best resolve collisions. Those assumptions are valid more often for adjacent tiles than for diagonal tiles, so you want to avoid collision resolution with diagonal tiles as much as possible.

Here’s an image that shows the order of the tiles as they exist in the indices array. The bottom, top, left, and right tiles are resolved first. Knowing this order will also help you know when to set the flag that the Koala is touching the ground (so you know if he can jump or not, which you’ll cover later).

Order Index

You’re almost ready for the next build to verify that everything is correct! However, there are a few things to do first. You need to add the walls layer as an instance variable to the GameLevelScene class so you can access it there.

Inside GameLevelScene.m, make the following changes:

// Add to the @interface declaration
@property (nonatomic, strong) TMXLayer *walls;
 
// Add to the init method, after the map is added to the layer
self.walls = [self.map layerNamed:@"walls"];
 
// Add to the update method
[self checkForAndResolveCollisionsForPlayer:self.player forLayer:self.walls];

Build and run! But unfortunately it crashes, as you will see in the console:

Logging Tiles

First you’ll see you’re getting information about tile positions. You only see tiles at coordinates 18 and 19, because where Koalio is at, there are no tiles above and to his sides.

Ultimately, this will crash with a TMXLayerInfo Assertion Failure error message though. This happens when the tileGIDat: method is given a tile position that is outside the boundaries of the tile map.

You’re going to stop it from happening by implementing collision detection.

Taking Away Your Koala’s Privileges

Up to this point, the Koala got to set his own position. But now you’re taking that privilege away.

If the Koala updates his position and then the GameLevelScene finds a collision, you’ll want your Koala to get moved back. You don’t want him bouncing all over like a cat on catnip!

So, he needs a new variable that he can update, but that will stay a secret between himself and the GameLevelScenedesiredPosition.

You want the Koala class to calculate and store his desired position. But the GameLevelScene will update your Koala’s position after that position is validated for collisions. The same applies to the collision detection tile loop — you don’t want the collision detector updating the actual sprite until after all the tiles have been checked for collisions and resolved.

You’ll need to change a few things. First, add this new property to Player.h

@property (nonatomic, assign) CGPoint desiredPosition;

Now, modify the collisionBoundingBox method in Player.m to the following:

- (CGRect)collisionBoundingBox {
  CGRect boundingBox = CGRectInset(self.frame, 2, 0);
  CGPoint diff = CGPointSubtract(self.desiredPosition, self.position);
  return CGRectOffset(boundingBox, diff.x, diff.y);
}

This computes a bounding box based on the desired position, which the layer will use for collision detection.

Next, make the following change to the update method so that it’s updating the desiredPosition property instead of the position property:

// Replace this line 'self.position = CGPointAdd(self.position, velocityStep);' with:
self.desiredPosition = CGPointAdd(self.position, velocityStep);

Finally, in GameLevelScene.m‘s checkForAndResolveCollisionsForPlayer:forLayer: method, there’s a reference to player.position. Change it to desiredPosition as well:

CGPoint playerCoord = [layer coordForPoint:player.desiredPosition];

Let’s Resolve Some Collisions!

Now it’s time for the real deal. This is where you’re going to tie it all together.

Inside checkForAndResolveCollisionsForPlayer:forLayer:, replace the code from the comment “collision resolution goes here” until the end of the method (including the final curly brace) with the following code:

      //1
      if (CGRectIntersectsRect(playerRect, tileRect)) {
        CGRect intersection = CGRectIntersection(playerRect, tileRect);
        //2
        if (tileIndex == 7) {
          //tile is directly below Koala
          player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y + intersection.size.height);
        } else if (tileIndex == 1) {
          //tile is directly above Koala
          player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y - intersection.size.height);
        } else if (tileIndex == 3) {
          //tile is left of Koala
          player.desiredPosition = CGPointMake(player.desiredPosition.x + intersection.size.width, player.desiredPosition.y);
        } else if (tileIndex == 5) {
          //tile is right of Koala
          player.desiredPosition = CGPointMake(player.desiredPosition.x - intersection.size.width, player.desiredPosition.y);
          //3
        } else {
          if (intersection.size.width > intersection.size.height) {
            //tile is diagonal, but resolving collision vertically
            //4
            float intersectionHeight;
            if (tileIndex > 4) {
              intersectionHeight = intersection.size.height;
            } else {
              intersectionHeight = -intersection.size.height;
            }
            player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y + intersection.size.height );
          } else {
            //tile is diagonal, but resolving horizontally
            float intersectionWidth;
            if (tileIndex == 6 || tileIndex == 0) {
              intersectionWidth = intersection.size.width;
            } else {
              intersectionWidth = -intersection.size.width;
            }
            player.desiredPosition = CGPointMake(player.desiredPosition.x  + intersectionWidth, player.desiredPosition.y);
          }
        }
      }
    }
  }
  //5
  player.position = player.desiredPosition;
}

Okay! Let’s look at the code you’ve just implemented.

  1. To check for the collision, you use CGRectIntersectsRect to see if the player’s (desired) rectangle and the tile’s rectangle intersect. Just because there’s a tile adjacent to the Koala’s position, doesn’t necessarily mean that they are overlapping.
    If there is a collision, then you get the CGRect that describes the overlapping section of the two CGRects with the CGRectIntersection() function.

Pausing to Consider a Dilemma…

Here’s the tricky bit. You need to determine how to resolve this collision.

You might think the best way to do so is to move your Koala backwards out of the collision, or in other words, to reverse the last move until a collision no longer exists with the tile. That’s the way some physics engines work, but you’re going to implement a better solution.

Consider this: gravity is constantly pulling the Koala into the tiles underneath him, and those collisions are constantly being resolved.

If you imagine that the Koala is moving forward, the Koala is also going to be moving downward at the same time due to gravity. If you choose to resolve that collision by reversing the last move (forward and down), the Koala would need to move upward and backward — but that’s not what you want!

Your Koala needs to move up enough to stay on top of those tiles, but continue to move forward at the same pace.

Illustration of good vs. bad ways to move up from the wall.

The same problem would also present itself if the Koala were sliding down a wall. If the user is pressing the Koala into the wall, then the Koala’s desired trajectory is diagonally downward and into the wall. Reversing this trajectory would move him upward and away from the wall — again, not the motion you want! You want the Koala to stay on the outside of the wall without slowing or reversing his downward speed.

Illustration of good vs. bad ways to move away from the wall.

Therefore, you need to decide when to resolve collisions vertically, when to resolve them horizontally, and to handle both events as mutually exclusive cases. Some physics engines always resolve one way first, but you really want to make the decision based on the location of the tile relative to the Koala. So, for example, when the tile is directly beneath the Koala, you will always resolve that collision by moving the Koala upward.

What about when the tile is diagonally opposite to the Koala’s position? In this case, you’ll use the intersecting CGRect as a guess as to how you should move him. If the intersection of the two rects is wider than it is deep, you’ll assume that the correct resolution in this case is vertical. If the intersecting rect is taller than it is wide, you’ll resolve it horizontally.

Detecting collision direction from intersection rectangle.

This process will work reliably as long as the Koala’s velocity stays within certain bounds and your game runs at a reasonable frame rate. Later on, you’ll include some clamping code for the Koala so that he doesn’t fall too quickly, which could cause problems, such as moving through an entire tile in one step.

Once you’ve determined whether you need a horizontal or vertical collision resolution, you will use the intersecting CGRect size in dimension to move the Koala back out of a collision state. Look at the height or width, as appropriate, of the collision CGRect and use that value as the distance to move the Koala.

By now, you may have suspected why you need to resolve tiles in a certain order. You’ll always do the adjacent tiles before the diagonal ones. If you check the collision for the tile that is below and to the right of the Koala, you’ll want to resolve this collision vertically.

However, it’s possible that in this position the collision CGRect would be taller than it is wide, such as in the case where the Koala is barely colliding with the tile.

Refer again to the figure to the right. The blue area (kinda hard to see; it’s to the right of the red area) is tall and skinny, because that collision intersection only represents a small portion of the whole collision. However, if you’ve already resolved the tile directly beneath the Koala, then you’re no longer be in a collision state with the tile below and to the right, thereby avoiding the problem altogether.

Back to the Code!

Returning to the monster checkForAndResolveCollisionsForPlayer:forLayer: method…

  1. In section 2 you use the index to determine the position of the tile. You are going to deal with the adjacent tiles individually, moving the Koala by subtracting or adding the width or height of the collision as appropriate. Simple enough. However, once you get to the diagonal tiles, you’re going to implement the algorithm described in the previous section.
  2. In section 3, you determine whether the collision is wide or tall. If it’s wide, you resolve vertically.
  3. Next you create a variable to hold the value of the distance that you need to move the Player to no longer be in a collision state with the tile.
  4. You’ll either be moving the Koala up or down, which you determine next by seeing if the tile index is greater than five (tile indices 6 and 8 are both beneath). Based on that, you know whether you need to add or subtract the collision height from the Koala. The horizontal collision resolution follows the same logic.
  5. Finally, you set the position of the Koala to the final collision-detection resolved result.

That method is the guts of your collision detection system. It’s a basic system, and you may find that if your game moves very quickly or has other goals, you need to alter it to get consistent results. At the end of this article, there are a couple of really great resources with more about how to handle collision detection.

Let’s put it to use! You can remove or comment out the log statement in the checkForAndResolveCollisionsForPlayer:forLayer:

//NSLog(@"GID %ld, Tile Coord %@, Tile Rect %@, player rect %@", (long)gid, NSStringFromCGPoint(tileCoord), NSStringFromCGRect(tileRect), NSStringFromCGRect(playerRect));

Build and run. Are you surprised by the results?

Eventual Gravity

Koalio is stopped by the floor, but sinks into it eventually! What gives?

Can you guess what’s been missed? Remember — you are adding the force of gravity to the Koala’s velocity with each frame. This means that Koalio is constantly accelerating downward.

Here’s the solution: You are constantly increasing the speed of the Koala’s downward trajectory until it is greater than the size of the tile — you’re moving through an entire tile in a single frame, which was a problem discussed earlier.

When you resolve a collision, you also need to reset the velocity of the Koala to zero for that dimension! The Koala has stopped moving, so the velocity value should reflect it.

If you don’t do this, you’ll get weird behaviors, both moving through tiles as you saw above, and also in situations where your Koala jumps into a low ceiling and he floats against it longer than he should. This is the kind of weirdness you want to avoid in your game.

It was mentioned before that you need a good way to determine when the Koala is on the ground so he can’t jump off of thin air. You’ll set that flag up now. Add the indicated lines to the checkForAndResolveCollisionsForPlayer:forLayer:

- (void)checkForAndResolveCollisionsForPlayer:(Player *)player forLayer:(TMXLayer *)layer
{
  NSInteger indices[8] = {7, 1, 3, 5, 0, 2, 6, 8};
  player.onGround = NO;  ////Here
  for (NSUInteger i = 0; i < 8; i++) {
    NSInteger tileIndex = indices[i];
 
    CGRect playerRect = [player collisionBoundingBox];
    CGPoint playerCoord = [layer coordForPoint:player.desiredPosition];
 
    NSInteger tileColumn = tileIndex % 3;
    NSInteger tileRow = tileIndex / 3;
    CGPoint tileCoord = CGPointMake(playerCoord.x + (tileColumn - 1), playerCoord.y + (tileRow - 1));
 
    NSInteger gid = [self tileGIDAtTileCoord:tileCoord forLayer:layer];
    if (gid != 0) {
      CGRect tileRect = [self tileRectFromTileCoords:tileCoord];
      //NSLog(@"GID %ld, Tile Coord %@, Tile Rect %@, player rect %@", (long)gid, NSStringFromCGPoint(tileCoord), NSStringFromCGRect(tileRect), NSStringFromCGRect(playerRect));
      //1
      if (CGRectIntersectsRect(playerRect, tileRect)) {
        CGRect intersection = CGRectIntersection(playerRect, tileRect);
        //2
        if (tileIndex == 7) {
          //tile is directly below Koala
          player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y + intersection.size.height);
          player.velocity = CGPointMake(player.velocity.x, 0.0); ////Here
          player.onGround = YES; ////Here
        } else if (tileIndex == 1) {
          //tile is directly above Koala
          player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y - intersection.size.height);
        } else if (tileIndex == 3) {
          //tile is left of Koala
          player.desiredPosition = CGPointMake(player.desiredPosition.x + intersection.size.width, player.desiredPosition.y);
        } else if (tileIndex == 5) {
          //tile is right of Koala
          player.desiredPosition = CGPointMake(player.desiredPosition.x - intersection.size.width, player.desiredPosition.y);
          //3
        } else {
          if (intersection.size.width > intersection.size.height) {
            //tile is diagonal, but resolving collision vertically
            //4
            player.velocity = CGPointMake(player.velocity.x, 0.0); ////Here
            float intersectionHeight;
            if (tileIndex > 4) {
              intersectionHeight = intersection.size.height;
              player.onGround = YES; ////Here
            } else {
              intersectionHeight = -intersection.size.height;
            }
            player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y + intersection.size.height );
          } else {
            //tile is diagonal, but resolving horizontally
            float intersectionWidth;
            if (tileIndex == 6 || tileIndex == 0) {
              intersectionWidth = intersection.size.width;
            } else {
              intersectionWidth = -intersection.size.width;
            }
            //5
            player.desiredPosition = CGPointMake(player.desiredPosition.x  + intersectionWidth, player.desiredPosition.y);
          }
        }
      }
    }
  }
  //6
  player.position = player.desiredPosition;
}

Each time the Koala has a tile under him (either adjacently or diagonally) you set the player.onGround to YES and set his velocity to zero. Also, if the Koala has a tile adjacently above him, you set his velocity to zero. This will make the velocity variable properly reflect the Koala’s actual movement and speed.

You set the onGround flag to NO at the beginning of the loop. This way, the only time the onGround returns a YES is if you have detected a collision underneath the Koala. You can use this to know when the Koala can and cannot jump. You need to add this property to the Koala class, so do that now.

Add the following property to the header file in Player.h:

@property (nonatomic, assign) BOOL onGround;

Build and run. Is it working as expected? Yes! O frabjous day! Callooh! Callay!

Solid Ground

Where to Go From Here?

Congratulations! You’ve built yourself a working physics engine! If you’re through to this point, you can breathe a sigh of relief and pat yourself on the back. That was the hard part – nothing but smooth sailing in Part 2 of this tutorial!

Here’s the complete project you have built so far.

In Part 2, you’ll make your hero Koalio run and jump. You’ll also make the spikes on the floor dangerous, and handling winning/losing the game.

If you want more information about platformers and physics engines, here are a few resources I recommend:

Let me know how it’s going so far by submitting your comments to the forums!

Sprite Kit Tutorial: How to Make a Platform Game Like Super Mario Brothers – Part 1 is a post from: Ray Wenderlich

The post Sprite Kit Tutorial: How to Make a Platform Game Like Super Mario Brothers – Part 1 appeared first on Ray Wenderlich.

Sprite Kit Tutorial: How to Make a Platform Game Like Super Mario Brothers – Part 2

$
0
0
Learn how to make a game like Super Mario!

Learn how to make a game like Super Mario!

Update 2/2/14: I have updated this tutorial for Sprite Kit to celebrate the launch of the second edition of the Platformer Game Starter Kit, which is fully updated for Sprite Kit. Enjoy!

Welcome back to our 2-part Sprite Kit tutorial series on making a game like Super Mario!

In the first part of the series, you learned how to create a simple, tile-based physics engine that controls how the hero of your game, Koalio, moves around his world.

In this second and final part of the series, you’ll learn how to make Koalio run and jump – the fun part of the game!

You’ll also add collisions with those scary spikey floors, handle winning and losing, and of course add some gratuitous sound effects and music.

This second part is WAY simpler (and shorter) than the first tutorial, a reward for the hard work you put in last time. So turn your coding mojo on, and enjoy!

Moving Koalio Around

The controls you’re going to implement are very simple. There will be forward and jump controls only — much like 1-bit Ninja. If you touch the left half of the screen, Koalio will run forward. Touching the right half of the screen will make Koalio jump.

You heard me right – Koalio can’t move backwards! True Koalas don’t back down from danger.

Since Koalio will be moved forward by the user, rather than by the GameLevelScene, you need some values that you can check in the Player class to update his forward velocity. Add the following properties to the Player class:

In Player.h:

@property (nonatomic, assign) BOOL forwardMarch;
@property (nonatomic, assign) BOOL mightAsWellJump;

Now add the following touch-handling methods to the GameLevelScene.m:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  for (UITouch *touch in touches) {
    CGPoint touchLocation = [touch locationInNode:self];
    if (touchLocation.x > self.size.width / 2.0) {
      self.player.mightAsWellJump = YES;
    } else {
      self.player.forwardMarch = YES;
    }
  }
}
 
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  for (UITouch *touch in touches) {
 
    float halfWidth = self.size.width / 2.0;
    CGPoint touchLocation = [touch locationInNode:self];
 
    //get previous touch and convert it to node space
    CGPoint previousTouchLocation = [touch previousLocationInNode:self];
 
    if (touchLocation.x > halfWidth && previousTouchLocation.x <= halfWidth) {
      self.player.forwardMarch = NO;
      self.player.mightAsWellJump = YES;
    } else if (previousTouchLocation.x > halfWidth && touchLocation.x <= halfWidth) {
      self.player.forwardMarch = YES;
      self.player.mightAsWellJump = NO;
    }
  }
}
 
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
 
  for (UITouch *touch in touches) {
    CGPoint touchLocation = [touch locationInNode:self];
    if (touchLocation.x < self.size.width / 2.0) {
      self.player.forwardMarch = NO;
    } else {
      self.player.mightAsWellJump = NO;
    }
  }
}

These changes should be pretty straightforward. If the user creates a touch event that has an X-coordinate of less than half the screen’s width, you turn on the forwardMarch boolean for the player. Otherwise (if the location of the touch event is greater than that), simply turn on the mightAsWellJump boolean.

touchesMoved is a little more complicated, because you only want to change the boolean values above if the touch crosses mid screen, so you have to calculate the previousTouch location as well. Other than that, you’re just checking to see which direction the touch crosses and turning the appropriate boolean on or off. Finally, if the user stops touching the one side of the screen or the other, you want to turn the appropriate boolean value off.

There are a few changes that need to be made to detect touches. First, add this line to initWithSize:

self.userInteractionEnabled = YES;

Now that you are passing the touches through to your player class booleans, you can add some code to the update method so that Koalio can move. Start with the forward movement. Change the update method in Player.m to the following:

- (void)update:(NSTimeInterval)delta
{
  CGPoint gravity = CGPointMake(0.0, -450.0);
  CGPoint gravityStep = CGPointMultiplyScalar(gravity, delta);
  //1
  CGPoint forwardMove = CGPointMake(800.0, 0.0);
  CGPoint forwardMoveStep = CGPointMultiplyScalar(forwardMove, delta);
 
  self.velocity = CGPointAdd(self.velocity, gravityStep);
  //2
  self.velocity = CGPointMake(self.velocity.x * 0.9, self.velocity.y);
  //3
  //Jumping code goes here
  if (self.forwardMarch) {
    self.velocity = CGPointAdd(self.velocity, forwardMoveStep);
  }
  //4
  CGPoint minMovement = CGPointMake(0.0, -450);
  CGPoint maxMovement = CGPointMake(120.0, 250.0);
  self.velocity = CGPointMake(Clamp(self.velocity.x, minMovement.x, maxMovement.x), Clamp(self.velocity.y, minMovement.y, maxMovement.y));
 
  CGPoint velocityStep = CGPointMultiplyScalar(self.velocity, delta);
 
  self.desiredPosition = CGPointAdd(self.position, velocityStep);
}

Let’s break these new lines down section-by-section:

  1. You add a forwardMove force that will come into play while the user is touching the screen. As a reminder, you are scaling that force (800 points per second) to the appropriate amount for the current frame’s time step (delta) in order to have consistent acceleration.
  2. Here you apply a damping force to the horizontal velocity to simulate friction. You’re applying physics here just as you did with gravity. In each frame, additional movement force will be applied.


    When the force is removed, you want the player to come to a stop, but not immediately. Here you apply a 0.90 damping; in other words, reducing the overall horizontal force by ten percent each frame.

  3. In section three, you check for the boolean (meaning that the screen is being touched) and add the forwardMove force if appropriate.
  4. In section four, you apply the clamping. This limits the player’s maximum movement speed in both the horizontal (running top speed), upward (jumping speed) and downward (falling) directions.

    These damping and clamping values put limits on how quickly things happen in the game. It also prevents the buildup of velocity problem that you experienced in the first part of the tutorial.

    You want the player to have a maximum speed and to reach that speed within a second or less. This way your player’s movements still feel natural, and provide a level of control. The maximum force that you’ll allow is a positive 120 value, which would be one quarter the screen width per second.

    If you want to increase the rate of acceleration of your player, increase the forwardMove variable and the damping value 0.90 respectively. If you want to increase your player’s maximum speed, simply increase the 120 value. You’re also capping the jumping velocity at 250 and falling velocity at 450.

Build and run. You should be able to make Koalio run forward by pressing the left half of the screen. Watch that Koala go!

IMG_2644

Your Mac Will Make Him… Jump, Jump!

The jump is the distinguishing feature of the platformer, and the element that leads to most of the fun. You want to make sure that the jumping movement is fluid and feels right. In this tutorial, you’ll implement the jump algorithm used in Sonic the Hedgehog, as described here.

Add the following to the update method at the marked (//Jumping code goes here) line:

CGPoint jumpForce = CGPointMake(0.0, 310.0);
 
if (self.mightAsWellJump && self.onGround) {
    self.velocity = CGPointAdd(self.velocity, jumpForce);
}

If you stop here (go ahead and build and run if you like), you’ll get old school Atari jumping. Every jump the will be same height. You apply a force to the player, and wait until gravity pulls him back down again.

In modern platform games, users have much more control over the jumping. You want controllable, completely unrealistic (but fun as hell) Mario Bros/Sonic jumping abilities where you can change directions mid air and even stop a jump short.

To accomplish this, you’ll need to add the variable component. There are a couple ways to do it, but you’ll do it the Sonic way. Set the jump algorithm to reduce the force of the upward thrust if the user stops pressing the screen. Replace the above code with the following:

CGPoint jumpForce = CGPointMake(0.0, 310.0);
float jumpCutoff = 150.0;
 
if (self.mightAsWellJump && self.onGround) {
  self.velocity = CGPointAdd(self.velocity, jumpForce);
} else if (!self.mightAsWellJump && self.velocity.y > jumpCutoff) {
  self.velocity = CGPointMake(self.velocity.x, jumpCutoff);
}

This code performs one extra step. In the event that the user stops pressing the screen (self.mightAsWellJump will become NO), it checks the upward velocity of the player. If that value is greater than the cutoff, it will set the velocity to the cutoff value.

This effectively reduces the jump. This way, you’ll always get a minimum jump (at least as high as the jumpCutoff), but if you continue to hold, you’ll get the full jump force available.

Build and run now. This is starting to feel like a real game! From this point on, you’ll probably need to test on a real device instead of the simulator (if you haven’t been already) in order to use both “buttons.”

IMG_2645

You got Koalio running and jumping, but eventually he’ll run out of screen real estate. Time to fix that!

Add this snippet of code from the tile-based game tutorial to GameLevelScene.m:

- (void)setViewpointCenter:(CGPoint)position {
  NSInteger x = MAX(position.x, self.size.width / 2);
  NSInteger y = MAX(position.y, self.size.height / 2);
  x = MIN(x, (self.map.mapSize.width * self.map.tileSize.width) - self.size.width / 2);
  y = MIN(y, (self.map.mapSize.height * self.map.tileSize.height) - self.size.height / 2);
  CGPoint actualPosition = CGPointMake(x, y);
  CGPoint centerOfView = CGPointMake(self.size.width/2, self.size.height/2);
  CGPoint viewPoint = CGPointSubtract(centerOfView, actualPosition);
  self.map.position = viewPoint;
}

You also need to import the SKTUtils.h for this to work:

#import "SKTUtils.h"

This code clamps the screen to the position of the player. In the case where Koalio is at the edge of the level, it stops centering on him, and clamps the edge of the level to the edge of the screen.

There’s one modification here from the original post. In the last line, the map is moved, instead of the layer. This is possible because the player is a child of the map, so when the player moves right, the map moves left and the player remains in the center of the screen.

The touch methods rely on a position within the layer as well. If you moved your layer around instead, you would need to take those calculations into account. This is easier.

For a complete explanation, refer to the tile-based game tutorial.

You need to add that call to the update method:

[self setViewpointCenter:self.player.position];

Build and run now. You can navigate Koalio through the entire level!

IMG_2646

The Agony of Defeat

Now you can move on to handling the winning and losing game scenarios.

Tackle the losing scenario first. There are hazards placed in this level. If the player collides with a hazard, the game will end.

Since these are fixed tiles, you need to handle them like you handled the wall collisions in the previous tutorial. However, instead of resolving collisions, you’ll end the game. You’re in the home stretch now — there’s only a few things left to do!

Add the following method to GameLevelScene.m:

- (void)handleHazardCollisions:(Player *)player
{
  NSInteger indices[8] = {7, 1, 3, 5, 0, 2, 6, 8};
 
  for (NSUInteger i = 0; i < 8; i++) {
    NSInteger tileIndex = indices[i];
 
    CGRect playerRect = [player collisionBoundingBox];
    CGPoint playerCoord = [self.hazards coordForPoint:player.desiredPosition];
 
    NSInteger tileColumn = tileIndex % 3;
    NSInteger tileRow = tileIndex / 3;
    CGPoint tileCoord = CGPointMake(playerCoord.x + (tileColumn - 1), playerCoord.y + (tileRow - 1));
 
    NSInteger gid = [self tileGIDAtTileCoord:tileCoord forLayer:self.hazards];
    if (gid != 0) {
      CGRect tileRect = [self tileRectFromTileCoords:tileCoord];
      if (CGRectIntersectsRect(playerRect, tileRect)) {
        [self gameOver:0];
      }
    }
  }
}

All of this code should look familiar, since it’s copied and pasted from the checkForAndResolveCollisionsForPlayer:forLayer: method. The only method that’s new is gameOver. This call takes one parameter: 0 if the player has lost, 1 if the player has won.

You’re using the hazards layer instead of the walls layer, so you’ll need to set that up in the @interface at the beginning of the implementation file as property

@property (nonatomic, strong) TMXLayer *hazards;

Set it up in initWithSize (just after the walls setup line):

self.hazards = [self.map layerNamed:@"hazards"];

One last thing you need to do is call the method in update. Add this line after the call to checkForAndResolveCollisionsForPlayer:forLayer::

[self handleHazardCollisions:self.player];

Now, if the player runs into any tile from the hazards layer, you’ll call gameOver. That method is just going to throw up a restart button with a message that you’ve lost (or won, as the case may be):

-(void)gameOver:(BOOL)won {
  //1
  self.gameOver = YES;
  //2
  NSString *gameText;
  if (won) {
    gameText = @"You Won!";
  } else {
    gameText = @"You have Died!";
  }
 
  //3
  SKLabelNode *endGameLabel = [SKLabelNode labelNodeWithFontNamed:@"Marker Felt"];
  endGameLabel.text = gameText;
  endGameLabel.fontSize = 40;
  endGameLabel.position = CGPointMake(self.size.width / 2.0, self.size.height / 1.7);
  [self addChild:endGameLabel];
 
  //4
  UIButton *replay = [UIButton buttonWithType:UIButtonTypeCustom];
  replay.tag = 321;
  UIImage *replayImage = [UIImage imageNamed:@"replay"];
  [replay setImage:replayImage forState:UIControlStateNormal];
  [replay addTarget:self action:@selector(replay:) forControlEvents:UIControlEventTouchUpInside];
  replay.frame = CGRectMake(self.size.width / 2.0 - replayImage.size.width / 2.0, self.size.height / 2.0 - replayImage.size.height / 2.0, replayImage.size.width, replayImage.size.height);
  [self.view addSubview:replay];
}
 
- (void)replay:(id)sender
{
  //5
  [[self.view viewWithTag:321] removeFromSuperview];
  //6
  [self.view presentScene:[[GameLevelScene alloc] initWithSize:self.size]];
}
  1. The first line sets a new boolean called gameOver. You use this value to stop the update method from allowing the player to continue to move and interact with the level. You’ll see that in just a minute.
  2. Next, you assign a string indicating whether the player has won or lost.
  3. Then, the code creates a label, and assigns a string based on whether the user has won or lost. Sprite Kit has a handy SKNode subclass designed for labels like this.
  4. Finally, you create a UIButton that the user can tap to restart the level over again (calling the replay: method). This should look familiar to you if you’ve use UIButtons. The only thing that may look peculiar is that you set the tag property. This is so that you can look up the button later and remove it when you reload the scene.
  5. In the replay method, you first get the UIButton by its tag and remove it from the view. You don’t want that button hanging around during the next play session.
  6. Finally, you call presentScene on a newly allocated instance of the GameLevelScene, this reloads the scene with a fresh copy.

The only other thing you need to do is add the gameOver boolean to the GameLevelScene class. Add it to the @interface at the beginning of the GameLevelScene.m:

@property (nonatomic, assign) BOOL gameOver;

Add the following line to the beginning of the update method:

if (self.gameOver) return;

One last step. Drag replay.png and replay@2x.png from Resources\sprites.atlas to Resources in order to make a copy of the files that is outside sprites.atlas. This is necessary so that UIKit can find the image to display the UIButton (UIKit does not know anything about Sprite Kit texture atlases).

Go ahead and build and run now, and find some spikes to jump on! You should see something like this:

IMG_2647

Don’t repeat this too much though, or the animal protection agency might come after you! :]

The Pit of Doom

Now for the scenario where Koalio falls down a hole. In this case, you’ll end the game.

As the code is now, it will crash. (Horrors!) The code throws a TMXLayerInfo assertion error. That’s where you need to intervene. This occurs in the checkForAndResolveCollisionsForPlayer:forLayer: method when you call tileGIDAtTileCoord:.

Add the following code in GameLevelScene.m, in the checkForAndResolveCollisionsForPlayer:forLayer: method, after the CGPoint playerCoord = [layer coordForPoint:player.desiredPosition]; line;

if (playerCoord.y >= self.map.mapSize.height - 1) {
  [self gameOver:0];
  return;
}

This code will run the gameOver routine and abandon the process of building the tile array. You’ll abandon the collision loop as well and avoid any issues that might result from it.

The handleHazardCollisions also loops through the tiles. You need to add some code to that method to prevent it from doing so if the Koala has jumped down a hole. Add the following line to the beginning of that method:

if (self.gameOver) return;

Build and run now. Find a pit to jump into, and . . . no crashing! The game ends as it should.

IMG_2651

Winning!

Now, handle the case where your hero Koalio wins the game!

All you’re going to do is monitor the X-position of the player and trigger the “win” condition when he crosses the threshold (which will be at the end of the level). This level is about 3400 pixels wide. You’re going to trigger a “win” condition when the player gets to pixel 3130.

Add a new method in GameLevelScene.m as follows:

-(void)checkForWin {
  if (self.player.position.x > 3130.0) {
    [self gameOver:1];
  }
}

Add the checkForWin method after the handleHazardCollisions call in update:

[self checkForWin];

Build and run now. Navigate your hero Koalio through the level, and if you can make it to the end, you’ll have this message:

IMG_2652

Gratuitous Music and Sound Effects

You know what time it is – time for gratuitous music and sound effects!

Let’d dive right into it. You’ll be using the built in Sprite Kit methods for sound effects and the SKTAudio engine for the background music. Add this at the top of GameLevelScene.m:

#import "SKTAudio.h"

Then add this line to the initWithSize method of the GameLevelScene (before self.userInteractionEnabled = YES;):.

[[SKTAudio sharedInstance] playBackgroundMusic:@"level1.mp3"];

This gives you some nice gaming music. Thanks to Kevin Macleod of Incompetech.com for composing the music (Brittle Reel). He has tons of CC licensed music there!

Now add a jumping sound. Go to the Player.m and add the following to the update method inside the jumping code:

if (self.mightAsWellJump && self.onGround) {
  self.velocity = CGPointAdd(self.velocity, jumpForce);
  [self runAction:[SKAction playSoundFileNamed:@"jump.wav" waitForCompletion:NO]];
} else if (!self.mightAsWellJump && self.velocity.y > jumpCutoff) {
  self.velocity = CGPointMake(self.velocity.x, jumpCutoff);
}

Finally, play a sound when Koalio falls down a hole or when he hits a hazard. Do that in the gameOver method in GameLevelScene.m:

-(void)gameOver:(BOOL)won {
  self.gameOver = YES;
  [self runAction:[SKAction playSoundFileNamed:@"hurt.wav" waitForCompletion:NO]];
 
  NSString *gameText;

Build and run, and enjoy your groovy new tunes.

And, that’s it. You’ve written a platformer. You. Are. Awesome!

Where to Go From Here?

Here’s the final SuperKoalioPt2Fin with all of the code from this Sprite Kit tutorial.

There are a lot more things that could have been covered: everything from enemy collisions and AI, to enhanced movement abilities (wall slide, double jump, etc.), to level design guidelines.

And speaking of that… great news about that!

The Platformer Game Starter Kit

We just released the second edition of the Platformer Game Starter Kit, which is fully updated for Sprite Kit!

Here’s what you’ll learn in the Platformer Game Starter Kit:

  • How to manage and load multiple levels
  • How to make a scrollable, unlockable level selection screen
  • How easily Sprite Kit works with UIKit Storyboards
  • How to efficiently use sprite sheets, animations, tilesets, and work with pixel art!
  • How to create a state machine to handle character/enemy animations and behaviors
  • More about how to make amazing and fun tile-based physics engines!
  • How to create an on-screen joystick and HUD
  • How to add hardware controller support
  • Level design for platformers
  • How to build Enemy AI and dynamic behaviors.
  • Interviews with the developers of several top iOS platformer games with tips and tricks
  • . . . and lots, lots more!

You can check out the second edition of the Platformer Game Starter Kit on the raywenderlich.com store.

In the meantime, don’t forget the resources recommended at the end of Part 1 of this tutorial.

I hope you enjoyed getting your physics on and are more inspired than ever about building your own platformer!

Sprite Kit Tutorial: How to Make a Platform Game Like Super Mario Brothers – Part 2 is a post from: Ray Wenderlich

The post Sprite Kit Tutorial: How to Make a Platform Game Like Super Mario Brothers – Part 2 appeared first on Ray Wenderlich.

Reminder: Free Live Tech Talk on CocoaPods this Tuesday!

$
0
0
Free live tech talk on CocoaPods tomorrow!

Free live tech talk on CocoaPods tomorrow!

This is a reminder that we are having a free live tech talk on CocoaPods this Tuesday (tomorrow at the time of this post), and you’re all invited! Here are the details:

  • When: Tuesday, Feb 4 at 2:00 PM EST – 3:00 PM EST
  • What: CocoaPods Tech Talk followed by live Q&A (come w/ questions!)
  • Who: Cesare Rocchi and Orta Therox
  • Where: Google Hangouts Event Page
  • Why: For learning and fun!
  • How: Visit the event page and a video URL should be posted. Follow the instructions there to submit your Q&A (via text) as the talk runs.

We hope to see some of you at the tech talk, and we hope you enjoy!

Reminder: Free Live Tech Talk on CocoaPods this Tuesday! is a post from: Ray Wenderlich

The post Reminder: Free Live Tech Talk on CocoaPods this Tuesday! appeared first on Ray Wenderlich.

Automator for Mac OS X: Tutorial and Examples

$
0
0
Learn how to automate common developer tasks with Automator!

Learn how to automate common developer tasks with Automator!

As a software developer, there are a bunch of repetitive and tedious tasks you have to do almost every day. Wouldn’t it be great if there was a way to automate them?

Well, there is – by using the app Automator that is built right into Mac OX X.

In this tutorial, you will learn how to use Automator through five examples, which show you how to automate five common developer tasks:

  1. Xcode -> Text Editor. Create a service that allows you to highlight text from Xcode, then instantly copy it into a new file in your favorite text editor.
  2. Auto-resize images. Create a folder action that automatically resizes, copies and renames images placed in that folder, which prepares them for iOS development.
  3. SQLite queries. Create a SQL query tool for a selected SQLite file.
  4. CSV -> database. Create an application which allows you to drag and drop one or more CSV files into a database. Like magic, the contents of the CSV will automatically populate the selected table. Doesn’t that sound like a nice feature?
  5. Running shell commands. Create a workflow to run shell commands using variables.

By the end of this tutorial, you’ll have learned how you can improve your workflow to save a lot of time. That way you’ll have time for more important things… like catching a nap! :]

What is Automator?

AutomatorIcon

Automator first became a part of OS X at Tiger (10.4). From the beginning, it has been a way for users to automate repetitive tasks with Apple software, such as Safari, iTunes and Calendar. A number of third-party products, like Photoshop, Microsoft Office and many text editors are also compatible with Automator.

Its design is more user-friendly than writing code or running shell scripts, as it presents the user with an array of built-in options to use, and all within a visual interface. The concept behind the design is to make automation accessible without the need to write code.

What You Need

Here are the things you’ll need for this tutorial:

  • An Apple Computer running OS X 10.4 or later: This tutorial was produced in OS X 10.9 Mavericks, and some of the actions listed in here will not be available in older versions. The UI has had many changes since the original release.
  • Automator: This should be installed by default in OS, and as long as you’ve not moved it you’ll find it in your Applications folder.
  • Sample Files: For the fourth Sample project, you’ll have access to two files you can use, so go ahead and download them now. The second sample application also requires some image files on your Mac in order to test. Any image files will do – yes, even LOLcats! :]
LOLcat

Attribution: michellelevine

Getting Started

Open Automator from your Applications folder on your computer.

You’ll be greeted by a screen that asks what type of document you wish to create:

Screen Shot 2013-12-11 at 2.05.42 pm

You can get a description of how each document type works by clicking on it and reading the description that shows below the box. Later on in this lesson, you’ll learn more some of these types, but for now, select Workflow and click Choose.

You should now see the Automator UI:
Automator UI

  1. The hide/show library button expands and retracts panel 2 and 3. This feature gives you more room to work.


    The Media button gives you access to media files on your hard drive that you may wish to work with, including audio, image and video data. The buttons on the right side of the display allow you to Record a series of actions, such as button clicks, within an application. You can also Run your action workflow from here.

  2. In this section, you can toggle between actions and variables and use the search bar to narrow down the options. You can add an action to the workflow by dragging from 2 to 4 or double clicking the option in 2.
  3. This section tells you additional information about the action or variable selected in section 2. It’s useful because you can see exactly what inputs, options and outputs are available.
  4. This section is your workflow, it allows you to order how you want actions to occur in a procedural, top-to-bottom fashion. You can click and drag an action to re-order the workflow.
  5. In this section you can see a log output and the current variables in use.

For your first example, you’re going to create an Automator workflow to help you export code snippets to your favorite text editor from right within Xcode.

Opening Selected Text in Text Editor

Open up Xcode, select some text, and right click. A pop-up menu will appear, and one of the options in this menu is Services, which has a number of interesting things you can do with that text.

For example, in the screenshot below, if you chose New TextWrangler Document with Selection from the list, it would instantly create a new TextWrangler document that contains the selected text:

Services

Note: If there is no text selected, then the services menu will likely be empty. If you don’t see any services, then you probably don’t have any text selected. Try again!

Now, let’s say you have a favorite text editor that doesn’t have a service created for you already, like the TextWrangler example here. Even if you don’t have access to the internal API, you can still automate the copy/open/paste process by creating your own service using Automator.

Let’s give this a shot! In Automator, close your current document and create a new document with File\New. Then select Service and click Choose.

Then look for this box, which should appear at the top of your workflow pane:

automator workflow input

Since you selected a service, it will look for a usable variable to pass into the workflow. If you have a look in the first dropdown box, you’ll see a list of all the types of media that a service can handle:

variable types

For this project, leave it on text. In the second box, choose the applications you want the service to be available. Leave Any Application checked to allow text from anywhere to display this service.

Note: For future reference, you can restrict which applications can use a service in this same dropdown menu.

To do this, Other… from the drop down list and then locate the app you want from the Applications Folder.

Screen Shot 2013-12-15 at 10.10.02 pm

Make sure the Library is visible on the far left and Actions is selected:

Screen Shot 2013-12-15 at 10.09.32 pm

Click Library from the left column to un-filter the list of actions.

Type Copy into the search box to narrow down the list:

actions library

Find Copy to Clipboard in the list, click down on it and drag it from the actions library to the workflow area on the right.
workflow with action

Notice the triangle just below the entire selection dropdown field?
Screen Shot 2013-12-14 at 7.10.44 pm

This represents an output and data passing from one action to the next. The text collected by the service is passed onto the Copy to Clipboard action. When this service runs, it will copy the selected text to the clipboard.

Go back to the search box for our library and type launch.

Select Launch Application from the list and drag it to the right under your Copy to Clipboard action.
Screen Shot 2013-12-15 at 10.13.21 pm

This action has configurable settings; you can select which application to launch by using the dropdown list. This example uses TextWrangler (download it for free), but feel free to choose your favorite text editor instead.

workflow shot 2

So now your text is on the clipboard, and you’ve launched the application. What’s next? Now you just need to paste the text into the application.

But not too fast! You have a problem. There is no action called Paste from Clipboard. What are you going to do?

One option is to record the events of pasting text into TextWrangler, but why would you do that when you can use AppleScript?

Search and drag Run AppleScript from the actions list to the current workflow below Launch Application.

Note: While it would be fun to take a deep dive into AppleScript, it’s outside the scope of the topic. So, this course will cover some basic code examples, which will hopefully whet your appetite and inspire you to learn more about this useful feature.

In the Run AppleScript box replace the code with the following:

tell application "System Events" to keystroke "v" using {command down}

Your workflow should now look like this:

Untitled

The above is not a paste function, you’re telling the system to press command-v (which has the result of pasting the copied text into your text editor).

Before you test this out, it’s a good idea to make it more reliable, and there are a few ways to do this. Modify the code to the following:

tell application "TextWrangler"
	activate
	delay 1
	tell application "System Events" to keystroke "v" using {command down}
end tell

Be sure to replace the text “TextWrangler” with the name of the text editor you chose.

This makes sure sure your chosen text editor is running and is the active window before pasting your text. Specifically:

  • If you want to give an application multiple commands, you can combine them in a tell statement, as shown in the above code.
  • First, you tell your text editor to activate (become the active window). If the application is closed this command will open it, so you may remove the Launch Application action if you wish.
  • Delay tells the action to wait before proceeding to the next line. The number after the word delay represents the number of seconds to wait.

Now go to File, Save and select the name you wish to appear in your services list. In this example you can use “Open in TextWrangler”.

As services run automated procedures over the selected item(s), so it’s important to name them in a way that describes the action they perform. You’ll see a lot of actions start with “Add to” or “Open In” — some even clarify further by adding “with selection” to the end.

Trying it Out

Open up Xcode, create a new project (or open an existing project) and select some text.

Next, either right-click on the text or go up to the menu bar, select Xcode. In the Services menu you should now see your new service. Go ahead and run it.

Screen Shot 2013-12-12 at 11.47.43 am

If it all goes right, you should see your selected text in your text editor:

Screen Shot 2013-12-12 at 11.48.08 am

Note: If you chose a different text editor, you might have to instruct the text editor to create a new document before pasting the code. Here’s what I had to do to get this script to work with TextEdit:
tell application "TextEdit"
	activate
	delay 0.5
	tell application "System Events" to keystroke "n" using {command down}
	delay 0.5
	tell application "System Events" to keystroke "v" using {command down}
end tell

This makes a new document with Command-N, waits half a second, then pastes the text into the new document. Experiment with what best suits your needs!

So that’s how you can automate copying and pasting into application. Next up, you’ll learn how to use Automator to rename and auto-resize a batch of images for iOS projects.

Batch Renaming and Resizing Images

Apple woke up to the fact that preparing images for iOS development can result in poor quality images, so they have included the asset catalog in iOS 7 to make it easier to track image assets.

Note: To learn more about the asset catalog in iOS 7, check out Chapter 9 in iOS 7 by Tutorials, “What’s New in Xcode 5.”

You still need to create all of the required images, and it’s still a best practice to follow the previous naming conventions. What’s cool about Automator is that you can delegate the tedious task of processing and renaming to an automated action. Imagine how much easier image processing will be!

If you’ve been thinking about getting an app to help you resize and rename files, don’t do it! You can easily make one yourself with Automator.

There are a few ways to do this, but try using Folder Actions first. They allow you to set up a folder that runs a specific workflow on every file placed within.

Folder actions can revolutionize your workflow. Here are some examples of how you can use them:

  • Drag and drop a single file into a folder set up with an action of your choosing, and the workflow will automatically apply to that file.
  • Select multiple images, drag and drop them onto the folder and the workflow will work through each file in a loop sequence.
  • Use this folder as your default save location for files. This way you have a “set and forget” solution, so your files will always be prepped and ready to use when you go looking for them.

Creating a Folder Action

Open Automator and select New to create a new project. Select Folder Action and click Choose. and you’ll see the input at the top of the workflow is different again:

Screen Shot 2013-12-12 at 4.59.40 pm

Here you can choose the folder to apply this folder action to.

From the dropdown menu select Other… and go to the location you want your folder. If you don’t have a location in mind, just create it on the desktop for now. To do this, select New Folder and call it Prepare Images for iOS.

Open this folder via finder and add two more folders in it, one called Processed the other called Processed @2x, this is where photos will go after processing.

Your folder structure should now look like:
Screen Shot 2013-12-12 at 12.25.37 pm

Go back to the Automator project and add the Copy Finder Items action from the library to the workflow. Remember to use the search box if you’re having trouble finding it.

In the To: dropdown list select Other… and locate the folder named Processed @2x.

Your workflow should now look like this:

Untitled3

This moves the files from the Prepare Images for iOS folder to the Processed @2x subfolder.

Next, you’ll rename the file so that it has @2x at the end of the file name but before the file extension.

From your library add the action called Rename Finder Items. Make sure the dropdown box at the top of the action is set to Add Text. In the Add: area type @2x and set it to After Name. Note that there is a small text label below this section which gives you an example of how the filename will look after this modification:

Screen Shot 2013-12-12 at 5.10.09 pm

Go to File then Save, call it Prepare for iOS.

This is all you need do for full resolution images, but you’ll also need half resolution versions with the original filename.

Note: You can create multiple workflows and assign them to the same folder, and even select which actions to do for a given batch.

To try this out, go to your desktop and right-click the Prepare Images for iOS folder, go down to Services and select Folder Actions Setup…

Below is an example of a folder set up to run 2 workflows:

Screen Shot 2013-12-12 at 6.35.42 pm

Use the checkboxes to enable or disable folder actions, as well as the individual scripts. How simple is that?

But don’t get ahead of yourself! Work through this next exercise to make it work with a single workflow, and focus on learning about variables.

Using Automator Variables

Have a look at your variables tab, and you’ll see a bunch of standard information which can be collected from the system: date and time, system information, random numbers etc. You can drag each of these onto the workflow so its value is accessed via the output.
Screen Shot 2013-12-12 at 6.42.43 pm

Next, you’re going use a variable to store filename of the original image passed into the workflow, so that you preserve the original file.

Back in Automator in your current folder action project, add the Set Value of Variable action to your workflow at the top above Copy Finder Items.

Click New Variable… from the dropdown list and name it Original Input. Your workflow should now look like this:

Screen Shot 2013-12-12 at 6.50.28 pm

Now that the original filename is stored in a variable, you need to retrieve it from the variable after finishing with the @2x version.

Find Get Value of Variable from the actions list, drag it onto the bottom of your workflow, and make sure the Variable selected is Original Input.

All actions on the workflow return an output. The output of the “Rename Finder Items” step is the array of renamed files. However, in this case you don’t care about those filenames and just want to work with the array of files you squirreled away in the Original Input variable earlier.

To fix this, you can disconnect an action from previous outputs. Simply expand the Options of the Get Value of Variable and select Ignore this action’s input. When you make this change you’ll notice that it’s no longer connected to the output of the above action.

Screen Shot 2013-12-12 at 6.57.24 pm

Now you can finish this up by adding another Copy Finder Items action under the Get Value of Variable action and setting it to your Processed folder.

Then add a Scale Images action to your workflow, set it to scale by percentage and adjust the value to 50%.

Screen Shot 2013-12-12 at 6.59.26 pm

Go to File, select Save. You’re done – let’s try this out!

Find some images you can try out (such as that LOLcat image from earlier) and drag and drop them into the Prepare Images for iOS folder.

In your Prepare Images for iOS folder you can see the file(s) you dragged in:

Screen Shot 2014-01-24 at 11.03.45 AM

Have a look in your Processed @2x folder you’ll find the same images, but renamed with @2x – indicating the kitty is ready for retina. Meow.

Screen Shot 2014-01-24 at 11.04.29 AM

In your Processed folder, file names are unchanged. If you look at the dimensions you’ll find they are half the original dimensions:

Screen Shot 2014-01-24 at 11.04.33 AM

Well done! You now have full control over your image assets. Besides working with Retina, you can use this same process to prep images for iPad, iPhone and iWhatever. To do this, all you need is the ration or change for each device, and a single high resolution image.

Beyond that, you can even automate actions for photo manipulation. For example, you can add filters, like posterize and vignette. If you’re working on an image-rich project, look into using Automator – it’ll probably save time and sanity!

Perform SQL queries on an SQLite file using Automator

If you need to use a database within your app, the most likely format is SQLite. It’s light and compact, and is commonly used by Core Data. When you’re developing cross-platform apps, SQLite works great on Android. Using Automator can make managing the database more efficient.

Normally, you’d probably need buy an app to do tasks like set up sample data in your SQLite file, run a query to get data out of your SQLite database, or perform table maintenance. But not after you learn to use Automator, which can make performing SQL functions and queries on your SQLite databases easy!

To start, you’ll make a simple workflow which will allow you to run any queries you want on a selected database.

Note: If your library doesn’t have a Developer Tools category, it’s likely you don’t have Xcode installed on your Mac. Please install Xcode before you proceed with this part of the tutorial, as it uses Developer Tools.

In Automator, select File from the menu bar and then New. This time, select Workflow as the type, as you’ll only be running it from Automator.

Screen Shot 2014-01-24 at 11.19.02 AM

Since there is no file passed into the workflow at the beginning, you’ll have to get the file. In this example, you’ll be using the DataStore file from a Core Data project. Note that one is included in the sample files for this tutorial.

Note: If you would like to view your own Core Data database file, one easy way to get it is to enable Application supports iTunes file sharing in your apps Info.plist file. Run the app on your device and you should then see your DataStore.sqlite file from your iTunes fileshare. Save it to your desktop and you’re ready to go.

Drag the Get Specified Finder Items action onto your workflow, select Add and find the DataStore.sqlite file.

Screen Shot 2013-12-13 at 8.14.55 am

Now that you have the file in your workflow, you can start working with it.

Drag a Execute SQL action onto your workflow. This can run any SQL you want on the database.

Note: If you’re unfamiliar with SQL (Structured Query Language) and plan on using SQLite files a lot, then I would recommend trying w3schools SQL tutorial and our .

In the SQL commands section, you can run as many SQL commands as you want, separated with ;. For now though, you’ll run a query which will work on any Core Data database.

SELECT * FROM Z_PRIMARYKEY;

When this action runs, its output will be the result of the query. In this case it should contain one line for each entity model you’ve set up.

Now what you need is to be able to see the results returned by the query.

The “View Results” Action

So far you’ve made a lot of assumptions based on what you believe the current variables being passed to the output to be. If you’re working on much bigger workflows, or need instant feedback of what the current output is then this action is useful.

Drag the View Results action from the library onto your workflow under Execute SQL. You don’t need to put anything into this box as just a place to display information.

Click the Run button at the top right to see what happens.
Screen Shot 2013-12-13 at 8.42.51 am

You can now see the results of the SQL query in plain text.

Feel free to experiment with the View Results action to fully understand what the output of each action is. For example if you were to drag it above Execute SQL, the result would be something like:

(
"/Users/northy/Desktop/DataStore.sqlite"
)

The output of the Get Specified Finder Items action will be the filepaths of all files included in the action.

Note: Use with care, as it’s not unusual for View Results to change the format of a string before it reaches output. It’s also possible to just use the Results section at the bottom of each action.

Making a standalone application to add data from CSV to Database

There may be occasions where you need to add sample data to a database or multiple databases — like when you’re making a demo or testing a new app. You could use a Folder Action to handle this, but the sample project requires you to work with multiple files of different natures, specifically a CSV file and a SQLite database. This can become confusing when using a Folder Action, so for this lesson you’re going to make an Application.

Applications are just like every other application in your applications folder. They are single files which can take an input by drag and dropping a file or folder onto them:

dragndrop

The application you’re going to make takes a CSV file as its input (found in the sample files provided), and adds the data to the SQLite database you were just working with.

You’ll start by creating a table in the database for your sample data. The idea is your app will be a names database for smart people, so you need a table to store names. You’ll create the table by running a SQL create table query in the workflow you just created.

Replace the SQL in the Execute SQL action with the following:

create table people(firstname varchar, lastname varchar);

This query will create your table with the name people and will contain two fields: first name and last name.

The feedback you get in your view results action won’t tell you much. The green tick means there wasn’t any errors, so you can assume the table was created correctly:
Screen Shot 2013-12-13 at 4.35.31 pm

Now that you have added the People table to your database, you can create an application to automate adding CSV data.

In Automator, go to File then New from the menu bar. This time, select Application.

The file you’re going to pass into the application is a CSV file (comma delimited data files). They are text files where each line represents a new row of data and a comma separates each field. The one located in the sample file looks something like this:
Screen Shot 2013-12-13 at 4.46.10 pm

The CSV file will pass in as the first output to connect to. Next, you need to add a few actions to format the text, convert it to SQL and store it in a variable.

The first action to add is Combine Text Files:
Screen Shot 2013-12-13 at 4.51.38 pm

This is necessary in case you drag multiple CSV files into the application; it will loop through the contents of each text file and combine everything into one. Also, the file you drag into the application passes in its file path, so you want to make sure you’re getting the file’s contents.

Next, drag onto the workflow a Convert CSV to SQL action. You’ll see a segmented control which allows you to edit the text before, per row and after. Enter the following SQL on each:

Before:

begin transaction;

Per Row:

INSERT INTO people (firstname, lastname) values ("%{1}@","%{2}@");

After:

end transaction;

You have now written SQL instructions to insert rows into your table for each line in the CSV file, or, combined CSV data.

If you were to run this action on the sample names.CSV file, the resulting SQL will be as follows:

{"begin transaction;
INSERT INTO people (firstname, lastname) values (\"Ray\",\"Wenderlich\");
INSERT INTO people (firstname, lastname) values (\"Adam\",\"Burkepile\");
INSERT INTO people (firstname, lastname) values (\"Charlie\",\"Fulton\");
INSERT INTO people (firstname, lastname) values (\"Matt\",\"Galloway\");
end transaction;
"}

Next, add a View Results action to your workflow. While you don’t need to see the results at this point, you should be aware that at the time of creating this tutorial, there is a bug in the current version of Automator (2.4) where the variables may react strangely to certain text files. Having a View Results action seems to mitigate the bug.

Now add a Set Value of Variable action, create a new variable called SQL Statements.

The workflow should now look like:
Screen Shot 2013-12-13 at 5.10.13 pm

Now what you need to do is set it up so the user (which will be you in a moment) picks a database for the SQL, then apply the SQL to the chosen database.

Add an Ask for Finder Items action to your workflow, this will allow you to select the database where you wish to run the SQL statements.

Change its options to ignore this action’s input, change the prompt to Choose a SQLite Database: and set Start at: to a default location where you save SQLite files or leave it on Desktop.

Screen Shot 2014-01-13 at 6.54.41 pm

Drag a Set Value of Variable action underneath it and don’t bother setting the variable now; you’ll come back to it later.

Add a Get Value of Variable action, set the variable to SQL Statements and options to Ignore this Action’s Inputs.

Add an Apply SQL action, select New Variable from the Database dropdown list. Name the new variable SQLite Database.

Screen Shot 2013-12-15 at 11.12.48 pm

Now you can go back to the Set Value of Variable action (third action from the bottom of the workflow) and change it to SQLite Database.
Screen Shot 2013-12-13 at 5.41.53 pm

Congratulations, you’ve now made an application! Save the file somewhere on your Desktop for easy access.

To recap the workflow you just created:

  • You first bring in all CSV files dropped onto the application.
  • You then combine them into a single text string, and convert it into SQL statements, and save it in a variable.
  • Next, you ask the user to supply a database file and save the path to a variable.
  • Finally, you pass your SQL statements into the Apply SQL action, which runs on the provided SQLite file.

To test it out, drag and drop your names.csv file on top of the application you just saved.

dragndrop

It will then ask you to supply an SQLite database:
Screen Shot 2013-12-13 at 5.51.16 pm

That’s it! The process is done. How can you check that it has worked?

Go back to the previous workflow (the one you used to create the table) and change the SQL to:

SELECT * FROM people;

Then run the workflow:

Screen Shot 2013-12-13 at 5.53.55 pm

Look at you! You now have experience with all of the available SQL actions. Is your mind overflowing with ideas on how to use them to automate your database related projects? It should be!

So far you’ve only scratched the surface of what you can do with Automator actions. In the next and final practical example you’ll have another look at AppleScript, and how you can use outputs from your other actions.

Using Variables from your Workflow in AppleScript

It’s very easy to use Automator to collect variables, files, text, etc. Combine that with the power of AppleScript and you can do some pretty nifty stuff.

If you take a look at the standard data added to a Run AppleScript you’ll see the following:

on run {input, parameters}
	return input
end run

The input at the end of the Run Applescript action is actually an array of all data collected prior to this action in the workflow. To use these inputs, you need to assign them to a variable in your AppleScript.

set documentsDirectory to item 1 of input

You can assign variables with either set or copy, followed by the name of the variable. You can assign it a value straight away, above where you assign the value of the first item in the array of inputs.

If you have multiple variables, they are available based on their position in the workflow:
Screen Shot 2013-12-13 at 11.22.11 pm

The variables could be assigned as follows:

set documentsDirectory to item 1 of input
set computerUptime to item 2 of input
set currentWeekday to item 3 of input

Let’s try this out. You’ll be making a simple workflow that takes a variable (in this case your computer’s documents directory) and runs a command in terminal, based on the variable.

In Automator start a new project and select Workflow as the project type.

Select Variables in your library and search for Documents and drag it onto your workflow.

Screen Shot 2014-01-13 at 8.11.43 pm

Go back to your Actions library and drag a Run AppleScript action onto the workflow:

Screen Shot 2014-01-13 at 8.13.08 pm

Enter the following into your AppleScript text box:

on run {input, parameters}
 
	set documentsDirectory to item 1 of input
 
	tell application "Terminal"
		activate
		do script "cd " & documentsDirectory
	end tell
 
	return input
end run

Run the workflow. The current directory will change to your documents directory:
Screen Shot 2013-12-13 at 11.35.47 pm

To recap, you first instantiate a new variable called documentsDirectory which is then assigned to the first item in the array of inputs. You then tell your terminal application to activate it. Once activated, it will run the text string script which is made up of text and the documentsDirectory variable.

This is a very simple example but will give you some ideas about how you can expand it, and how AppleScript can become even more useful to you.

If you plan on doing a lot of shell command automation, then there is an action called Run Shell Script to make this process even more efficient.

Where To Go From Here?

Congratulations! You’re now familiar enough with Automator to create your own workflows and automate some of those tedious tasks.

You also had an introduction to the lightweight — but powerful — language of AppleScript. If you want to learn more about AppleScript, have a look at the official Applescript Documentation, which dives much deeper into the language.

If you want to see more practical examples using Automator or AppleScript, be sure to let us know.

Also, I would also love to hear about your workflows and the creative ways you’ve managed to save time, so be sure to leave a comment or two about your discoveries. Happy automating!

Automator for Mac OS X: Tutorial and Examples is a post from: Ray Wenderlich

The post Automator for Mac OS X: Tutorial and Examples appeared first on Ray Wenderlich.


Cocoa Design Patterns: The raywenderlich.com Podcast Episode 3

$
0
0
Episode 3: Cocoa Design Patterns

Episode 3: Cocoa Design Patterns

Good news – episode 3 of the raywenderlich.com podcast is now available! This time we chose the winner of our recent podcast topic poll: Cocoa Design Patterns.

[Subscribe in iTunes]

Here’s what is covered in this episode:

  • News: The name trademarking controversies (from Paper to Candy to the number 53)
  • What’s new in raywenderlich.com: Best new tutorials & big announcement next Monday
  • Tech Talk: Cocoa Design Patterns Discussion

Links and References

Our Sponsor

News

What’s New on raywenderlich.com

Tech Talk

Contact Us

Where To Go From Here?

We hope you enjoyed this podcast! We have an episode each month, so be sure to subscribe in iTunes to get access as soon as it comes out.

We’d love to hear what you think about the podcast, and any suggestions on what you’d like to hear in future episodes. Feel free to drop a comment here, or email us anytime at podcast@raywenderlich.com!

Cocoa Design Patterns: The raywenderlich.com Podcast Episode 3 is a post from: Ray Wenderlich

The post Cocoa Design Patterns: The raywenderlich.com Podcast Episode 3 appeared first on Ray Wenderlich.

CocoaPods Tech Talk Video

$
0
0

The first Tuesday of each month, one of the members of the team gives a Tech Talk, and by popular request we’ve started to stream these live.

Last night in our February Tech Talk, we were lucky enough to have two team members give a joint presentation on CocoaPods: Cesare Rocchi and Orta Therox.

Here’s the video for anyone who didn’t get a chance to watch!

Source Code

Here is the source code for the demo from the tech talk.

Helpful Links

Orta and Cesare mentioned a ton of helpful links in the talk – here is my best attempt to capture everything.

  • cocoapods.org: The official site
  • cocoapods.org/about: Learn about the team behind CocoaPods
  • guides.cocoapods.org: Tons of helpful guides on using CocoaPods
  • Podlife: App to give you notifications when CocoaPods are updated
  • cocoacontrols.com: Nice visual browser for open source libraries/controls
  • Alcatraz: Package manager referred to by Orta (but not working yet in Xcode 5 at the time of this blog post)
  • CocoaPods for Xcode: An Xcode plugin for CocoaPods
  • KFCocoaPodsPlugin: Another Xcode plugin for CocoaPods
  • MBProgressHUD: One of Cesare’s favorite CocoaPods; Displays a popup to indicate work being done on a background thread
  • ARAnalytics: One of Cesare’s favorite CocoaPods; Simplifies iOS Analytics, made by Orta
  • FLKAutoLayout: One of Orta’s favorite CocoaPods; Helper class for auto layout
  • ORStackView: One of Orta’s favorite CocoaPods; Easily create stack views with auto layout, made by Orta
  • FXBlurView: One of Orta’s favorite CocoaPods; Realtime blurs in iOS
  • Alex Curylo’s Favorite Pods: A list of good CocoaPods from a blogging buddy of mine
  • @_funkyboy: Follow Cesare on Twitter
  • @orta: Follow Orta on Twitter

Want to Join Us Next Month?

Thanks again Cesare and Orta for giving a great talk and having the guts to present to a live audience :] And thank you to everyone who attended – we hope you enjoyed it!

Next month, Brian Moakley, Marin Todorov, and myself will have some demos and an epic debate about Sprite Kit vs. Cocos2D vs. Unity 2D. Come join us to find out which is the “one true best” framework for making iOS games!

We will be broadcasting this talk live on Tuesday, March 4 at 2:00 PM EST, so if you want to join us sign up here! As you watch the talk, you can submit any Q&A you may have live.

Hope to see some of you there! :]

CocoaPods Tech Talk Video is a post from: Ray Wenderlich

The post CocoaPods Tech Talk Video appeared first on Ray Wenderlich.

Trigonometry for Game Programming [Sprite Kit Version]: Part 1/2

$
0
0

Learn Trigonometry for game programming!

Note from Ray: Tony has ported this popular tutorial from Cocos2D to Sprite Kit. We hope you enjoy!

Does the thought of doing mathematics give you cold sweats? Are you ready to give up on your career as a budding game developer because the math just doesn’t make any sense to you?

Don’t fret – math can be fun, and this cool 2-part game tutorial will back up that claim!

Here’s a little secret: as an app developer, you don’t really need to know a lot of math. If you can add or multiply two numbers together, you’re already halfway there. Most of the computations that we do in our professional lives don’t go much beyond basic arithmetic.

That said, for making games it is useful to have a few more math skills in your toolbox. You don’t need to become as smart as Archimedes or Einstein, but a basic understanding of trigonometry, combined with some common sense, will take you a long way.

In this tutorial, you will learn about some important trigonometric functions and how you can use them in your games. Then you’ll get some practice applying the theories by developing a simple spaces shooter iPhone game that requires a lot of trigonometry, using the Sprite Kit game framework.

Don’t worry if you’ve never used Sprite Kit before or are going to use a different framework for your game – the mathematics we’ll cover in this tutorial will apply to your engine no matter what. And you don’t need any prior experience, as I’ll walk through the process step-by-step.

If you supply the common sense, this tutorial will get you up to speed on the trigonometry, so let’s get started!

Getting Started: It’s All About Triangles

It sounds like a mouthful, but trigonometry simply means calculations with triangles (that’s where the tri comes from).

You may not have realized it until now, but games are full of triangles. For example, imagine you have a spaceship game, and you want to calculate the distance between these ships:

Distance between ships

You have X and Y position of each ship, but how can you find the length of that line?

Well, you can simply draw a line from the center point of each ship to form a triangle like this:

Then, since you know the X and Y coordinates of each ship, you can compute the length of each of the new lines. Now that you know the lengths of two sides of the triangle, you can use some Trigonometry to compute the length of the diagonal line – the distance between the ships.

Note that one of the corners of this triangle has an angle of 90 degrees. This is also known as a right triangle, and that’s the sort of triangle you’ll be dealing with in this tutorial.

Any time you can express something in your game as a triangle with a 90-degree right angle – such as the spatial relationship between the two sprites in the picture – you can use trigonometric functions to do calculations on them.

So in summary, Trigonometry is the mathematics that you use to calculate the lengths of the sides of these triangles, as well as the angles between those sides. And that comes in handy more often that you might think.

For example, in this space ship game you might want to:

  • Have one ship shoot a laser in the direction of the other ship
  • Have one ship start moving in the direction of another ship to chase
  • Play a warning sound effect if an enemy ship is getting too close

All of this and more you can do with the power of Trigonometry!

Your Arsenal of Functions

First, let’s get the theory out of the way. Don’t worry, I’ll keep it short so you can get to the fun coding bits as quickly as possible.

These are the parts that make up a right triangle:

In the picture above, the slanted side is called the hypotenuse. It always sits across from the corner with the 90-degree angle (also called a right angle), and it is always the longest of the three sides.

The two remaining sides are called the adjacent and the opposite, as seen from one particular corner of the triangle, the bottom-left corner in this case.

If you look at the triangle from the other corner (top-right), then the adjacent and opposite change places:

Alpha (α) and beta (β) are the names of the two other angles. You can call these angles anything you want (as long as it sounds Greek!) but usually alpha is the angle in the corner of interest and beta is the angle in the opposing corner. In other words, you label your opposite and adjacent sides with respect to alpha.

The cool thing is that if you only know two of these things, trig allows you to find out all the others using the sine, cosine and tangent functions. For example, if you know an angle and the length of one of the sides, then the sine, cosine and tangent functions can tell you the length of the other sides:

Think of the sin, cos, and tan functions as “black boxes” – you plug in numbers and get back results. They are pre-written functions you can call from almost any programming language.

Know Angle and Length, Need Sides

Let’s consider an example. Say you know the alpha angle between the ships is 45 degrees, and the length between the ships (the hypotenuse) is 10 points long. You can then plug these values into the formula:

sin(45) = opposite / 10

To solve this for the hypotenuse, you shift things around a bit:

opposite = sin(45) * 10

If you call the built-in sin function, you’ll find the sine of 45 degrees is 0.707 (rounded off), and filling that in that gives you the result:

opposite = 0.707 * 10 = 7.07

There is a handy mnemonic for remembering what these functions do that you may remember from high school: SOH-CAH-TOA (where SOH stands for Sine is Opposite over Hypotenuse, and so on), or if you need something more catchy: Some Old Hippy / Caught Another Hippy / Tripping On Acid. (That hippy was probably a mathematician who did a little too much trig.) :]

Know 2 Sides, Need Angle

The above formulas are useful when you already know an angle, but that is not always the case – sometimes it is the angle you are looking for. Then you need to know the lengths of at least two of the sides and plug these into the inverse functions:

Inverse trig functions

  • angle = arcsin(opposite/hypotenuse)
  • angle = arccos(adjacent/hypotenuse)
  • angle = arctan(opposite/adjacent)

In other words, if sin(a) = b, then it is also true that arcsin(b) = a. Of these inverse functions, you will use the arc tangent (arctan) the most in practice. Sometimes these functions are also notated as sin-1, cos-1, and tan-1, so don’t let that fool you.

Is any of this sinking in or sounding familiar? Good, but you’re not done yet with the theory lesson – there is still more that you can calculate with triangles.

Know 2 Sides, Need Remaining Side

Sometimes you may know the length of two of the sides and need to know the length of the third, like in the example at the beginning of this tutorial where you wanted to find the distance between the two space ships.

This is where Trigonometry’s Pythagorean Theorem comes to the rescue. Even if you forgot everything else about math, this is probably the one formula you do remember:

a2 + b2 = c2

Or, put in terms of the triangle that you saw earlier:

opposite2 + adjacent2 = hypotenuse2

If you know any two of these sides, then calculating the third is simply a matter of filling in the formula and taking the square root. This is a very common thing to do in games and you’ll be seeing it several times in this tutorial.

Note: Want to drill this formula into your head while having a great laugh at the same time? Search YouTube for “Pythagoras song” – it’s an inspiration for many!

Have Angle, Need Other Angle

Lastly, the angles. If you know one of the non-right angles from the triangle, then figuring out the other ones is a piece of cake. In a triangle, all angles always add up to a total of 180 degrees. Because this is a right triangle, you already know that one of the angles is 90 degrees. That leaves:

alpha + beta + 90 = 180

Or simply:

alpha + beta = 90

The remaining two angles must add up to 90 degrees. So if you know alpha, you also know beta, and vice-versa.

And those are all the formulas you need to know! Which one to use in practice depends on the pieces that you already have. Usually you either have the angle and the length of at least one of the sides, or you don’t have the angle but you do have two of the sides.

Enough theory. Let’s put this stuff into practice.

To Skip, or Not to Skip?

In the next few sections, you will be setting up a basic Sprite Kit project with a space ship that can move around the screen using the accelerometer. This won’t involve any trigonometry (yet), so if you already know Sprite Kit and feel like this guy:

"F that!" guy

Then feel free to skip ahead to the Begin the Trigonometry! section below – I have a starter project waiting for you there.

But if you’re the type who likes to code everything from scratch, keep reading! :]

Creating the Project

First make sure you have Xcode 5 or later, since it is the first version of Xcode to include Sprite Kit.

Next start up Xcode, select File\New\Project…, choose iOS\Application\SpriteKit Game template, and click Next:

Select SpriteKit Template

Name the project TrigBlasterSK and set the device family to iPhone. Build and run the template as-is. If all works OK, you should see the following:

HelloWorld

Download the resources for this tutorial. This file contains the images for the sprites and the sound effects. Unzip it and drag the art folder into Xcode to add it to the project. In the import dialog, make sure Copy items into destination group’s folder is checked.

DragArtIntoProject

Import Resources Into Project

Great, the preliminaries are over and done with – now let’s get coding for real.

Steering with Accelerometers

Because this is a simple game, you will be doing all your work inside a single class: the MyScene.m. Right now, this scene contains a bunch of stuff that you don’t need. For starters, it does not start up with the correct orientation so let’s fix that first.

Switching to Landscape Orientation

First, open your target setting by clicking your TrigBlasterSK project in the Project Navigator, selecting the TrigBlasterSK target. Then, in the Deployment Info section make sure General is checked at the top, under Device Oritentation uncheck all but Landscape Right as shown below:

Landscape Settings

If you build and run, the app will launch in Landscape orientation but its graphic context is still not landscape. The details of this are covered in our space game tutorial, but for now you need to have the ViewController.m file changed a bit so the start up code is executed later in the process.

Select the ViewController.m and replace the viewDidLoad method with the following:

- (void)viewDidLoad
{
    [super viewDidLoad];
}
 
-(void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    // Configure the view.
    // Configure the view after it has been sized for the correct orientation.
    [self startScene];
}
 
- (void)startScene
{
    SKView *skView = (SKView *)self.view;
    if (!skView.scene) {
        skView.showsFPS = YES;
        skView.showsNodeCount = YES;
 
        // Create and configure the scene.
        MyScene *theScene = [MyScene sceneWithSize:skView.bounds.size];
        theScene.scaleMode = SKSceneScaleModeAspectFill;
 
        // Present the scene.
        [skView presentScene:theScene];
    }
}
 
- (BOOL)prefersStatusBarHidden
{
    return YES;
}

The above code moved the scene creation to the viewWillLayoutSubviews method where the layout orientation is known. There is also an added method at the bottom prefersStatusBarHidden that will cause the status bar to be removed from your game while it is running-giving more realism during play.

Replace the contents of MyScene.m with:

#import "MyScene.h"
 
@implementation MyScene
 
-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
 
        self.backgroundColor = [SKColor colorWithRed:94.0/255.0 green:63.0/255.0 blue:107.0/255.0 alpha:1.0];
 
    }
    return self;
}
 
 
-(void)update:(NSTimeInterval)currentTime {
    /* Called before each frame is rendered */
}
 
@end

Build and run, and you should see nothing but purple:

purple-background

Let’s make things a bit more exciting by adding a spaceship to the scene. You will need some instance variables for this, so modify the @implementation line in MyScene.m as follows:

@implementation MyScene
{
    CGSize _winSize;
    SKSpriteNode *_playerSprite;
}

The _winSize variable stores the dimensions of the screen, which is useful because you will be referring to that quite often. The _playerSprite variable holds the spaceship sprite.

Now change initWithSize to the following:

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
 
        self.backgroundColor = [SKColor colorWithRed:94.0/255.0 green:63.0/255.0 blue:107.0/255.0 alpha:1.0];
 
        _winSize = CGSizeMake(size.width, size.height);
 
        _playerSprite = [SKSpriteNode spriteNodeWithImageNamed:@"Player"];
        _playerSprite.position = CGPointMake(_winSize.width - 50.0f, 60.0f);
        [self addChild:_playerSprite];
    }
    return self;
}

This is all pretty basic if you have worked with Sprite Kit before. The player sprite is positioned in the bottom-right corner of the screen. Remember that with Sprite Kit, it is the bottom of the screen that has y-coordinate 0, unlike in UIKit, where y = 0 points to the top of the screen. You have moved the x-coordinate to 60.0 to get it above the status text being shown.

Build and run to try it out, and you should see the following:

spaceship-bottom-right-corner

To move the spaceship, this game uses the iPhone’s built-in accelerometers. Unfortunately, the iOS Simulator cannot simulate the accelerometers. That means that from now on, you will need to run the app on your device to test it.

Note: If you are unsure how to put the app on your device, check out this extensive tutorial that explains how to obtain and install the certificates and provisioning profiles that allow Xcode to run apps on your device. It’s not as intimidating as it looks, but you do need to sign up for the paid Apple developer program.

To move the spaceship with the accelerometers, you’ll obviously be rocking your device from side to side. During testing I found that this sometimes caused the screen to autorotate from landscape right to landscape left, which is really annoying when you’re in the middle of a heated battle.

This was the reason you used the Project Settings\General screen and in the Device Orientation, de-selected all options except for Landscape Right earlier.

Using the accelerometers from code is pretty straightforward through the new CoreMotion framework. There are two ways to get accelerometer data: pushed to your application at a specific frequency, and where you request the values when you need them. Apple recommends not having data pushed to your application unless timing is very critical (like a measurement or navigation service) since it can drain the batteries faster.

Your game has a natural place to read the accelerometer data, that’s in the update method that gets called by Sprite Kit for every loop. You will grab the these values to change the speed of the spaceship in this method.

You need to do some setup to use core motion but it is pretty easy.

First declare your intent to use the CoreMotion framework and bring it in through the new @import syntax. On the line before your #import "MyScene.h" add the following line:

@import CoreMotion;

This new syntax tells Xcode you wish to use the CoreMotion framework and also to expose the .h headers for you. Pretty neat, compared to the old way of accessing external frameworks.

Next you need some instance variables to keep track of the accelerometer values. You only store the values for two of the accelerometers; the “Z” one isn’t used by this game. Add the following lines to your implementation area:

@implementation MyScene
{
    . . .
    UIAccelerationValue _accelerometerX;
    UIAccelerationValue _accelerometerY;
 
 
    CMMotionManager *_motionManager;
}

Now create a couple of utility methods for using the accelerometers and to cleanup should we exit the game. Underneath the initWithSize method add the following methods:

- (void)dealloc
{
    [self stopMonitoringAcceleration];
    _motionManager = nil;
}
 
- (void)startMonitoringAcceleration
{
    if (_motionManager.accelerometerAvailable) {
        [_motionManager startAccelerometerUpdates];
        NSLog(@"accelerometer updates on...");
    }
}
 
- (void)stopMonitoringAcceleration
{
    if (_motionManager.accelerometerAvailable && _motionManager.accelerometerActive) {
        [_motionManager stopAccelerometerUpdates];
        NSLog(@"accelerometer updates off...");
    }
}

The start and stop methods check to make sure the capability is present on the device and if so tell it to start gathering data. The stop method will be called when you wish to turn off the data gathering.

A good place to activate the accelerometers is inside initWithSize. Add the following line to it underneath the [self addChild:_playerSprite]; line:

_motionManager = [[CMMotionManager alloc] init];
[self startMonitoringAcceleration];

Next, add the method that will be called to read the values and let your player change positions:

- (void)updatePlayerAccelerationFromMotionManager
{
    const double FilteringFactor = 0.75;
 
    CMAcceleration acceleration = _motionManager.accelerometerData.acceleration;
    _accelerometerX = acceleration.x * FilteringFactor + _accelerometerX * (1.0 - FilteringFactor);
    _accelerometerY = acceleration.y * FilteringFactor + _accelerometerY * (1.0 - FilteringFactor);
 
}

This bit of logic is necessary to filter – or “smooth” – the data that you get back from the accelerometers so that it appears less jittery.

Note: An accelerometer records how much gravity currently pulls on it. Because the user is holding the iPhone in her hands, and hands are never completely steady, there are a lot of tiny fluctuations in this gravity value. We are not so much interested in these unsteady motions as in the larger changes in orientation that the user makes to the device. By applying this simple low-pass filter, you retain this orientation information but filter out the less important fluctuations.

Now that you have a stable measurement of the device’s orientation, how can you use this to make the player’s spaceship move?

Movement in games often works like this:

  1. Set the acceleration based on some form of user input, in this case from the accelerometer values.
  2. Add the new acceleration to the spaceship’s current speed. This makes the object speed up or slow down, depending on the direction of the acceleration.
  3. Add the new speed to the spaceship’s position to make it move.

Of course, you have a great mathematician to thank for these equations: Sir Isaac Newton!

You need some more instance variables to pull this off. There is no need to keep track of the player’s position because the SKSpriteNode already does that for you, but acceleration and speed are your responsibility.

Add these instance variables:

@implementation MyScene
{
    . . .
    float _playerAccelX;
    float _playerAccelY;
    float _playerSpeedX;
    float _playerSpeedY;
 
}

It’s good to set some bounds on how fast the spaceship can travel or it would be pretty hard to maneuver. Infinite acceleration sounds like a good idea in theory but in practice it doesn’t work out so well (besides, even Einstein thinks there are limits to how fast you can go).

Add the following lines directly below the #import statement:

const float MaxPlayerAccel = 400.0f;
const float MaxPlayerSpeed = 200.0f;

This defines two constants, the maximum acceleration (400 points per second squared) and the maximum speed (200 points per second).

Now add the following bit of logic to the bottom of updatePlayerAccelerationFromMotionManager:

if (_accelerometerY > 0.05)
{
    _playerAccelX = -MaxPlayerAccel;
}
else if (_accelerometerY < -0.05)
{
    _playerAccelX = MaxPlayerAccel;
}
if (_accelerometerX < -0.05)
{
    _playerAccelY = -MaxPlayerAccel;
}
else if (_accelerometerX > 0.05)
{
    _playerAccelY = MaxPlayerAccel;
}

This is a basic technique for controlling a sprite using the accelerometers. When the device is tilted to the left, you give the player maximum acceleration to the left. Conversely, when the device is tilted to the right, you give the player maximum acceleration to the right. Likewise for the up and down directions.

Note: You’re using the _accelerometerY value for the x-direction. That’s as it should be. Remember that this game is in landscape, so the Y-accelerometer runs from left to right in this orientation, and the X-accelerometer from top to bottom.

You’re almost there. The last step is applying the _playerAccelX and Y values to the speed and position of the spaceship. You will do this in the game’s update method. This method is called 60 times per second, so it’s the natural place to perform all of the game logic.

Add updatePlayer to the class:

- (void)updatePlayer:(NSTimeInterval)dt
{
    // 1
    _playerSpeedX += _playerAccelX * dt;
    _playerSpeedY += _playerAccelY * dt;
 
    // 2
    _playerSpeedX = fmaxf(fminf(_playerSpeedX, MaxPlayerSpeed), -MaxPlayerSpeed);
    _playerSpeedY = fmaxf(fminf(_playerSpeedY, MaxPlayerSpeed), -MaxPlayerSpeed);
 
    // 3
    float newX = _playerSprite.position.x + _playerSpeedX*dt;
    float newY = _playerSprite.position.y + _playerSpeedY*dt;
 
    // 4
    newX = MIN(_winSize.width, MAX(newX, 0));
    newY = MIN(_winSize.height, MAX(newY, 0));
 
    _playerSprite.position = CGPointMake(newX, newY);
}

If you’ve programmed games before (or studied physics), then this should look very familiar. Here’s how it works:

  1. Add the current acceleration to the speed.

    The acceleration is expressed in points per second (actually, per second squared, but don’t worry about that) but update: is performed a lot more often than once per second. To compensate for this difference, you multiply the acceleration by the elapsed or “delta” time, dt. Without this, the spaceship would move about sixty times faster than it should!

  2. Clamp the speed so that it doesn’t go faster than MaxPlayerSpeed if it is positive or -MaxPlayerSpeed if it is negative. You could write this using an if statement instead:

    if (_playerSpeedX < -MaxPlayerSpeed) 
    {
        _playerSpeedX = -MaxPlayerSpeed;
    }
    else if (_playerSpeedX > MaxPlayerSpeed) 
    {
        _playerSpeedX = MaxPlayerSpeed;
    }

    However, I like the succinctness of the one-line version. fminf() makes sure the speed doesn’t become larger than MaxPlayerSpeed because it always picks the smallest of the two, while fmaxf() makes sure the speed doesn’t drop below -MaxPlayerSpeed because it always picks the largest value. It’s a neat little trick.

  3. Add the current speed to the sprite’s position. Again, speed is measured in points per second, so you need to multiply it by the delta time to make it work correctly.
  4. Clamp the new position to the sides of the screen. You don’t want the player’s spaceship to go offscreen, never to be found again!

One more thing, we need to measure time as differences (deltas) in time. The Sprite Kit update method gets called repeatedly with the current time. We will need to track the delta time between calls to the update method ourselves, so that your velocity calculations are smooth.

To track “delta” time add two more instance variables:

@implementation MyScene
{
    . . .
    NSTimeInterval _lastUpdateTime;
    NSTimeInterval _deltaTime;
}

Then replace the update stub with the actual implementation:

-(void)update:(NSTimeInterval)currentTime {
    /* Called before each frame is rendered */
 
    //To compute velocities we need delta time to multiply by points per second
    //SpriteKit returns the currentTime, delta is computed as last called time - currentTime
    if (_lastUpdateTime) {
        _deltaTime = currentTime - _lastUpdateTime;
    } else {
        _deltaTime = 0;
    }
    _lastUpdateTime = currentTime;
 
    [self updatePlayerAccelerationFromMotionManager];
    [self updatePlayer:_deltaTime];
}

That should do it.

The updatePlayerAccelerationFromMotionManager is called to calculate the players acceleration from the accelerometer values and save/clamp them.

Finally, the updatePlayer method is called to move the ship, making use of the delta time to compute the velocity.

Build and run the game on an actual device (not the simulator). You can now control the spaceship by tilting the device:

accelerometer

Begin the Trigonometry!

If you skipped ahead to this section, here is the starter project at this point. Build and run it on your device – you’ll see there’s a spaceship that you can move around with the accelerometer.

You haven’t seen any trigonometry yet, so let’s put some into action.

It would be cool – and much less confusing to the player! – to rotate the spaceship in the direction it is currently moving rather than having it always pointing upward.

To rotate the spaceship, you need to know the angle to rotate it. But you don’t know what that is, so how can you figure that out?

Let’s think about what you do know. You do have the player’s speed, which consists of two components: a speed in the x-direction and a speed in the y-direction:

If you rearrange these a little, you can see that they form a triangle:

Here you know the lengths of the adjacent (_playerSpeedX) and the opposite (_playerSpeedY) sides.

So basically, you know 2 sides of a right triangle, so you are in the Know 2 Sides, Need Angle case mentioned in the beginning of this tutorial. That means you need to use one of the inverse functions: arcsin, arccos or arctan.

You know the opposite and adjacent sides, so you want to use the arctan function to find the angle to rotate the ship. Remember, that looks like the following:

angle = arctan(opposite / adjacent)

The Objective-C math library comes with the atan() function that computes the arc tangent, but it has one big issue: what if the x-speed is 0? In that case, the adjacent is 0 and dividing by 0 is mathematically undefined. Your app might crash or just behave weirdly when that happens.

Instead of using atan(), it is better to use the function atan2(), which takes two parameters and correctly handles the division-by-zero scenario without you having to worry about it:

angle = atan2(opposite, adjacent)

Important: The angle that atan2f() gives you is not the inner angle inside the triangle, but the angle that the hypotenuse makes with that 0-degree line:

This is another reason why atan2() is a lot more useful than plain, old atan().

So let’s give that a shot. Add the following two lines to the bottom of updatePlayer:

float angle = atan2f(_playerSpeedY, _playerSpeedX);
_playerSprite.zRotation = angle;

Note that the arctan function you are using is actually called atan2f() instead of just atan2(). More about that in a moment. Also notice that the y-coordinate goes first. A common mistake is to write atan2f(x, y) but that’s the wrong way around. Remember the first parameter is the opposite, and in this case the Y coordinate is the opposite.

Build and run the app to try it out:

Not rotating correctly

Hmm, this doesn’t seem to be working quite right. What is wrong here?

Radians, Degrees and Points of Reference

Normal human beings tend to think of angles as values between 0 and 360. Mathematicians, however, measure angles in radians, which are expressed in terms of π (the Greek letter Pi, which sounds like pie but doesn’t taste as good).

It’s not essential to understand, but if you’re curious, one radian is the angle you get when you travel the distance of the radius along the arc of the circle. You can do that 2π times (roughly 6.28 times) before you end up at the beginning of the circle again.

So while you may think of angles as values from 0 to 360, a mathematician sees values from 0 to 2π. Luckily for us Sprite Kit uses radians for its angles as well. The atan2f() function returns a value in radians but the direction we are pointing is off by 90 degrees.

Since you will be working with both radians and degrees, put these helper macros in that make this easy. At the top of your MyScene.m file, underneath the #import "MyScene.h" add the following two defines:

#define SK_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) * 0.01745329252f) // PI / 180
#define SK_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) * 57.29577951f) // PI * 180

I don’t have these formulas memorized but they are easy enough to derive yourself when you realize than an angle of 360 degrees corresponds to 2π:

Angle in degrees / 360 degrees = Angle in radians / 2π radians

To go from radians to degrees, the formula becomes:

Angle in degrees = (Angle in radians / 2π) * 360

And the other way around, from degrees to radians:

Angle in radians = (Angle in degrees / 360) * 2π

I point this out because forgetting to convert between radians and degrees is probably the most common mistake programmers make when they are dealing with trigonometry! So if you don’t see the rotation you expected, make sure you’re not mixing up your degrees and radians…

So what the @#! is π? Pi is the ratio of the circumference of a circle to its diameter. In other words, if you measure the outside of any circle and divide it by its diameter (which is twice the radius), you get the number 3.141592… and that is what we call π. It takes 2π radians to go all the way around a circle, which is why 360 degrees corresponds to 2π. Pi is a very important concept in mathematics that shows up all the time, especially with anything that is cyclical.

By the way, not everyone likes π. Some people believe that it would have been better to choose the ratio of the circumference to the radius, instead of to the diameter. This number is called tau (τ) and is equal to 2π, thus simplifying many of the calculations. You can read more about tau at tauday.com.

Anyway, back to the game. Build and run to see some glorious rotation in action.

Whoops, something is still not right. The spaceship certainly rotates but it seems to be pointing in the direction opposite to where it’s flying!

Here’s what’s happening: the sprite for the spaceship points straight up, which corresponds to the default rotation value of 0 degrees/radians. But in mathematics, an angle of 0 degrees (or radians) doesn’t point upward, but to the right:

Rotation-differences

To overcome these differences from sprite orientation, change the line to:

_playerSprite.zRotation = angle - SK_DEGREES_TO_RADIANS(90);

This subtracts 90 degrees (after getting degrees to radians to subtract from the angle in radians) to make the sprite point to the right at an angle of 0 degrees, so that it lines up with the way atan2f() does things.

Build and run once more to try it out. You’ll finally have a spaceship that has its head on straight!

Spaceship flying with correct rotation

Bouncing Off the Walls

You have a spaceship that you can move using the accelerometers and you’re using trig to make sure it points in the direction it’s flying. That’s a good start.

Unfortunately, having the spaceship get stuck on the edges of the screen isn’t very satisfying. You’re going to fix that by making it bounce off the screen borders instead.

First, comment these lines from updatePlayer:

//newX = MIN(_winSize.width, MAX(newX, 0));
//newY = MIN(_winSize.height, MAX(newY, 0));

Then add the following code to updatePlayer, just after the commented lines:

BOOL collidedWithVerticalBorder = NO;
BOOL collidedWithHorizontalBorder = NO;
 
if (newX < 0.0f)
{
    newX = 0.0f;
    collidedWithVerticalBorder = YES;
}
else if (newX > _winSize.width)
{
    newX = _winSize.width;
    collidedWithVerticalBorder = YES;
}
 
if (newY < 0.0f)
{
    newY = 0.0f;
    collidedWithHorizontalBorder = YES;
}
else if (newY > _winSize.height)
{
    newY = _winSize.height;
    collidedWithHorizontalBorder = YES;
}

This checks whether the spaceship hit any of the screen borders and if so, sets a BOOL variable to YES. But what to do after such a collision takes place? To make the spaceship bounce off the border, you can simply reverse its speed and acceleration.

First, define a constant at the top of the file, right below the const float MaxPlayerSpeed = 200.0f; line:

const float BorderCollisionDamping = 0.4f;

Add the following lines in updatePlayer, directly below the code you last added there:

if (collidedWithVerticalBorder)
{
    _playerAccelX = -_playerAccelX * BorderCollisionDamping;
    _playerSpeedX = -_playerSpeedX * BorderCollisionDamping;
    _playerAccelY = _playerAccelY * BorderCollisionDamping;
    _playerSpeedY = _playerSpeedY * BorderCollisionDamping;
}
 
if (collidedWithHorizontalBorder)
{
    _playerAccelX = _playerAccelX * BorderCollisionDamping;
    _playerSpeedX = _playerSpeedX * BorderCollisionDamping;
    _playerAccelY = -_playerAccelY * BorderCollisionDamping;
    _playerSpeedY = -_playerSpeedY * BorderCollisionDamping;
}

If a collision registered, you flip the acceleration and speed values around. Notice that this also multiplies the acceleration and speed by a damping value, BorderCollisionDamping.

As usually happens in a collision, some of the movement energy is dissipated by the impact. In this case, you make the spaceship retain only 40% of its speed after bumping into the screen edges.

Try it out. Smash the spaceship into the border and see what happens. Who said spaceships can’t bounce?

Bouncing spaceship

For fun, play with the value of BorderCollisionDamping to see the effect of different values for this constant. If you make it larger than 1.0f, the spaceship actually gains energy from the collision.

Note: Now why is there an f behind those numbers in the code: 0.4f, 0.1f, 0.0f, and so on? And why did you use atan2f() instead of just atan2()? When you write games, you want to work with floating point numbers as much as possible because, unlike integers, they allow for digits behind the decimal point. This allows you to be much more precise.

There are two types of floating point numbers: floats and doubles (there is also a “long double”, but that’s the same as a double on the iPhone). Doubles are more precise than floats but they also take up more memory and are slower in practice. When you don’t put the f behind the number and just use 0.4, 0.1, 0.0, or when you use the version of a math function without the f suffix, you are working with doubles and not floats.

It doesn’t really matter if you use a double here and there. For example, the time value that CACurrentMediaTime() returns is a double. However, if you’re doing hundreds of thousands of calculations every frame, you will notice the difference. I did a quick test on a couple of my devices and the same calculations using doubles were 1.5 to 2 times slower. So it’s a good habit to stick to regular floats where you can.

You may have noticed a slight problem. Keep the spaceship aimed at the bottom of the screen so that it continues smashing into the border over and over, and you’ll see a constant flicker between the up and down angles.

Using the arc tangent to find the angle between a pair of x- and y-components works quite well, but only if those x and y values are fairly large. In this case, the damping factor has reduced the speed to almost zero. When you apply atan2f() to very small values, even a tiny change in these values can result in a big change in the resulting angle.

One way to fix this is to not change the angle when the speed is very slow. That sounds like an excellent reason to give a call to our old friend, Pythagoras.

pythagoras

Right now you don’t have such a thing as “the ship’s speed”. Instead, you have two speeds: one in the x-direction and one in the y-direction. But in order to draw any conclusions about “the ship’s speed” – is it too slow to actually rotate the ship? – you need to combine these x and y speed values into one single value:

Here you are in the Know 2 Sides, Need Remaining Side case discussed earlier.

As you can see, the true speed of the spaceship, that is, how many points it moves across the screen in a second, is the hypotenuse of the triangle that is formed by the speed in the x-direction and the speed in the y-direction. Put in terms of the Pythagorean formula:

true speed2 = _playerSpeedX2 + _playerSpeedY2

To find the actual value, you need to take the square root:

true speed = √(_playerSpeedX2 + _playerSpeedY2)

Add this code to updatePlayer. First remove this block of code:

float angle = atan2f(_playerSpeedY, _playerSpeedX);
_playerSprite.zRotation = angle - SK_DEGREES_TO_RADIANS(90.0f);

And replace it with this block of code:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
{
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
    _playerSprite.zRotation = angle - SK_DEGREES_TO_RADIANS(90.0f);
}

Build and run. You’ll see the spaceship now acts a lot more stable at the edges of the screen. If you’re wondering where the value 40.0f came from, the answer is: experimentation. I put some NSLog() statements into the code to look at the speeds at which the craft typically hit the borders, and then I tweaked this value until it felt right.

Blending Angles for Smooth Rotation

Of course, fixing one thing always breaks something else. Try slowing down the spaceship until it has stopped, then flip the device so the spaceship has to turn around and fly the other way.

Previously, that happened with a nice animation where you actually saw the ship turning. But because you just added some code that prevents the ship from changing its angle at low speeds, the turn is now very abrupt. It’s only a small detail, but it is the details that make great products.

The fix is to not switch to the new angle immediately, but to gradually “blend” it with the previous angle over a series of successive frames. This re-introduces the turning animation and still prevents the ship from rotating when it is not moving fast enough.

Blending sounds fancy, but it’s actually quite easy to implement. It does require you to keep track of the spaceship’s angle between updates, so add a new instance variable for it in the implementation block in MyScene.m:

@implementation MyScene
{
    ...
    float _playerAngle;
}

Change the rotation code in updatePlayer from this:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
{
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
    _playerSprite.zRotation = angle - SK_DEGREES_TO_RADIANS(90.0f);
}

To this:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
{
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
 
    const float RotationBlendFactor = 0.2f;
    _playerAngle = angle * RotationBlendFactor + _playerAngle * (1.0f - RotationBlendFactor);
}
 
_playerSprite.zRotation = _playerAngle - SK_DEGREES_TO_RADIANS(90.0f);

The _playerAngle variable combines the new angle and its own previous value by multiplying them with a blend factor. In human-speak, this means the new angle only counts for 20% towards the actual rotation that you set on the spaceship. Of course, over time more and more of the new angle gets added so that eventually the spaceship does point in the proper direction.

Note that the line that sets the _playerSprite’s rotation property is now outside the if statement.

Build and run to verify that there is no longer an abrupt change from one rotation angle to another.

Now try flying in a circle a couple of times, both clockwise and counterclockwise. You’ll notice that at some point in the turn the spaceship suddenly spins in the opposite direction. It always happens at the same point in the circle. What’s going on?

Well, there is something you should know about atan2f(). It does not return an angle in the convenient range of 0 to 360 degrees, but a value between +π and –π radians, or between +180 and -180 degrees to us non-mathematicians:

That means if you’re turning counterclockwise, at some point the angle will jump from +180 degrees to -180 degrees; or the other way around if you’re turning clockwise. And that’s where the weird spinning effect happens.

The problem is that when the new angle jumps from 180 degrees to -180 degrees, _playerAngle is still positive because it is trailing behind a bit. When you blend these two together, the spaceship actually starts turning the other way around. It took me a while to figure out what was causing this!

To fix it, you need to recognize when the angle makes that jump and adjust _playerAngle accordingly. Add a new instance variable at the bottom of the MyScene implementation block:

@implementation MyScene
{
    ...
    float _lastAngle;
}

And change the rotation code one more time from this:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
{
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
 
    const float RotationBlendFactor = 0.2f;
    _playerAngle = angle * RotationBlendFactor + _playerAngle * (1.0f - RotationBlendFactor);
}
 
_playerSprite.zRotation = _playerAngle - SK_DEGREES_TO_RADIANS(90.0f);

To this:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
{
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
 
    // Did the angle flip from +Pi to -Pi, or -Pi to +Pi?
    if (_lastAngle < -3.0f && angle > 3.0f)
    {
        _playerAngle += M_PI * 2.0f;
    }
    else if (_lastAngle > 3.0f && angle < -3.0f)
    {
        _playerAngle -= M_PI * 2.0f;
    }
 
    _lastAngle = angle;
    const float RotationBlendFactor = 0.2f;
    _playerAngle = angle * RotationBlendFactor + _playerAngle * (1.0f - RotationBlendFactor);
}
 
_playerSprite.zRotation = _playerAngle - SK_DEGREES_TO_RADIANS(90.0f);

Build and run. That’ll fix things right up and you should have no more problems turning your spacecraft!

This may have seemed like an arcane little problem not worth so much time, but it’s good to be aware that atan2f() gives you angles from -180 degrees (or –π radians) to +180 degrees (or π radians). If you’re using arc tangent to calculate an angle and you get weird behavior like the spinning you saw here, then you might need to verify that your angles really are what you expect them to be.

Using Trig to Find Your Target

This is a great start – you have a spaceship moving along pretty smoothly! But so far the spaceship’s life is too easy and carefree. Let’s spice this up by adding an enemy: a big cannon!

Add two new instance variables in the implementation block in MyScene.m:

@implementation MyScene
{
    ...
    SKSpriteNode *_cannonSprite;
    SKSpriteNode *_turretSprite;
}

You’ll set these sprites up in initWithSize. Place this code before the creation of _playerSprite so that the spaceship always gets drawn “above” the cannon:

_cannonSprite = [SKSpriteNode spriteNodeWithImageNamed:@"Cannon"];
_cannonSprite.position = CGPointMake(_winSize.width/2.0f, _winSize.height/2.0f);
[self addChild:_cannonSprite];
 
_turretSprite = [SKSpriteNode spriteNodeWithImageNamed:@"Turret"];
_turretSprite.position = CGPointMake(_winSize.width/2.0f, _winSize.height/2.0f);
[self addChild:_turretSprite];

The cannon consists of two sprites: the fixed base and the turret that can rotate to take aim at the player. Build and run, and you should have a brand-new cannon sitting smack in the middle of the screen.

Cannon-in-center.png

Now you’ll give the cannon a target to snipe at – I bet you know who!

Yep, its turret should point at the player at all times. To get this to work, you need to figure out the angle to rotate the turret so that it points toward the player.

Figuring this out will be very similar to how you figured out how to rotate the spaceship in order to point toward the direction it’s moving in. The difference is that this time, the triangle won’t be based on the speed of the spaceship. Instead it will be drawn between the center positions of the two sprites:

Again, you can use atan2f() to calculate this angle. Add the following method:

- (void)updateTurret:(NSTimeInterval)dt
{
    float deltaX = _playerSprite.position.x - _turretSprite.position.x;
    float deltaY = _playerSprite.position.y - _turretSprite.position.y;
    float angle = atan2f(deltaY, deltaX);
 
    _turretSprite.zRotation = angle - SK_DEGREES_TO_RADIANS(90.0f);
}

The deltaX and deltaY variables measure the distance between the player sprite and the turret sprite. You plug these values into atan2f() to get angle of rotation.

As before, you need to convert this angle to include the offset from zero (90.0) so the sprite points correctly. Remember that atan2() always gives you the angle between the hypotenuse and the 0-degree line. It is not the angle inside the triangle.

Don’t forget to call this new updateTurret: method from update, or nothing much will happen:

-(void)update:(NSTimeInterval)currentTime
{
    ...
    [self updatePlayerAccelerationFromMotionManager];
    [self updatePlayer:_deltaTime];
    [self updateTurret:_deltaTime];
}

Build and run. The cannon is now always pointing at the spaceship. See how easy that was? That’s the power of trig for you!

Turret pointed towards player

Challenge: It is unlikely that a real cannon would be able to act so instantaneously – it would have to be able to predict exactly where the target was going. Instead, a real cannon would always be playing catch up.

You can accomplish this by “blending” the old angle with the new one, just like you did earlier. The smaller the blend factor, the more time the turret needs to catch up with the spaceship. See if you can implement this on your own.

Adding Health Bars… That Move!

Soon the player will be able to fire missiles at the cannon, and the cannon will be able to inflict damage on the player. To show the amount of hit points each object has remaining, you will add health bars to the scene using SKNode, a way to group other node content using Sprite Kit.

First, you have to do some prep. Add a couple of new constants to the top of the file:

const int MaxHP = 100;
const float HealthBarWidth = 40.0f;
const float HealthBarHeight = 4.0f;

Also add a couple of new instance variables in the implementation block:

@implementation MyScene
{
    ...
    int _playerHP;
    int _cannonHP;
    SKNode *_playerHealthBar;
    SKNode *_cannonHealthBar;
}

Add the following to the bottom of initWithSize to set up these new variables:

_playerHealthBar = [SKNode node];
[self addChild:_playerHealthBar];
 
_cannonHealthBar = [SKNode node];
[self addChild:_cannonHealthBar];
 
_cannonHealthBar.position = CGPointMake(
                                         _cannonSprite.position.x - HealthBarWidth/2.0f + 0.5f,
                                         _cannonSprite.position.y - _cannonSprite.size.height/2.0f - 10.0f + 0.5f);
 
_playerHP = MaxHP;
_cannonHP = MaxHP;

The _playerHealthBar and _cannonHealthBar objects are instances of SKNode, which is like a sprite except that you can draw other sprites and shapes into it. You will be using a SKSpriteNode but drawing directly into using Core Graphics, which is ideal for your health bars that consist of a border and a filled rectangle.

Note that you placed the _cannonHealthBar sprite slightly below the cannon, but didn’t assign a position to the _playerHealthBar yet. That’s because the cannon never moves, so you can simply set the position of its health bar once and forget about it.

However, whenever the spaceship moves, you have to adjust the position of the _playerHealthBar as well. That happens in updatePlayer. Add the following lines to the bottom of that method:

_playerHealthBar.position = CGPointMake(
                                _playerSprite.position.x - HealthBarWidth/2.0f + 0.5f,
                                _playerSprite.position.y - _playerSprite.size.height/2.0f - 15.0f + 0.5f);

Now all that’s left is drawing the bars themselves. Add this new method to the bottom of the file:

-(void) drawHealthBar:(SKNode *)node withName:(NSString *)name andHealthPoints:(int)hp
{
    [node removeAllChildren];
 
    float widthOfHealth = (HealthBarWidth - 2.0f)*hp/MaxHP;
 
    UIColor *clearColor = [UIColor clearColor];
    UIColor *fillColor = [UIColor colorWithRed:113.0f/255.0f green:202.0f/255.0f blue:53.0f/255.0f alpha:1.0f];
    UIColor *borderColor = [UIColor colorWithRed:35.0f/255.0f green:28.0f/255.0f blue:40.0f/255.0f alpha:1.0f];
 
    //create the outline for the health bar
    CGSize outlineRectSize = CGSizeMake(HealthBarWidth-1.0f, HealthBarHeight-1.0);
    UIGraphicsBeginImageContextWithOptions(outlineRectSize, NO, 0.0);
    CGContextRef healthBarContext = UIGraphicsGetCurrentContext();
 
    //Drawing the outline for the health bar
    CGRect spriteOutlineRect = CGRectMake(0.0, 0.0, HealthBarWidth-1.0f, HealthBarHeight-1.0f);
    CGContextSetStrokeColorWithColor(healthBarContext, borderColor.CGColor);
    CGContextSetLineWidth(healthBarContext, 1.0);
    CGContextAddRect(healthBarContext, spriteOutlineRect);
    CGContextStrokePath(healthBarContext);
 
    //Fill the health bar with a filled rectangle
    CGRect spriteFillRect = CGRectMake(0.5, 0.5, outlineRectSize.width-1.0, outlineRectSize.height-1.0);
    spriteFillRect.size.width = widthOfHealth;
    CGContextSetFillColorWithColor(healthBarContext, fillColor.CGColor);
    CGContextSetStrokeColorWithColor(healthBarContext, clearColor.CGColor);
    CGContextSetLineWidth(healthBarContext, 1.0);
    CGContextFillRect(healthBarContext, spriteFillRect);
 
    //Generate a sprite image of the two pieces for display
    UIImage *spriteImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
 
    CGImageRef spriteCGImageRef = [spriteImage CGImage];
    SKTexture *spriteTexture = [SKTexture textureWithCGImage:spriteCGImageRef];
    spriteTexture.filteringMode = SKTextureFilteringLinear; //This is the default anyway
    SKSpriteNode *frameSprite = [SKSpriteNode spriteNodeWithTexture:spriteTexture size:outlineRectSize];
    frameSprite.position = CGPointZero;
    frameSprite.name = name;
    frameSprite.anchorPoint = CGPointMake(0.0, 0.5);
 
    [node addChild:frameSprite];
}

This sets up the rectangles for a single health bar. First it sets up the colors for the health bar pieces, then draws it twice: once for the border, which always has the same size, and once for the amount of health, which changes depending on the number of remaining hit points. The method then generates a Sprite Kit Texture of these drawing calls, and finally renders an SKSpriteNode from this texture.

This SKSpriteNode is then added to the SKNode so it gets rendered in the location of the SKNode.

Of course, you need to call this method, once for the player and once for the cannon. Add these two lines to the bottom of update:

[self drawHealthBar:_playerHealthBar withName:@"playerHealth" andHealthPoints:_playerHP];
[self drawHealthBar:_cannonHealthBar withName:@"cannonHealth" andHealthPoints:_cannonHP];

Build and run. Now both the player and the enemy have health bars:

Health bars

Using Trig for Collision Detection

Right now, the spaceship can fly directly over the cannon without consequence. It would be more fun if it didn’t have it so easy and instead suffered damage for colliding with the cannon. This is where this tutorial enters the sphere of collision detection (don’t miss that pun).

At this point, a lot of game devs think, “I need a physics engine!” and start using Sprite Kit’s built-in physics engine. While it’s certainly true that you can use Sprite Kit’s physics engine for this, it’s not that hard to do collision detection yourself, especially when you can use circles.

Detecting whether two circles intersect is a piece of cake: all you have to do is look at the distance between them (*cough* Pythagoras) and see if it is smaller than the radius of both circles.

Add two new constants to the top of MyScene.m:

const float CannonCollisionRadius = 20.0f;
const float PlayerCollisionRadius = 10.0f;

These are the sizes of the collision circles around the cannon and the player. Looking at the sprite, you’ll see that the radius of the cannon is slightly larger at 25 points, but it’s nice to have a bit of wiggle room. You don’t want your games to be too unforgiving or players will think it is unfair.

The fact that the spaceship isn’t circular at all shouldn’t deter you. Often a circle is a good enough approximation of the shape of your sprite, and it has the big advantage that it makes it easy to do the necessary calculations. In this case, the body of the ship is roughly 20 points in diameter (remember, the diameter is twice the radius).

Add a new method that does the collision detection:

- (void)checkCollisionOfPlayerWithCannon
{
    float deltaX = _playerSprite.position.x - _turretSprite.position.x;
    float deltaY = _playerSprite.position.y - _turretSprite.position.y;
 
    float distance = sqrtf(deltaX*deltaX + deltaY*deltaY);
 
    if (distance <= CannonCollisionRadius + PlayerCollisionRadius)
    {
        [self runAction:_collisionSound];
    }
}

You’ve seen how this works before: first you calculate the distance between the x-positions of the two sprites, then the y-distance. With these two values, you can calculate the hypotenuse, which is the true distance between these sprites. If that distance is smaller than the collision circles, a sound effect is played.

In update, add a call to this new method, above the calls to drawHealthBar:

[self checkCollisionOfPlayerWithCannon];

Add this line to initWithSize to pre-load the sound effect:

_collisionSound = [SKAction playSoundFileNamed:@"Collision.wav" waitForCompletion:NO];

Declare your intent to use the AVFoundation framework and bring it in through the new @import syntax. Insert the following line, right after your @import CoreMotion; at the top:

@import AVFoundation;

Finally, declare an instance variable to for the SKAction sound effect by adding this line in the implementation block:

SKAction *_collisionSound;

Give it a whirl and drive the spaceship into the cannon.

Ship colliding with turret

Notice that the sound effect is a little odd. It’s because while the spaceship flies over the cannon, the game registers repeated collisions, one after another. There isn’t just one collision, there are many, and it plays the sound effect for every one of them.

Collision detection is only the first step. The second step is collision response. Not only do you want to play a sound effect, but you also want the spaceship to bounce off the cannon.

Add these lines inside the if statement in checkCollisionOfPlayerWithCannon:

const float CannonCollisionDamping = 0.8f;
_playerAccelX = -_playerAccelX * CannonCollisionDamping;
_playerSpeedX = -_playerSpeedX * CannonCollisionDamping;
_playerAccelY = -_playerAccelY * CannonCollisionDamping;
_playerSpeedY = -_playerSpeedY * CannonCollisionDamping;

This is similar to what you did for making the spaceship bounce off the screen borders. Build and run to see how it works.

It works pretty well when the spaceship is going fast when it hits the cannon. But if it’s moving too slow, then even after reversing the speed, the ship stays within the collision radius and never makes its way out of it. So this solution has some problems.

Instead of bouncing the ship off the cannon by reversing its existing speed, you will simply expel the ship with a fixed speed so that it always moves outside of the collision area, no matter how fast or slow it was flying when it hit the cannon.

This requires you to add a new constant at the top of MyScene.m:

const float CannonCollisionSpeed = 200.0f;

Remove those five lines you just added in checkCollisionOfPlayerWithCannon and replace them with the following:

float angle = atan2f(deltaY, deltaX);
 
_playerSpeedX = cosf(angle) * CannonCollisionSpeed;
_playerSpeedY = sinf(angle) * CannonCollisionSpeed;
_playerAccelX = 0.0f;
_playerAccelY = 0.0f;
 
_playerHP = MAX(0, _playerHP - 20);
_cannonHP = MAX(0, _cannonHP - 5);

That is the speed you want the spaceship to have after it collides with the cannon. You need to calculate how much of that speed goes in the x-direction and how much in the y-direction, based on the angle of impact:

You can calculate the angle using arctan because you know the current speed of the player. To calculate how fast the player bounces off the cannon in the x- and y-directions, you already have two pieces of information: the angle that you just calculated, and the new speed, CannonCollisionSpeed, which is the hypotenuse of the triangle.

If you look back at the theory section, you will see that you can calculate the new x-component of the speed using the cosine function and the y-component using the sine function:

  • new x-speed = cos(angle) * hypotenuse
  • new y-speed = sin(angle) * hypotenuse

And that’s exactly what you’re doing in the code above. Build and run, and you’ll see the spaceship now bounces properly off the cannon and loses some hit points in the process.

Ship colliding and losing points

Adding Some Spin

For an additional effect, you can add spin to the spaceship after a collision. This is additional rotation that doesn’t influence the flight direction. It just makes the effect of the collision more profound (and the pilot dizzy). Add a new instance variable in the implementation block:

@implementation MyScene
{
    ...
    float _playerSpin;
}

In checkCollisionOfPlayerWithCannon, add the following line inside the collision detection block:

if (distance <= CannonCollisionRadius + PlayerCollisionRadius)
{
    ...
    _playerSpin = 180.0f * 3.0f;
}

This sets the amount of spin to a circle and a half, which I think looks pretty good. Now at the bottom of updatePlayer, add the following code to add spin to the rotation:

_playerSprite.zRotation += -SK_DEGREES_TO_RADIANS(_playerSpin);
 
if (_playerSpin > 0.0f)
{
    _playerSpin -= 2.0f * 360.0f * dt;
    if (_playerSpin < 0.0f)
    {
        _playerSpin = 0.0f;
    }
}

The amount of spin quickly decreases over time – I chose a speed of 720 degrees per second. Once the spin has reached 0, it stops.

Build and run and set that ship spinning!

Where to Go from Here?

Here is the full example project from the tutorial up to this point.

Triangles are everywhere! You’ve seen how you can use this fact to breathe life into your sprites with the various trigonometric functions.

You have to admit, it wasn’t that hard to follow along, was it? Math doesn’t have to be boring if you can apply it to fun projects, such as making games.

But there’s more to come: in Part 2 of this Trigonometry for Game Programming series, you’ll add missiles to the game, learn more about sine and cosine, and see some other useful ways to put the power of trig to work in your games.

In the meantime, drop by the forums to share how it’s going for you so far! Or if you really can’t wait until part 2, why not brush up on some more Sprite Kit with some of our other tutorials. Alternatively, if you want to start diving into more game development why not take a look at our book, iOS Games by Tutorials.

Credits: The graphics for this game are based on a free sprite set by Kenney Vleugels. The sound effects are based on samples from freesound.org.

Trigonometry for Game Programming [Sprite Kit Version]: Part 1/2 is a post from: Ray Wenderlich

The post Trigonometry for Game Programming [Sprite Kit Version]: Part 1/2 appeared first on Ray Wenderlich.

Trigonometry for Game Programming [Sprite Kit Version]: Part 2/2

$
0
0

Learn Trigonometry for Game Programming!

Note from Ray: Tony has ported this popular tutorial from Cocos2D to Sprite Kit. We hope you enjoy!

Welcome back to the Trigonometry for Game Programming series!

In the first part of the series, you learned the basics of trigonometry and experienced for yourself how useful it is for making games. You saw that math doesn’t have to be boring – as long as you have a fun project to apply it to, such as making your own games.

Oh yes, and you built the foundation for a solid little space shooter game!

In this second and final part of the series, you will add missiles, an orbiting asteroid shield and an animated “game over” screen to your game. Along the way, you’ll learn more about sine and cosine and see some other useful ways to put the power of trig to work in your games.

Get ready to ride the sine wave back into space!

Getting Started

This tutorial picks up where you left off in the last part. If you don’t have it already, here is the project with all of the source code up to this point.

As of right now, your game has a spaceship and a rotating cannon, each with health bars. While they may be sworn enemies, neither has the ability to damage the other, unless the spaceship flies right into the cannon.

It’s time for some fireworks. You will now give the player the ability to fire missiles at the cannon by swiping the screen. The spaceship will then launch a missile in the direction of that swipe. For that, you first need to enable touch handling on the layer.

Open the Xcode project and add these instance variables to MyScene.m:

@implemenation MyScene
{
    ...
    SKSpriteNode *_playerMissileSprite;
    CGPoint _touchLocation;
    CFTimeInterval _touchTime;
}

Also add these lines at the bottom of the code inside initWithSize:

_playerMissileSprite = [SKSpriteNode spriteNodeWithImageNamed:@"PlayerMissile"];
_playerMissileSprite.hidden = YES;
[self addChild:_playerMissileSprite];

You initially set the missile sprite to be hidden and only make it visible when the player fires.

Now add the methods to handle the touches, starting with the one that is called whenever the user first puts her finger on the touchscreen:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    _touchLocation = location;
    _touchTime = CACurrentMediaTime();
}

This is pretty simple – you store the touch location and the time of the touch in the new instance variables.

The actual work happens in touchesEnded, which you add next:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (CACurrentMediaTime() - _touchTime < 0.3 && _playerMissileSprite.hidden)
    {
        UITouch *touch = [touches anyObject];
        CGPoint location = [touch locationInNode:self];
        CGPoint diff = CGPointMake(location.x - _touchLocation.x, location.y - _touchLocation.y);
        float diffLength = sqrtf(diff.x*diff.x + diff.y*diff.y);
        if (diffLength > 4.0f)
        {
            // TODO: more code here
        }
    }
}

This determines whether there hasn’t been too much time between starting and ending the swipe, 0.3 seconds at most (otherwise, the missle doesn’t fire). Also, the player can only shoot one missile at a time, so the touch is ignored if a missile is already flying.

The last bit figures out what sort of gesture the user made: was it really a swipe, or just a tap? You only launch missiles on swipes, not taps.

Note: You have done this sort of calculation a couple of times already – subtract two coordinates, then use the Pythagorean Theorem to find the distance between them.

It is common in games to combine the x and y values into one type such as CGPoint. Such a value is referred to as a vector, a topic large enough that it deserves its own tutorial.

There are two ways you could make the missile fly.

The first would be to create new _playerMissileSpeedX and Y instance variables and fill them in with the correct speed values, based on the angle that you’re aiming the missile. Inside update, you would then add this speed to the missile sprite’s position, and check whether the missile flew outside of the visible screen so the player can fire again. This is very similar to how you made the spaceship move in Part 1 of this tutorial.

However, the missile doesn’t need to change course on its way: it always flies in a straight line. So you can take an alternative approach: calculating the destination point when you launch the missile. Then you can set a moveTo action on the missile sprite and let Sprite Kit handle the rest. This also saves you from having to check whether the missile has left the visible screen.

I bet you saw this coming: the second approach is exactly what you are going to implement right now, and you will use trig to do it.

To begin, add the following lines to the TODO section in touchesEnded:

 float angle = atan2f(diff.y, diff.x);
 _playerMissileSprite.zRotation = angle - SK_DEGREES_TO_RADIANS(90.0f);
 
 _playerMissileSprite.position = _playerSprite.position;
 _playerMissileSprite.hidden = NO;

This is nothing fancy. You calculate the angle, set up the sprite’s rotation and position, and make the missile sprite visible.

Now, however, comes the math. You know the starting position of the missile (which is the position of the player’s ship) and you know the angle (derived from the player’s swipe motion). What you need to calculate now is the destination point of the missile based on these facts.

There are actually four different situations that you need to handle, so let’s first consider the one where the user swiped in a downward motion:

The destination point always lies just outside the screen instead of exactly on the edge of the screen, because it looks better if the missile completely flies out of sight. For this purpose, add a constant at the top of MyScene.m and call it Margin:

const float Margin = 20.0f;

To find the destination point, you need more than just the angle. Fortunately, if you know that the player is shooting downward, you also know the vertical distance the missile needs to fly. That is simply the starting y-position of the missile, plus the margin. (Remember that the bottom of the screen is y = 0 in Sprite Kit.)

Now you have the angle and the adjacent side. To get the destination point, you simply need to calculate the opposite side of the triangle and add it to the starting x-position of the missile.

That sounds like a job for the tan() function – not the arc tangent that you’ve been using up to now, but the normal one:

tan(angle) = opposite / adjacent

Or for your purposes:

opposite = tan(angle) * adjacent

There’s only one snag. The angle that is pictured in the above diagram isn’t really the angle you measured, because the angle you get back from atan2f() is always relative to the 0 degree line of the circle:

Note that in this particular diagram, the adjacent and opposite sides are swapped – it is now the adjacent you’re looking for. So the formula becomes:

adjacent = opposite / tan(angle)

You could probably make this work, but that division makes me cringe. What if tan(angle) happens to be 0? Then you’ll have a division-by-zero on your hands. I’d much rather stick to the first formula, the one that calculates the opposite.

It turns out that this is actually no problem if you remember that beta = (90 – alpha). In other words, if you position yourself in the opposite corner of the triangle and use an angle of (90 – alpha), then you can use that first formula without problems.

Be careful: now that the opposite side points the other way around, you need to subtract it rather than add it to the starting x-position.

Let’s put all this in code. Modify the touchesEnded method to look like the following:

if (diffLength > 4.0f)
{
    float angle = atan2f(diff.y, diff.x);
    _playerMissileSprite.zRotation = angle - SK_DEGREES_TO_RADIANS(90.0f);
 
     _playerMissileSprite.position = _playerSprite.position;
     _playerMissileSprite.hidden = NO;
 
    float adjacent, opposite;
    CGPoint destination;
 
    // 1
    angle = M_PI_2 - angle;
    adjacent = _playerMissileSprite.position.y + Margin;
    opposite = tanf(angle) * adjacent;
    destination = CGPointMake(_playerMissileSprite.position.x - opposite, -Margin);
 
    // 2
    //set up the sequence of actions for the firing
    SKAction *missileMoveAction = [SKAction moveTo:destination duration:2.0f];
    SKAction *missileDoneMoveAction = [SKAction runBlock:(dispatch_block_t)^() {
        _playerMissileSprite.hidden = YES;
    }];
    SKAction *moveMissileActionWithDone = [SKAction sequence:@[_missileShootSound, missileMoveAction, missileDoneMoveAction]];
 
    [_playerMissileSprite runAction:moveMissileActionWithDone];
}

Add the following line to the end of the @implemenation MyScene section:

SKAction *_missileShootSound;

Also add this line at the bottom of the code inside initWithSize method:

_missileShootSound = [SKAction playSoundFileNamed:@"Shoot.wav" waitForCompletion:NO];

This does two things:

  1. It adjusts the angle so you’re looking at it from the other corner. It then calculates the length of the adjacent side, and uses the tangent function to find the length of the opposite side. Finally, it calculates the destination coordinate.
  2. It creates a sequence of actions (each runs one after the other), starting with a sound action, followed by moveTo. Once the sprite has reached its destination, the missileDoneMoveAction action will make the sprite invisible again.

Build and run, and try it out. Move the ship so that it is about halfway centered on the screen and then swipe down. You should see a missile fly along the path that you swiped. Note that you can only fire one missile at a time – the player should have to wait until the previous missile has disappeared from the screen before firing again.

If you try swiping in any other direction, you’ll notice that the missile doesn’t quite fly where you want. That’s because currently you have only handled the situation where the destination point lies below the bottom of the screen. Each of the other screen edges requires slightly different calculations.

Replace the code that calculates the destination point (the “// 1″ section) with this:

if (angle <= -M_PI_4 && angle > -3.0f * M_PI_4)
{
    // Shoot down
    angle = M_PI_2 - angle;
    adjacent = _playerMissileSprite.position.y + Margin;
    opposite = tanf(angle) * adjacent;
    destination = CGPointMake(_playerMissileSprite.position.x - opposite, -Margin);
}
else if (angle > M_PI_4 && angle <= 3.0f * M_PI_4)
{
    // Shoot up
    angle = M_PI_2 - angle;
    adjacent = _winSize.height - _playerMissileSprite.position.y + Margin;
    opposite = tanf(angle) * adjacent;
    destination = CGPointMake(_playerMissileSprite.position.x + opposite, _winSize.height + Margin);
}
else if (angle <= M_PI_4 && angle > -M_PI_4)
{
    // Shoot right
    adjacent = _winSize.width - _playerMissileSprite.position.x + Margin;
    opposite = tanf(angle) * adjacent;
    destination = CGPointMake(_winSize.width + Margin, _playerMissileSprite.position.y + opposite);
}
else  // angle > 3.0f * M_PI_4 || angle <= -3.0f * M_PI_4
{
    // Shoot left
    adjacent = _playerMissileSprite.position.x + Margin;
    opposite = tanf(angle) * adjacent;
    destination = CGPointMake(-Margin, _playerMissileSprite.position.y - opposite);
}

You have actively divided the screen into four sections, like slices of a pie:

Each of these sections calculates a slightly different destination point. Notice that the code for shooting left comes last, because here you have the issue of switching from +180 to -180 degrees again (recall that this is the range of values that atan2() returns).

Now you should be able to fire in any direction! Build and run and give it a try.

Shooting in any direction

There is still one problem (how could there not be?). Sometimes the missile appears to be going faster than at other times.

That’s because currently, the duration of the animation is hard-coded to last 2 seconds. If the missile needs to travel far, then it goes faster in order to cover more distance in the same amount of time.

Instead what you want is for each missile to always travel at the same speed. The hypotenuse comes to the rescue once more!

First, add a new constant at the top of MyScene.m:

const float PlayerMissileSpeed = 300.0f;

This is the distance that you want the missile to travel per second. By calculating the length of the hypotenuse, you know the actual distance that the missile travels. To get the needed duration of the animation, you divide those two distances.

Sounds complicated? Nah…

In touchesEnded, change the code that creates the missileMoveAction action (the // 2 section) to the following:

float hypotenuse = sqrtf(adjacent*adjacent + opposite*opposite);
NSTimeInterval duration = hypotenuse / PlayerMissileSpeed;
 
//set up the sequence of actions for the firing
SKAction *missileMoveAction = [SKAction moveTo:destination duration:duration];

That’s all there is to it. Build and run the app again. Now the missile always flies at the same speed, no matter how far or close the destination point is.

And that’s how you use trig to set up a moveTo action. It’s a bit involved, but then it’s largely fire & forget because Sprite Kit does all the work of animating for you.

Meme - not bad

Hitting Your Targets

Right now, the missile completely ignores the cannon – your spaceship might as well be firing a beam of green light!

That’s about to change. As before, you will use a simple radius-based method of collision detection.

Add a new constant to the source at the top of MyScene.m:

const float CannonHitRadius = 25.0f;

For simplicity’s sake, you will assume that the missile itself doesn’t have a radius, so whenever its center point goes within the cannon hit radius, that will be sufficient to register as a collision. That’s precise enough for this game.

In preparation to play the hit sound, add an instance variable at the bottom of the implementation for the sound action:

SKAction *_missileHitSound;

And initialize the SKAction in the initWithSize method, by adding this line at the bottom:

_missileHitSound = [SKAction playSoundFileNamed:@"Hit.wav" waitForCompletion:NO];

Now, add a new method:

- (void)updatePlayerMissile:(NSTimeInterval)dt
{
    if (!_playerMissileSprite.hidden)
    {
        float deltaX = _playerMissileSprite.position.x - _turretSprite.position.x;
        float deltaY = _playerMissileSprite.position.y - _turretSprite.position.y;
 
        float distance = sqrtf(deltaX*deltaX + deltaY*deltaY);
        if (distance < CannonHitRadius)
        {
            [self runAction:_missileHitSound];
 
            _cannonHP = MAX(0, _cannonHP - 10);
 
            _playerMissileSprite.hidden = YES;
            [_playerMissileSprite removeAllActions];
        }
    }
}

This should be old hat by now: you calculate the distance and then play a sound effect via a Sprite Kit SKAction when the distance has become smaller than the hit radius.

Note that you need to call removeAllActions on the missile sprite, because you want it to stop moving. This tells the moveTo SKaction that it is no longer needed.

Call this new method from within update. Place it directly below the call to updatePlayer.

[self updatePlayerMissile:_deltaTime];

Build and run, then try it out. Finally you can inflict some damage on the enemy!

Inflicting damage

Challenges for the 1337

Here’s a challenge for you: can you make the cannon shoot back at the spaceship?

Try to see if you can figure it out – you already know all the required pieces, and this will be some really good practice to make sure you understand what we’ve covered so far.

Try it out for yourself before you look at the solution!

Solution Inside: Make the cannon shoot back SelectShow>

 

Got that, and think you’re some hot stuff? Here’s another challenge for you!

Currently your missiles fly to their destination point in a straight line. But what if the missiles were heat-seeking? A heat-seeking missile adjusts its course when it detects that the player has moved.

You’ve got the power of trig on your side, so how would you do it? Hint: instead of calculating the speed and direction of the missile just once, you would do it again and again on each frame. Give it a try.

Solution Inside: Make the missile heat seeking SelectShow>

 

How’d you do? Is your spaceship dodging guided missiles like Tom Cruise, or still flying around scot-free?

Adding an Orbiting Shield

To make the game more challenging, you will give the enemy a shield. The shield will be a magical asteroid sprite that orbits the cannon and destroys any missiles that come near it.

Add a couple more constants to the top of MyScene.m:

const float OrbiterSpeed = 120.0f;  // degrees per second
const float OrbiterRadius = 60.0f;  // degrees
const float OrbiterCollisionRadius = 20.0f;

And some new instance variables as well:

@implementation MyScene
{
    ...
    SKSpriteNode *_orbiterSprite;
    float _orbiterAngle;  // in degrees
}

I added the “in degrees” comments above because unlike the other angles, which were in radians, here you’ll work with degrees. They are just easier to wrap your head around.

Initialize the new sprite inside initWithSize after all the previous code:

_orbiterSprite = [SKSpriteNode spriteNodeWithImageNamed:@"Asteroid"];
[self addChild:_orbiterSprite];

And add a new method:

- (void)updateOrbiter:(NSTimeInterval)dt
{
     // 1
    _orbiterAngle += OrbiterSpeed * dt;
    _orbiterAngle = fmodf(_orbiterAngle, 360.0f);
 
    // 2
    float x = cosf(SK_DEGREES_TO_RADIANS(_orbiterAngle)) * OrbiterRadius;
    float y = sinf(SK_DEGREES_TO_RADIANS(_orbiterAngle)) * OrbiterRadius;
 
    // 3
    _orbiterSprite.position = CGPointMake(_cannonSprite.position.x + x, _cannonSprite.position.y + y);
}

The asteroid will orbit around the cannon. In other words, it describes a circular path, round and round and round and round. To accomplish this, you need two pieces: the radius that determines how far the asteroid is from the center of the cannon, and the angle that describes how far it has rotated around that center point.

This is what updateOrbiter does:

  1. It increments the angle by a certain speed (from the OrbiterSpeed constant), adjusted for the delta time. Because we as humans like to think of angles as anything between 0 and 360 degrees, you use fmodf() to wrap the angle around back to 0 once it becomes greater than 360. You cannot use the % operator for this, because it only works on integers, but fmodf() does the same thing for floats (and fmod() does the same thing for doubles). Wrapping the angles isn’t strictly necessary but it helps you stay sane when you’re trying to debug something like this.
  2. It calculates the new x- and y-positions for the orbiter using the sine and cosine functions. These take the radius (which forms the hypotenuse of the triangle) and the current angle, and return the adjacent and opposite sides, respectively. More about this in a second.
  3. It sets the new position of the sprite by adding the x- and y-positions to the center position of the cannon.

You have briefly seen sinf() and cosf() in action, but it may not have been entirely clear how they worked. Sure, you have the formulas memorized (really? :]) and you know that both of these functions can be used to calculate the lengths of the other sides, once you have an angle and the hypotenuse.

But aren’t you curious why you can actually do that? I thought you were!

Let’s draw a circle:

The illustration above exactly depicts the situation of the asteroid orbiting around the cannon. The circle describes the path of the asteroid and the origin of the circle is the center of the cannon.

The angle starts at zero degrees but increases all the time until it ends up right back at the beginning. As you can see it, is the radius of the circle that determines how far away from the center the asteroid is placed.

So, given the angle and the radius, you can derive the x- and y-positions using the cosine and sine, respectively:

Now let’s take a look at a plot of a sine wave and a cosine wave:

On the horizontal axis are the degrees of a circle, from 0 to 360, or 0 to 2π radians if you’re a mathematician. The vertical axis usually goes from -1 to +1, but if your circle has a radius that is greater than one (and they tend to) then the vertical axis really goes from –radius to +radius.

As the angle increases from 0 to 360, find the angle on the horizontal axis in the plots for the cosine and sine waves. The vertical axis then tells you what the values for x and y are:

  • If the angle is 0 degrees, then cos(0) is 1*radius but sin(0) is 0*radius. That corresponds exactly to the (x, y) coordinate in the circle: x is equal to the radius, but y is 0.
  • If the angle is 45 degrees, then cos(45) is 0.707*radius and so is sin(45). This means x and y are both the same at this point on the circle. (Note: if you’re trying this out on a calculator, then switch it to DEG mode first. You’ll get radically different answers if it’s in RAD mode, no pun intended.)
  • If the angle is 90 degrees, then cos(90) is 0*radius and sin(90) is 1*radius. You’re now at the top of the circle where the (x, y) coordinate is (0, radius).
  • And so on… To get a more intuitive feel for how the coordinates in the circle relate to the values of the sine, cosine and even tangent functions, try out this cool interactive circle.

Make sense? Awesome. Did you also notice that the curves of the sine and cosine are very similar? In fact, the cosine wave is simply the sine wave shifted by 90 degrees. Go ahead and impress your friends and family with your knowledge of the mathematical origins of sine and cosine. :]

Back to coding. Add a call to updateOrbiter at the bottom of update:

-(void)update:(NSTimeInterval)currentTime
{
    ...
    [self updateOrbiter:_deltaTime];
}

Build and run the app. You should now have an asteroid that perpetually circles the enemy cannon.

You can also make the asteroid spin around its own axis. Add the following line to the bottom of updateOrbiter:

_orbiterSprite.zRotation = SK_DEGREES_TO_RADIANS(_orbiterAngle);

By setting the rotation to _orbiterAngle, the asteroid always stays oriented in the same position relative to the cannon, much like the moon always shows the same side to the earth. Even though it looks like it isn’t spinning, it certainly is!

Insert a minus sign to give the asteroid extra spin that you can see. Pick whichever effect you like best. Build and run to play around with it for a bit.

Let’s give the orbiter a purpose. If the missile comes too close, the asteroid will destroy it before it gets a chance to do any damage to the cannon. Add the following at the bottom of updateOrbiter:

if (!_playerMissileSprite.hidden)
{
    float deltaX = _playerMissileSprite.position.x - _orbiterSprite.position.x;
    float deltaY = _playerMissileSprite.position.y - _orbiterSprite.position.y;
 
    float distance = sqrtf(deltaX*deltaX + deltaY*deltaY);
    if (distance < OrbiterCollisionRadius)
    {
        _playerMissileSprite.hidden = YES;
        [_playerMissileSprite removeAllActions];
 
        _orbiterSprite.scale = 2.0f;
        [_orbiterSprite runAction:[SKAction scaleTo:1.0f duration:0.5f]];
    }
}

No surprises for you here. It’s the same code you’ve seen several times now. Just remember to stop the moveTo action that is on the missile sprite. For added visual effect, you also scale the asteroid sprite momentarily. This makes it look like the orbiter “ate” the missile.

Build and run to see your new orbiting shield in action.

Orbiting missile shield

Game Over, With Trig!

There is still more that you can do with sines and cosines. They’re not just useful for calculating things with triangles – they also come in handy for animations.

A good place to show an example of such an animation is the game over screen. Add a few new instance variables in the implementation block at the top of MyScene.m:

@implementation MyScene
{
    ...
    SKLabelNode *_gameOverLabel;
    SKSpriteNode *_darkenLayer;
    BOOL _gameOver;
    CFTimeInterval _gameOverElapsed;
}

And a new method:

- (void)checkGameOver:(NSTimeInterval)dt
{
    // 1
    if (_playerHP > 0 && _cannonHP > 0)  // not game over yet
    {
        return;
    }
 
    if (!_gameOver)
    {
        // 2
        _gameOver = YES;
        _gameOverElapsed = 0.0;
        [self stopMonitoringAcceleration];
 
        // 3
        UIColor *fillColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f];
        _darkenLayer = [SKSpriteNode spriteNodeWithColor:fillColor size:_winSize];
        _darkenLayer.alpha = 0.0;
        _darkenLayer.position = CGPointMake(_winSize.width/2.0f, _winSize.height/2.0f);
        [self addChild:_darkenLayer];
 
        // 4
        NSString *text = (_playerHP == 0) ? @"GAME OVER" : @"Victory!";
        _gameOverLabel = [[SKLabelNode alloc] initWithFontNamed:@"Helvetica"];
        _gameOverLabel.text = text;
        _gameOverLabel.fontSize = 24.0f;
        _gameOverLabel.position = CGPointMake(_winSize.width/2.0f + 0.5f, _winSize.height/2.0f + 50.0f);
        [self addChild:_gameOverLabel];
    }
    else
    {
        // 5
        if (_darkenLayer.alpha < 200)
        {
            float newOpacity = fminf(200.0f, _darkenLayer.alpha + 255.0f * dt);
            _darkenLayer.alpha = newOpacity;
        }       
    }
}

This method both checks whether the game is done, and if so, handles the game over animation:

  1. The game keeps on going until either the player or the cannon runs out of hit points.
  2. When the game is over, set _gameOver to YES and disable the accelerometer.
  3. Create a new, all-black color layer and add it on top of everything else. Set its opacity to 0 so that it is completely see-through. Elsewhere in this method you will animate the opacity value of this layer so that it appears to fade in.
  4. Add a new text label and place it on the screen. The text is either “Victory!” if the player won or “Game Over” if the player lost.
  5. The above steps only happen once to set up the game over screen – every time after that, the code enters the else clause. Here you animate the opacity of the new color layer from 0 to 200 – almost completely opaque, but not quite.

Call checkGameOver at the bottom of update:

-(void)update:(NSTimeInterval)currentTime
{
    ...
    [self checkGameOver:_deltaTime];
}

And add a small snippet of logic to the top of touchesEnded:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (_gameOver)
    {
 
        SKScene *scene = [[MyScene alloc] initWithSize:self.size];
        SKTransition *reveal = [SKTransition flipHorizontalWithDuration:1.0];
        [self.view presentScene:scene transition:reveal];
        return;
    }
    ...
}

This restarts the game when the user taps on the game over screen.

Build and run, then try it out. Shoot at the cannon or collide your ship with it until one of you runs out of hit points. The screen will fade to black and the game over text will appear:

Game over

The game no longer responds to the accelerometer, but the animations still keep going. That gives it a nice twist!

This is all fine and dandy, but where are the sine and cosines? As you may have noticed, the fade in animation of the black layer was very linear. It just goes from transparent to opaque in a conventional fashion.

You can be better than conventional – you can use sinf() to subtly change the timing. This is often known as “easing” and the effect you will apply in particular is known as an “ease out”.

Add the new constant at the top of MyScene.m:

const CFTimeInterval DarkenDuration = 2.0;

Next, change the code in the else statement in checkGameOver to:

- (void)checkGameOver:(NSTimeInterval)dt
{
    ...
    }
    else 
    {
        _gameOverElapsed += dt;
        if (_gameOverElapsed < DarkenDuration)
        {
            float t = _gameOverElapsed / DarkenDuration;
            t = sinf(t * M_PI_2);  // ease out
            _darkenLayer.alpha = (200.0f * t)/255.0;
        }
    }
}

_gameOverElapsed keeps track of how much time has passed since the game ended. It takes two seconds to fade in the black layer (DarkenDuration). The variable t determines how much of that duration has passed by. It always has a value between 0.0 and 1.0, regardless of how long DarkenDuration really is.

Then you perform the magic trick:

t = sinf(t * M_PI_2);  // ease out

This converts t from a linear interpolation into one that breathes a bit more life into things:

Build and run to see the new “ease out” effect. If you find it hard to see the difference, then try it with that “magic” line commented out, or change the duration of the animation. The effect is subtle, but it’s there.

Note: If you want to play with the values and test things out quick you can comment out the return in the if (_playerHP > 0 && _cannonHP > 0) // not game over yet block.

There is one more thing I’d like to show you. Let’s make the game over text bounce, because things that bounce are always more fun.

Inside that else clause in checkGameOver, add the following so that the method looks like so:

- (void)checkGameOver:(NSTimeInterval)dt
{
    ...
    }
    else 
    {
        _gameOverElapsed += dt;
        if (_gameOverElapsed < DarkenDuration)
        {
            float t = _gameOverElapsed / DarkenDuration;
            t = sinf(t * M_PI_2);  // ease out
            _darkenLayer.alpha = (200.0f * t)/255.0;
        }
        // Game Over Label Position
        float y = fabsf(cosf(_gameOverElapsed * 3.0f)) * 50.0f;
        _gameOverLabel.position = CGPointMake(_gameOverLabel.position.x, _winSize.height/2.0f + y);    }
}

OK, what’s happening here? Recall what a cosine looks like:

If you take the absolute value of cosf() – using fabsf() – then the section that would previously go below zero is flipped. The curve already looks like something that bounces, don’t you think?

Because the output of these functions lies between 0.0 and 1.0, you multiply it by 50 to stretch it out a little. The argument to cosf() is normally an angle, but you’re giving it the _gameOverElapsed time to make the cosine move forward through its curve.

The factor 3.0 is just to make it go a bit faster. You can tinker with these values until you have something that you think looks cool.

Build and run to check out the bouncing text:

bouncing text

You’ve used the shape of the cosine to describe the bouncing motion of the text label. These cosines are useful for all sorts of things!

One last thing you can do is let the bouncing motion lose height over time. You do this by adding a damping factor. Create a new instance variable in the MyScene implementation block:

@implementation MyScene
{
    ...
    float _gameOverDampen;
}

In checkGameOver, set _gameOverDampen. Do this at the beginning of the if block, like so:

- (void)checkGameOver:(NSTimeInterval)dt
{
    ...
    if (!_gameOver)
    {
        // 2
        _gameOver = YES;
        _gameOverDampen = 1.0f;
        ...
    }
    ...
}

In the else block, change the code underneath the “// Game Over Label Position” comment to be the following:

float y = fabsf(cosf(_gameOverElapsed * 3.0f)) * 50.0f * _gameOverDampen;
_gameOverDampen = fmaxf(0.0f, _gameOverDampen - 0.3f * dt);
_gameOverLabel.position = CGPointMake(_gameOverLabel.position.x, _winSize.height/2.0f + y);

It’s mostly the same as before, but you multiply the y-value with the damping factor and, simultaneously, you reduce this damping factor slowly from 1.0 back to 0.0 (but never less than 0; that’s what the fmaxf() prevents). Build and run, then try it out!

Where to Go from Here?

Here is the full example project from this Trigonometry for Game Programming tutorial series.

Congratulations, you have peered even deeper into the true natures of sine, cosine and tangent, and you have tried them out to see some examples of how they’re useful in a real game. I hope you’ve seen how handy Trigonometry really is for games!

Note that this series didn’t talk so much about arcsin and arccos. They are much less useful in practice than arctan. One common use for arccos is to find the angle between two arbitrary vectors – for example, to model the reflection of a light beam in a mirror, or to calculate how bright an object should be depending on its angle to a light source.

You can find another great example of the usefulness of trigonometric functions in the Tiny Wings tutorial. It uses cosine to give the hills from the game nicely curved shapes.

If you fancy using your new found skills for more game development, but don’t know where to start, then why not try out our book iOS Games by Tutorials. It will certainly kick start your development!

Drop by the forums to share your successes and agonies with trig. And use your new powers wisely!

Credits: The graphics for this game are based on a free sprite set by Kenney Vleugels. The sound effects are based on samples from freesound.org.

Trigonometry for Game Programming [Sprite Kit Version]: Part 2/2 is a post from: Ray Wenderlich

The post Trigonometry for Game Programming [Sprite Kit Version]: Part 2/2 appeared first on Ray Wenderlich.

Video Tutorial: Objective-C Categories

Video Tutorial: Objective-C Runtime

Video Tutorial: NSString


Video Tutorial: NSArray

Video Tutorial: NSDictionary

Video Tutorial: Beginning Breakpoints

Video Tutorial: Intermediate Breakpoints

Video Tutorial: Advanced Breakpoints

Viewing all 4374 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>