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

How to Make Great App Store Screenshots

$
0
0
Learn how to make beautiful app store screenshots!

Learn how to make beautiful app store screenshots!

What do apps like Cut the Rope 2, 7 Minute Workout Challenge, and Scribblenauts Remix all have in common?

Click the above links to see for yourself. Notice that each has a particularly interesting app store screenshot to lure and entice customers.

Instead of just doing a screen capture of the simulator and calling it a day, the developers added graphics, showed the app running on a device. It has the effect of making you want to download it, right now!

You may be thinking to yourself, “But I don’t want to pay a designer to create fancy screenshots.” or even worse, “I don’t have the graphics skills to make sexy screenshots like that.”

Well, there’s good news. Thanks to a plethora of tools available, creating interesting app-sale-driving screenshots has never been easier or affordable.

Let’s look at a screenshot from Ryan Nystrom’s Weather App:

Pretty nice looking app, right? Clean design, very professional, useful, everything you could want in a nice weather app. A screenshot like this could entice someone to download the app, but what if it was shown at an eye-catching angle with some descriptive text and background image?

Weather_FINAL

Now that looks like a truly impressive app! Just a few small additions transform a plain screen capture into something more grandiose.

What if you took a basic screenshot of Flappy Felipe:

felipe_bg

And spiced it up with an artwork overlay?

felipe_FINAL

This makes the game feel playful, and the Felipe bird acts as a mascot that represents what the app has to offer.

Today, you’ll learn how to create screenshots like these without having to pay a graphic designer and without having to go to art school yourself. It’s a lot easier than you might think.

Getting Started

Here are four tools that you can use to create nice screenshots and promotional images with minimal investment and difficulty:

The first three are pretty much drag-and-drop, but lack flexibility. Photoshop is the most flexible, but also the most time-consuming.

In this article, we’ll take a quick tour of the first three options, then we’ll have a step-by-step tutorial on how to recreate the beautiful screenshots shown above in Photoshop.

Screenshot Maker Pro

Screenshot Maker Pro is a free iOS app that lets you place screenshots into pictures of iOS devices. You can save up to two screenshots per day, or you can purchase the unlimited saving option for $2.99.

The main benefit of Screenshot Maker Pro is that it’s user-friendly. In addition, there are several options for devices on which you can choose to show your screenshots. You can choose from iOS, Android, Windows, Blackberry and even Mac OSX devices to display your screenshots.

screenshotmaker3

The interface is as simple as choosing the image you want to show on a device and then choosing the options such as type of device, reflection, shadow, etc. Within mere seconds, you can create images like these for Flappy Felipe and Ryan’s Weather App:

screenshotMakerPro

Did you notice how showing the apps running on a device at an angle adds a bit of flare and makes them more interesting?

Unfortunately, Screenshot Maker Pro does not allow you to add overlays or text onto your screenshots. You would have to do this in software such as Photoshop or another application.

Also, the screenshots it creates are not sized for any particular app store, so you’ll need another app to size them accordingly. However, it is a useful program to know when you want to create a quick screenshot for a myriad of devices.

Promotee

Promotee is an OSX app that is very similar to Screenshot Maker Pro.

Its drag-and-drop interface lets you place images into pictures of different devices. Unlike Screenshot Maker Pro, it doesn’t give you options for Windows and Blackberry devices.

promotee_UI

To use Promotee, just drag your image into the device you want and click the Save button.

It’s extremely easy to use, but lacks customization options. Similar to Screenshot Maker Pro, it will not export images that are sized to fit any app stores. You’ll need to resize them manually with different software.

promotee

If you’re looking for something that is quick and easy to use, Promotee may be the tool for you. If you’re looking to tinker with customization options, then you may want to look elsewhere.

PlaceIt

PlaceIt is a website that allows you to drag-and-drop your images onto devices, just like Screenshot Maker Pro and Promotee. However, PlaceIt really shines for other reasons, namely, the sheer number of devices and scenarios available.

placeIt_UI

The nice thing about PlaceIt is not only does it put your screenshot inside a device, but it also shows the device in different contexts, like on a table or in someone’s hand. You can even create animated images to show the user how to operate the app.

Pricing ranges from free to $100 per photo, or you can subscribe for $12 to $250+ a month. The price you’ll pay depends on how big you want to go.

placeIt_felipeWeather

Note: If you use any of the images you create on PlaceIt, you are subject to the terms of use found on the company’s website. This includes free downloads, which must adhere to their Free License Agreement.

If you want a variety of still and animated images, PlaceIt is definitely worth checking out. As far as pricing goes, the structure accommodates almost every budget. Best of all, the images look great.

PlaceIt does not offer native support for overlays, text or correct sizing for app stores, much like Screenshot Maker and Promotee. If you’re looking for those options, you’ll need additional tools.

Adobe Photoshop

Adobe Photoshop is going to give you the most features and versatility for your money when it comes to creating screenshots and other images. Wait a second…where are you going?

photoshop

Photoshop is not as complicated as it seems. Also, with Adobe’s new subscription model, you don’t have to make a large initial investment in software, so there’s little risk of buyer’s remorse.

For the rest of this article, we’re switching to tutorial-style, and I’ll show you how to get up close and personal with Adobe Photoshop.

Get ready to have a little fun with Flappy Felipe and Ryan’s weather app!

Installing Photoshop

If you’re not already a Photoshop user, sign up for an Adobe account and download a trial version of Photoshop CC here by clicking Try under Photoshop and Lightroom.

Bring up the installation wizard and follow the steps install Photoshop CC and Adobe Creative Cloud. Creative Cloud lets you install software and save files to their cloud server, should you decide to do so. The feature also makes updates much less disruptive, so it’s good to leave Creative Cloud running in the background so it can do its thing.

adobeCloud

Flappy Felipe Flying in Photoshop

The first enhanced screenshot you’ll create in Photoshop is for the Flappy Felipe game. You’re going to take a screenshot of the game and add some graphics for fun.

To give you a head start, download the Flappy Felipe artwork folder Felipe_START. Save and unzip the Felipe_START folder to your Desktop.

Locate a folder called 8bit_wonder in the Felipe_START folder. You’ll use this font in the Felipe screenshot project, so you need to install that onto your system.

You may also download it from here. For Mac users, double-click the 8-BIT WONDER.TTF file and then click Install. Windows users, if you’re unsure how to install fonts, check out this link.

Open Photoshop once installation finishes. Click on the Window menu option and make sure that your Layers, Paragraph and Characters window panels are open.

window

layers

Note:

Sometimes your windows can get lost amongst the different panes and options in Photoshop. If you ever lose a window, you can always open it and bring it to focus by selecting it in the Window menu.

There are two easy ways to open artwork in Photoshop. The first way is to navigate to File > Open… in Photoshop and then browse for your artwork.

The second way, which is my preferred method, is to simply drag-and-drop the artwork to the Photoshop icon in the dock/toolbar.

draganddrop

Using whichever method you like, open felipe_bg.png, felipe.png and speech_balloon.png in Photoshop.

Each image will open in Photoshop in its own tab. Click on the felipe.png tab, Select All (Cmd+A / Ctrl+A), and Copy (Cmd+C / Ctrl+C).

tabs

Next, click on the felipe_bg.png tab and Paste (Cmd+V / Ctrl+V). Now you have your Felipe mascot on the screenshot!

mascot

Every time you add an element to your image, the program creates a new layer in the Layers panel. This goes for text, shapes, pasted images, etc.

It is wise to get into the habit of naming all of your layers.

Double-click on the text of Layer 1 in the Layers panel and name it Felipe. Now you’ll know where to find Felipe when you need him (besides on RayWenderlich.com, of course).

rename

Using the same steps as before, click on the speech_balloon.png tab, Select All (Cmd+A / Ctrl+A) and Copy (Cmd+C / Ctrl+C).

Click back on the felipe_bg.png tab and Paste (Cmd+V / Ctrl+V). Double-click the text of Layer 1 in the Layers panel and name it balloon.

balloon

So now you know the basics of opening images in Photoshop and combining them into one project, and it’s easy enough.

There’s still one very important step, though. You need to save your project. In fact, save as often as you can. Although Photoshop CC is pretty stable, it would be a shame to lose your work.

Use Cmd+S / Ctrl+S or File/Save to save your new project as felipe_bg.psd in the Felipe_START folder. Your file will now have a .PSD extension (Photoshop Document) which will save the project in its entirety, including the layers.

Note: This will only save the felipe_bg tab. You won’t be using the other tabs anymore so that’s ok, but in the future it will be a good idea to save all of your tabs.

Now that you have all of the pieces you need, it’s time to arrange and add layer styles. In the Photoshop Tools Panel, select the Move Tool (V). If you don’t see the Tools Panel, you can find it by navigating to Window/ Tools.

moveTool

Select the Felipe layer in the Layers panel and move him into the lower-left corner of the project by clicking and dragging in your workspace.

felipeMove

Now select the balloon layer in the Layers panel and move it down by Felipe.

Select the Move Tool to fine-tune your layer positioning using the arrow keys on your keyboard. Holding Shift while pressing the arrows creates large movements and just the arrow keys move the layer in small increments.

balloonMove

Add Some Style

One of Photoshop’s most notable features is the ability to add different styles to layers. For this project, you’re going to make the Felipe mascot and his speech balloon stand out from the background image. Double-click the Felipe layer in the Layers panel and the Layer Style window will appear.

layerstyle

Click the checkboxes next to Stroke and Drop Shadow. As you may have guessed, this will add a line stroke and drop shadow effect to the Felipe mascot. However, you’ll need to customize the effects.

Click on the Stroke tab and set:

  • Size to ’3′
  • Position to ‘Outside’
  • Blend Mode to ‘Normal’
  • Opacity to ’100′
  • Fill Type to ‘Color’ and Color to ‘White’

stroke

To change the color to white, click the box next to Color, which will bring up the Color Picker window. Here you can drag the circle on the left to the upper-left corner and then click OK.

colorChoosing

Now click the Drop Shadow tab and set

  • Blend Mode to ‘Multiply’
  • Opacity to ’75′
  • Angle to ’120′
  • Distance to ’0′
  • Spread to ’45′
  • Size to ’13′
  • Noise to ’0′

dropShadow

Click OK and now you’ll see Felipe outlined in white with a drop shadow and a list of effects under his layer. Looking good, Felipe!

felipeEffect

Now you’ll apply the same layer effect to the balloon layer, but this time you only need to make a couple of clicks. Right-click on the Felipe layer in the Layers window and select Copy Layer Style.

copyStyle

Now right-click the balloon layer and select Paste Layer Style.

pasteStyle

Voila! Now the speech balloon has the same layer style as Felipe, and it was as easy as copying and pasting.

balloonHasStyle

Felipe Has Something to Say

Now, Felipe is an outgoing bird, so you’ll need to add some text to his speech balloon. In the Tools panel, select the Text Tool (T).

textTool

In the Character panel, select the 8BIT WONDER font you installed and set the size to 40.

characterProperties

Then click the box next to Color and change the color to #224200 in the Color Picker Window. Click OK.

changeFontColor

Next, in the Paragraph panel, select Center Text.

paragraph

Make sure your balloon layer is still selected in the Layers window, and that your Text tool is still selected from the Tools panel. Then click and drag a rectangle over the balloon layer to create a text box.

textBox

Now type (without quotes) “FANTASTIC FELIPE FLAPPING FUN” into the text box you just created. If your text runs outside of the box, you can use the squares on the text box’s border to resize the text area.

felipeFun

Click on the Move Tool in the Tools Panel and adjust the position of the text with the arrow keys on your keyboard as needed. You’ll notice that the text layer has now automatically changed its name from “Layer 1″ to “FANTASTIC FELIPE FLAPPING FUN”, so there’s no need to change the layer name.

textNameChange

And there you have it, your very own screenshot with a cartoon overlay!

All that is left is to Cmd+S/CTRL+S to save your changes to the .PSD file. Then go to File /Save For Web…, which will bring up the Save For Web (Cmd+Option+Shift+S) window. Here you can save it as a PNG-24 file and send it off to the App Store.

If you want to compare PSD files, you can download the completed version here.

saveForWeb

What’s the Weather Like Today?

Now that you’ve mastered essential Photoshop skills, this next lesson shouldn’t take long to complete. This screenshot is going to combine the use of Screenshot Maker Pro and Photoshop to make a unique image.

Download the source images here. Save and unzip the folder to your Desktop and open it.

Here you’ll see screenshot.png, which was created using Screenshot Maker Pro and bg.png, which is just part of a picture taken for this tutorial. Open both images in Photoshop.

weatherApp1

Now you need to past the screenshot.png image on top of the bg.png image.

  • Select the screenshot.png tab
  • Select All, and then Copy
  • Then select the bg.png tab and Paste

weatherApp2

  • Double-click Layer 1
  • Rename it screenshot
  • Cmd+S to save your PSD file to the Weather_START folder
  • Name your new Photoshop document Weather.psd

As noted before, software such as Screenshot Maker Pro, Promotee and PlaceIt do not export images to the exact sizes that app stores require. However, in Photoshop, you set the size of the image:

  • Open the Image Size window by navigating to Image/Image Size… or use Cmd+Option+I
  • Change the width to 1024 and height to 768
  • Click OK

weatherappresize

If you want to see the actual size of the image, navigate to View/100% and you’ll see your image in all of its 1024×768 glory.

100percent

If you’re on a Mac, change the font to Helvetica Neue, Thin, 60px in the Character panel. If you’re on a PC and don’t have Helvetica Neue, choose a comparable thin, sans-serif font.

helvetica

In the Paragraph panel, select Center Text.

center

Select the Text Tool (T) in the Tools Panel and in the lower-right corner of the image, place a rectangle text box.

weatherText

Now type (without quotes) “Live Weather Updates” with “Updates” going on its own line. If it doesn’t fit, resize the text box by dragging its corner until it fits neatly.

liveupdates

Select the Move Tool from the Tools Panel to fine-tune the text’s position and there you go! Now File/Save For Web… (Cmd+Option+Shift+S) and save as a PNG-24 file. Make sure the size is set to 1024 x 768.

Just like that, you’ve created two enhanced screenshots ready for the App Store!

finalWeather

If you’d like to compare PSD files, you can download the final version here.

Where to Go From Here?

Always remember to take advantage of every available slot for screenshots when you upload to an app store.

If you’re allowed 5 screenshots, don’t rest on your laurels at 3 or 4, go big and use all 5 because just having a couple more can make a big difference to your sales and downloads.

Browse the app stores and look at your favorite apps’ screenshots.

  • Are they just plain screen grabs from the simulator or are they enhanced in some way?
  • Are they shown on an actual device? Do they have cartoony overlays or mascots?
  • Do any of the pictures show a user interacting with the app or use animation to show how it works?

Use the ideas you see to inspire you as you create screenshots for your apps.
Try to replicate some of the effects you see from popular apps’ screenshots in Photoshop using the tools that you’ve learned in this tutorial. A great exercise is to use the .png graphic files from your app and combine them in Photoshop to see what you can create.

Don’t be afraid to try out new tools that you find online for screenshot creation. You never know if you’ll find the one that works the best for you.

Thanks for taking the time to join me for this tutorial. If you have questions or ideas that would help other developers, or perhaps just want to show off your new screenshot skills, feel free to leave your thoughts below.

How to Make Great App Store Screenshots is a post from: Ray Wenderlich

The post How to Make Great App Store Screenshots appeared first on Ray Wenderlich.


Swift Tutorial Part 3: Tuples, Protocols, Delegates, and Table Views

$
0
0
Create a simple iOS app in this Swift tutorial!

Create a simple iOS app in this Swift tutorial!

Welcome back to our Swift tutorial series!

In the first Swift tutorial, you learned the basics of the Swift language, and created your very own tip calculator class.

In the second Swift tutorial, you created a simple user interface for the tip calculator.

In this third Swift tutorial, you will learn about a new data type introduced in Swift: tuples.

You will also learn about protocols and delegates, table views, and how to prototype your user interfaces in Playgrounds.

This tutorial picks up where the second Swift tutorial left off. If you don’t have it already, be sure to download the sample project where you left it off last time.

Note: At the time of writing this Swift tutorial, it is unclear to us if we are allowed to post screenshots of Xcode 6 since it is still in beta. Therefore, we are suppressing screenshots in this Swift tutorial until we are sure it is OK.

Getting Started

So far, your Tip Calculator gives a proposed tip for each tip percentage. However, once you select the tip you want to pay, you have to add the tip to the bill total in your head – which kind of defeats the point!

It would be nice if your calcTipWithTipPct() method returned two values: the tip amount, and the bill total with tip applied.

In Objective-C, if you want to make a method return two values, you either have to make an Objective-C object with two properties for the return values, or you have to return a dictionary containing the two values. In Swift, there’s an alternative way: Tuples.

Let’s play around with Tuples a bit so you can get a feel for how they work. Create a new Playground in Xcode (or if you followed my advice in the first Swift tutorial, simply click on the Playground you have saved to your dock). Delete everything from your Playground so you start from a clean slate.

Unnamed Tuples

Let’s start by creating something called an Unnamed Tuple. Enter the following into your playground:

let tipAndTotal = (4.00, 25.19)

Here you group two Doubles (tip and total) into a single Tuple value. This uses inferred syntax since the compiler is able to automatically determine the type based on the initial value that you’re setting it to. Alternatively you could have written the line explicitly like this:

let tipAndTotal:(Double, Double) = (4.00, 25.19)

To access the individual elements of your tuple, you have two options: accessing by index, or decomposing by name. Add the following to the playground to try the first:

tipAndTotal.0
tipAndTotal.1

You should see 4.0 and 25.19 in the sidebar of your playground. Accessing by index works in a pinch, but isn’t as clean as decomposing by name. Add the following to your playground to try the latter:

let (theTipAmt, theTotal) = tipAndTotal
theTipAmt
theTotal

This syntax allows you to make a new constant that refers to each element in the tuple, with a particular name.

Named Tuples

Unnamed Tuples work, but as you saw takes some extra code to access each item by name.

It is often even more convenient to use Named Tuples instead, where you name your tuples at declaration time. Add the following lines to your playground to try:

let tipAndTotalNamed = (tipAmt:4.00, total:25.19)
tipAndTotalNamed.tipAmt
tipAndTotalNamed.total

As you can see this is a lot more convenient, and this is what we’ll be using for the rest of this tutorial.

Finally I’d like to point out that again you are using inferred syntax for the declaration of tipAndTotalNamed. If you wanted to use explicit syntax, it would look like this:

let tipAndTotalNamed:(tipAmt:Double, total:Double) = (4.00, 25.19)

Note that when you use explicit syntax, naming the variables on the right hand side is optional.

Returning Tuples

Now that you understand the basics of Tuples, let’s see how you can use them in your tip calculator to return two values.

Add the following code to your playground:

let total = 21.19
let taxPct = 0.06
let subtotal = total / (taxPct + 1)
func calcTipWithTipPct(tipPct:Double) -> (tipAmt:Double, total:Double) {
  let tipAmt = subtotal * tipPct
  let finalTotal = total + tipAmt
  return (tipAmt, finalTotal)
}
calcTipWithTipPct(0.20)

This is the same calcTipWithTipPct method you’ve been working with, except instead of returning a Double, it returns (tipAmt:Double, total:Double).

Here is the Playground file up to this point. Now, delete everything from your playground to start at a fresh slate.

A Full Prototype

At this point, you’re ready to take what you’ve learned and integrate it into your TipCalculatorModel class.

But before you start modifying your TipCalculator Xcode project, let’s try out the changes in this playground!

Copy your TipCalculatorModel class from your TipCalculator project into this playground. Then, modify the calcTipWithTipPct the same way you did earlier. Finally, modify returnPossibleTips to return a dictionary of Ints to Tuples instead of Ints to Doubles.

See if you can figure this out on your own; it’s good practice. But if you get stuck, check out the spoiler below!

Solution Inside: TipCalculatorModel - Modified SelectShow>


Here is the Playground file up to this point.

At this point, save your file and start up a new empty Playground. We’ll be returning to this Playground later.

Protocols

The next step is to prototype a table view for your app. But before you do that, you need to understand the concepts of protocols and delegates. Let’s start with protocols.

A protocol is a list of methods that specify a “contract” or “interface”. Add these lines to your Playground to see what I mean:

protocol Speaker {
  func Speak()
}

This protocol declares a single method called Speak.

Any class that conforms to this protocol must implement this method. Try this by adding two classes that conform to your protocol:

class Vicki: Speaker {
  func Speak() {
    println("Hello, I am Vicki!")
  }
}
 
class Ray: Speaker {
  func Speak() {
    println("Yo, I am Ray!")
  }
}

To mark a class as conforming to a protocol, you must put a colon after the class name, and then list the protocol (after the name of the class it inherits from, if any). These classes do not inherit from any other class, so you can just list the name of the protocol directly.

Also notice that if you do not include the Speak function, you will get a compiler error.

Now try this for a class that does inherit from another class:

class Animal {
}
class Dog : Animal, Speaker {
  func Speak() {
    println("Woof!")
  }
}

In this example, Dog inherits from Animal, so when declaring Dog you put a :, then the class it inherits from, then list any protocols. You can only inherit from 1 class in Swift, but you can conform to any number of protocols.

Optional Protocols

You can mark methods in a protocol as being optional. To try this, replace the Speaker protocol with the following:

@objc protocol Speaker {
  func Speak()
  @optional func TellJoke()
}

If you want to have a protocol with optional methods, you must prefix the protocol with the @objc tag (even if your class is not interoperating with objective-C). Then, you prefix any method that can be optional with the @optional tag.

Note there is currently no compiler error for your Person or Dog class, because the new function is optional.

In this example, Ray or Vicki can tell a joke, but sadly not a Dog. So implement this method only on those two classes:

class Vicki: Speaker {
  func Speak() {
    println("Hello, I am Vicki!")
  }
  func TellJoke() {
    println("Q: What did Sushi A say to Sushi B?")
  }
}
 
class Ray: Speaker {
  func Speak() {
    println("Yo, I am Ray!")
  }
  func TellJoke() {
    println("Q: Whats the object-oriented way to become wealthy?")
  }
  func WriteTutorial() {
    println("I'm on it!")
  }
}

Note that when you’re implementing a protocol, of course your class can have more methods than just the ones in the protocol if you want. Here the Ray class has an extra method.

Oh, and can you guess the answer to these jokes?

Solution Inside SelectShow>


Using Protocols

Now that you’ve created a protocol and a few classes that have implemented them, let’s try using them. Add the following lines to your playground:

var speaker:Speaker
speaker = Ray()
speaker.Speak()
// speaker.WriteTutorial() // error!
(speaker as Ray).WriteTutorial()
speaker = Vicki()
speaker.Speak()

Note that rather than declaring speaker as Ray, you declare it as speaker. This means you can only call methods on speaker that exist in the Speaker protocol, so calling WriteTutorial would be an error even though speaker is actually of type Ray. You can call WriteTutorial if you cast speaker back to Ray temporarily though, as you see here.

Also note that you can set speaker to Vicki as well, since Vicki also conforms to Speaker.

Now add these lines to experiment with the optional method:

speaker.TellJoke?()
speaker = Dog()
speaker.TellJoke?()

Remember that TellJoke is an optional method, so before you call it you should check if it exists.

These lines use use a technique called optional chaining to do this. If you put a ? mark after the method name, it will check if it exists before calling it. If it does not exist, it will behave as if you’ve called a method that returns nil.

Optional chaining is a useful technique anytime you want to test if an optional value exists before using it, as an alternative to the if let (optional binding) syntax discussed earlier. You will be using this much more in the rest of this series and other Swift tutorials on this site.

Delegates

A delegate is simply a variable that conforms to a protocol, which a class typically uses to notify of events or perform various sub-tasks. Think of it like a boss giving his minion status updates, or asking him/her to do something!

To see what I mean, add a new DateSimulator class to your playground, that allows two classes that conform to Speaker to go on a date:

class DateSimulator {
 
  let a:Speaker
  let b:Speaker
 
  init(a:Speaker, b:Speaker) {
    self.a = a
    self.b = b
  }
 
  func simulate() {
    println("Off to dinner...")
    a.Speak()
    b.Speak()
    println("Walking back home...")
    a.TellJoke?()
    b.TellJoke?()
  }
}
 
let sim = DateSimulator(a:Vicki(), b:Ray())
sim.simulate()

Imagine you want to be able to notify another class when the date begins or ends. This could be useful if you wanted to make a status indicator in your app appear or disappear when this occurs, for example.

To do this, first create a protocol with the events you want to notify, like so (add this before DateSimulator):

protocol DateSimulatorDelegate {
  func dateSimulatorDidStart(sim:DateSimulator, a:Speaker, b:Speaker)
  func dateSimulatorDidEnd(sim:DateSimulator, a: Speaker, b:Speaker)
}

Then create a class that conforms to this protocol (add this right after the DateSimulatorDelegate):

class LoggingDateSimulator:DateSimulatorDelegate {
  func dateSimulatorDidStart(sim:DateSimulator, a:Speaker, b:Speaker) {
    println("Date started!")
  }
  func dateSimulatorDidEnd(sim:DateSimulator, a: Speaker, b: Speaker)  {
    println("Date ended!")
  }
}

For simplicity, you’ll simply log out these events.

Then, add a new property to DateSimulator that takes a class that conforms to this protocol:

var delegate:DateSimulatorDelegate?

This is an example of a delegate. Again, a delegate is just some class that implements a protocol, that you might want to notify about events, or have it do certain tasks on your behalf.

Note that you are making it optional here, as the DateSimulator should be able to work fine, whether or not the delegate is set.

Right before the line sim.simulate(), set this variable to your LoggingDateSimulator:

sim.delegate = LoggingDateSimulator()

Finally, modify your simulate() function to call the delegate appropriately at the beginning and end of the method.

Try to do this part yourself, as it’s good practice! Note that you should check if delegate is set before using it – I recommend you try out optional chaining to solve this.

Solution Inside: Solution: Complete DateSimulator Class SelectShow>

Here is the playground file up to this point.

Now, save your file and return back to the playground file you saved earlier in this tutorial with your new and improved TipCalculatorModel class in it. It’s time to put this into a table view!

Table Views, Delegates, and Data Sources

Now that you understand the concepts of protocols and delegates, you are ready to use table views in your app.

It turns out table views have a property called delegate – you can set it to a class that conforms to UITableViewDelegate. This is a protocol with a bunch of optional methods to let you know about events like when a row is selected, or when the table view enters edit mode.

Table views also have another property called dataSource – you can set it to a class that conforms to UITableViewDataSource. The difference is instead of notifying this class about events, the table view asks it for data – like how many rows to display, and what should be displayed in each row.

The delegate is optional, but the dataSource is required. So let’s try this out by creating a data source for your tip calculator.

One of the cool things about playgrounds is you can actually prototype and visualize views (like UITableView there as well! This is a great way to make sure it’s working before integrating it into your main project.

Again, make sure you’re in the playground with your new and improved TipCalculatorModel class. Then add this code to the bottom of the file:

// 1
import UIKit
// 2
class TestDataSource : NSObject {
 
  // 3
  let tipCalc = TipCalculatorModel(total: 33.25, taxPct: 0.06)
  var possibleTips = Dictionary<Int, (tipAmt:Double, total:Double)>()
  var sortedKeys:Int[] = []
 
  // 4
  init() {
    possibleTips = tipCalc.returnPossibleTips()
    sortedKeys = sort(Array(possibleTips.keys))
    super.init()
  }
 
}

Let’s go over this section by section.

  1. In order to use UIKit classes like UITableView, you first need to import the UIKit framework.
  2. One of the requirements of implementing UITableViewDataSource is that your class extends NSObject (either directly or through intermediate classes).
  3. Here you initialize the tip calculator, and make an empty array for the possible tips and sorted keys. Note that you are keeping possibleTips and sortedKeys as variables (not constants) because in the actual app you’ll want these to be able to change over time.
  4. In init, you set up the two variables with initial values.

Now that you have a base, let’s make the class conform to UITableViewDataSource. To do this, first add the data source to the end of the class declaration:

class TestDataSource: NSObject, UITableViewDataSource {

Then add this two new method:

func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
  return sortedKeys.count
}

This is one of the two required methods you must implement to conform to UITableViewDataSource. It asks you how many rows are in each section of the table view. This table view will only have 1 section, so you return the number of values in sortedKeys (i.e. the the number of possible tip percentages).

Next, add the other required method:

// 1
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
  // 2
  let cell = UITableViewCell(style: UITableViewCellStyle.Value2, reuseIdentifier: nil)
 
  // 3
  let tipPct = sortedKeys[indexPath.row]
  // 4
  let tipAmt = possibleTips[tipPct]!.tipAmt
  let total = possibleTips[tipPct]!.total
 
  // 5
  cell.textLabel.text = "\(tipPct)%:"
  cell.detailTextLabel.text = String(format:"Tip: $%0.2f, Total: $%0.2f", tipAmt, total)
  return cell
}

Let’s go over this section by section:

  1. This method is called for each row in your table view. You need to return the view that represents this row, which is a subclass of UITableViewCell.
  2. You can create table view cells with a built-in style, or create your own custom subclasses for your own style. Here you are creating a table view cell with a default style – UITableViewCellStyle.Value2 in particular. Note that inferred typing allows you to shorten this to just .Value2 if you want, but I’m keeping the long form here for clarity.
  3. One of the parameters to this method is indexPath, which is a simple collection of the section and row for this table view cell. Since there’s just one section, you use the row to pull out the appropriate tip percentage to display from sortedKeys.
  4. Next you want to make a variable for each element in the Tuple for that tip percentage. Remember from the previous tutorial that when you access an item in a dictionary you get an optional value, since it is possible that there is nothing in the dictionary for that particular key. However, you are sure there is something for that key in this case so use the ! for forced unwrapping.
  5. Finally, the built-in UITableViewCell has two properties for its sublabels: textLable and detailTextLabel. Here you set these and return the cell.

Finally, test your table view by adding the following code to the bottom of your playground:

let testDataSource = TestDataSource()
let tableView = UITableView(frame:CGRect(x: 0, y: 0, width: 320, height: 320), style:.Plain)
tableView.dataSource = testDataSource
tableView.reloadData()

This creates a table view of a hardcoded size, and sets its data source to your new class. It then calls reloadData() to refresh the table view.

Move your mouse over to the sidebar of the playground, to the final line, and click on the eyeball next to UITableView. You should see a neat preview showing your table view with your tip calculator results!

Here is the playground file up to this point.

You’re done prototyping the new and improved TipCalculatorModel and your new table view. It’s time to integrate this code back into your main project!

Finishing Touches

Open your TipCalculator project, and copy the new and improved TipCalculatorModel overtop your existing implementation.

Next, open Main.storyboard, select your Text View (the results area) and delete it. From the Object Library, drag a Table View (not a Table View Controller) into your view controller. Set X=0, Y=187, Width=580, Height=293.

Re-set up Auto Layout by clicking the fourth button in the bottom right of Interface Builder (that looks like a Tie Fighter) and click Clear Constraints and then again with Add Missing Constraints to View Controller.

Finally, select your table view, and select the sixth Inspector (the Connections Inspector). You’ll see two entries for dataSource and delegate – drag the buttons to the right of those over to your view controller in the Document Outline.

Now your view controller is set as both the data source and the delegate of the view controller – similar to how you set the data source of the table view to your test class programmatically earlier.

Finally, make sure your Assistant Editor is open and showing ViewController.swift. Control-drag from the table view down below your final outlet. A popup will appear – enter tableView for the name, and click Connect. Now you can refer to your table view in code.

Now for the code modifications. Open ViewController.swift, and mark your class as conforming to UITableViewDataSource:

class ViewController: UIKit.UIViewController, UITableViewDataSource {

Then, add two new variables below tipCalc:

var possibleTips = Dictionary<Int, (tipAmt:Double, total:Double)>()
var sortedKeys:Int[] = []

Replace calculateTapped() with the following:

@IBAction func calculateTapped(sender : AnyObject) {
  tipCalc.total = Double(totalTextField.text.bridgeToObjectiveC().doubleValue)
  possibleTips = tipCalc.returnPossibleTips()
  sortedKeys = sort(Array(possibleTips.keys))
  tableView.reloadData()
}

This sets up possibleTips and sortedKeys and triggers a reload of the table view.

Delete the line that sets the resultsTextView in refreshUI().

Copy your two table view methods from your playground into your class, and finally delete all comments from the class.

I’ve attached a spoiler below with the completed class, in case you want to check your work.

Solution Inside: Completed ViewController.swift SelectShow>

Build and run, and enjoy the new look of your tip calculator!

Where To Go From Here?

Here is the finished Xcode project from this tutorial series so far.

Congratulations, you have learned a lot in this tutorial! You have learned about Tuples, Protocols and Delegates, and Table Views, and have upgraded the look and functionality of your Tip Calculator class.

Stay tuned for a bunch more Swift tutorials. We’ll be showing you how you can work with table views, Sprite Kit, some iOS 8 APIs, and much more!

In the meantime, if you want to learn more here are some great resources to check out:

Thanks for reading this tutorial, and if you have any comments or questions please join in the forum discussion below!

Swift Tutorial Part 3: Tuples, Protocols, Delegates, and Table Views is a post from: Ray Wenderlich

The post Swift Tutorial Part 3: Tuples, Protocols, Delegates, and Table Views appeared first on Ray Wenderlich.

Video Tutorial: Introduction to Swift Part 4: Dictionaries

Video Tutorial: Introduction to Swift Part 5: Control Flow

How to Make a Game Like Candy Crush with Swift Tutorial: Part 1

$
0
0
Learn to make a tasty match-3 game in the new Swift language

Learn to make a tasty match-3 game in the new Swift language

A little while back, I wrote an Objective-C tutorial about how to make a game like the Candy Crush Saga, which is a very popular casual match-3 game.

But I thought it would be great to make a Swift version as well, hence this post!

In this Swift tutorial, you’ll learn how to make a game like Candy Crush named Cookie Crunch Adventure. Yum, that sounds even better than candy!

In the process of going through this tutorial, you’ll get some excellent practice with Swift techniques such as enums, generics, subscripting, closures, and extensions. You’ll also learn a lot about game architecture and best practices.

This is Part One of a two-part series. In this first part, you’ll put the foundation in place: the gameplay view, the sprites and some of the logic for detecting swipes and swapping cookies.

In the second part, you’ll complete the gameplay and add the final polish to transform Cookie Crunch Adventure into a game of top-10 quality.

Note: This Swift tutorial assumes you have working knowledge of Sprite Kit and Swift. If you’re new to Sprite Kit, check out the beginner tutorials on the site or our book, iOS Games by Tutorials. For an introduction to Swift, see our Swift tutorial.

This tutorial requires Xcode 6 beta 2 or later. At the time of writing this tutorial, it is unclear to us if we are allowed to post screenshots of Xcode 6 since it is still in beta. Therefore, we are suppressing Xcode 6 screenshots in this Swift tutorial until we are sure it is OK. (Any Xcode screenshots shown are taken from the Objective-C version of this tutorial.)

Getting Started

Before you continue, download the resources for this Swift tutorial and unpack the zip file. You’ll have a folder containing all the images and sound effects you’ll need later on.

Start up Xcode 6, go to File\New\Project…, choose the iOS\Application\Game template and click Next. Fill out the options as follows:

  • Product Name: CookieCrunch
  • Language: Swift
  • Game Technology: SpriteKit
  • Devices: iPhone

Click Next, choose a folder for your project and click Create.

This is a portrait-only game, so open the Target Settings screen and in the General tab, make sure only Portrait is checked in the Device Orientation section:

Device orientation

To start importing the graphics files, go to the Resources folder you just downloaded and drag the Sprites.atlas folder into Xcode’s Project Navigator. Make sure Destination: Copy items if needed is checked.

You should now have a blue folder in your project:

Sprites atlas in project navigator

Xcode will automatically pack the images from this folder into a texture atlas when it builds the game. Using a texture atlas as opposed to individual images will dramatically improve your game’s drawing performance.

Note: To learn more about texture atlases and performance, check out Chapter 25 in iOS Games by Tutorials, “Sprite Kit Performance: Texture Atlases”.

There are a few more images to import, but they don’t go into a texture atlas. This is because they are either large full-screen background images (which are more efficient to keep outside of the texture atlas) or images that you will later use from UIKit controls (UIKit controls cannot access images inside texture atlases).

From the Resources/Images folder, drag each of the individual images into the asset catalog:

Images in asset catalog

Delete the Spaceship image from the asset catalog. This is a sample image that came with the template but you won’t need any spaceships while crunching those tasty cookies! :]

Outside of the asset catalog in the Project Navigator, delete GameScene.sks. You won’t be using Xcode’s built-in level editor for this game.

Great! It’s time to write some code. Replace the contents of GameViewController.swift with the following:

import UIKit
import SpriteKit
 
class GameViewController: UIViewController {
  var scene: GameScene!
 
  override func prefersStatusBarHidden() -> Bool {
    return true
  }
 
  override func shouldAutorotate() -> Bool {
    return true
  }
 
  override func supportedInterfaceOrientations() -> Int {
    return Int(UIInterfaceOrientationMask.AllButUpsideDown.toRaw())
  }
 
  override func viewDidLoad() {
    super.viewDidLoad()
 
    // Configure the view.
    let skView = view as SKView
    skView.multipleTouchEnabled = false
 
    // Create and configure the scene.
    scene = GameScene(size: skView.bounds.size)
    scene.scaleMode = .AspectFill
 
    // Present the scene.
    skView.presentScene(scene)
  }
}

This is mostly boilerplate code that creates the Sprite Kit scene and presents it in the SKView.

For the final piece of setup, replace the contents of GameScene.swift with this:

import SpriteKit
 
class GameScene: SKScene {
  init(size: CGSize) {
    super.init(size: size)
 
    anchorPoint = CGPoint(x: 0.5, y: 0.5)
 
    let background = SKSpriteNode(imageNamed: "Background")
    addChild(background)
  }
}

This loads the background image from the asset catalog and places it in the scene. Because the scene’s anchorPoint is (0.5, 0.5), the background image will always be centered on the screen on both 3.5-inch and 4-inch devices.

Build and run to see what you’ve got so far. Excellent!

Background image

Can I Have Some Cookies?

This game’s playing field will consist of a grid, 9 columns by 9 rows. Each square of this grid can contain a cookie.

2D grid

Column 0, row 0 is in the bottom-left corner of the grid. Since the point (0,0) is also at the bottom-left of the screen in Sprite Kit’s coordinate system, it makes sense to have everything else “upside down”—at least compared to the rest of UIKit. :]

Note: Wondering why Sprite Kit’s coordinate system is different than UIKit’s? This is because OpenGL ES’s coordinate system has (0, 0) at the bottom-left, and Sprite Kit is built on top of OpenGL ES.

To learn more about OpenGL ES, we have a video tutorial series for that.

To being implementing this, you need to create the class representing a cookie object. Go to File\New\File…, choose the iOS\Source\Swift File template and click Next. Name the file Cookie.swift and click Create.

Replace the contents of Cookie.swift with the following:

import SpriteKit
 
enum CookieType: Int {
  case Unknown = 0, Croissant, Cupcake, Danish, Donut, Macaroon, SugarCookie
}
 
class Cookie {
  var column: Int
  var row: Int
  let cookieType: CookieType
  var sprite: SKSpriteNode?
 
  init(column: Int, row: Int, cookieType: CookieType) {
    self.column = column
    self.row = row
    self.cookieType = cookieType
  }
}

The column and row properties let Cookie keep track of its position in the 2-D grid.

The sprite property is optional, hence the question mark after SKSpriteNode, because the cookie object may not always have its sprite set.

The cookieType property describes the—wait for it—type of the cookie, which takes a value from the CookieType enum. The type is really just a number from 1 to 6, but wrapping it in an enum allows you to work with easy-to-remember names instead of numbers.

You will deliberately not use cookie type Unknown (value 0). This value has a special meaning, as you’ll learn toward the end of this part of the tutorial.

Each cookie type number corresponds to a sprite image:

Cookie types

In Swift, an enum isn’t just useful for associating symbolic names with numbers; you can also add functions and computed properties to an enum. Add the following code inside the enum CookieType:

var spriteName: String {
  let spriteNames = [
    "Croissant",
    "Cupcake",
    "Danish",
    "Donut",
    "Macaroon",
    "SugarCookie"]
 
  return spriteNames[toRaw() - 1]
}
 
var highlightedSpriteName: String {
  let highlightedSpriteNames = [
    "Croissant-Highlighted",
    "Cupcake-Highlighted",
    "Danish-Highlighted",
    "Donut-Highlighted",
    "Macaroon-Highlighted",
    "SugarCookie-Highlighted"]
 
  return highlightedSpriteNames[toRaw() - 1]
}

The spriteName property returns the filename of the corresponding sprite image in the texture atlas. In addition to the regular cookie sprite, there is also a highlighted version that appears when the player taps on the cookie.

The spriteName and highlightedSpriteName properties simply look up the name for the cookie sprite in an array of strings. To find the index, you use toRaw() to convert the enum’s current value to an integer. Recall that the first useful cookie type, Croissant, starts at 1 but arrays are indexed starting at 0, so you need to subtract 1 to find the correct array index.

Every time a new cookie gets added to the game, it will get a random cookie type. It makes sense to add that as a function on CookieType. Add the following to the enum as well:

static func random() -> CookieType {
  return CookieType.fromRaw(Int(arc4random_uniform(6)) + 1)!
}

This calls arc4random_uniform() to generate a random number between 0 and 5, then adds 1 to make it a number between 1 and 6. Because Swift is very strict, the result from arc4random_uniform() (an UInt32) must first be converted to an Int, and then fromRaw() can convert this number into a proper CookieType value.

Now, you may wonder why you’re not making Cookie a subclass of SKSpriteNode. After all, the cookie is something you want to display on the screen.

If you’re familiar with the model-view-controller (or MVC) pattern, think of Cookie as a model object that simply describes the data for the cookie. The view is a separate object, stored in the sprite property.

This kind of separation between data models and views is something you’ll use consistently throughout this tutorial. The MVC pattern is more common in regular apps than in games but, as you’ll see, it can help keep the code clean and flexible.

Printing Cookies

If you were to use println() to print out a Cookie at the moment, it wouldn’t look very nice.

What you’d like is to customize the output of when you print a cookie. You can do this by making the Cookie conform to the Printable protocol.

To do this, modify the declaration of Cookie as follows:

class Cookie: Printable {

Then add a computed property named description:

var description: String {
  return "type:\(cookieType) square:(\(column),\(row))"
}

Now println() will print out something helpful: the type of cookie and its column and row in the level grid. You’ll use this in practice later.

print_cookies

Let’s also make the CookieType enum printable. Add the Printable protocol to the enum definition and have the description property return the sprite name, which is a pretty good description of the cookie type:

enum CookieType: Int, Printable {
  ...
  var description: String {
    return spriteName
  }
}

Keeping the Cookies: the 2-D Grid

Now you need something to hold that 9×9 grid of cookies. The Objective-C version of this tutorial did this,

Cookie *_cookies[9][9];

to create a two-dimensional array of 81 elements. Then you could simply do myCookie = _cookies[3][6]; to find the cookie at column 3, row 6.

Swift arrays, however, work quite differently from plain old C arrays and the above is not possible. Fortunately, you can write your own class that acts like a 2-D array and that is just as convenient to use.

Go to File\New\File…, choose the iOS\Source\Swift File template and click Next. Name the file Array2D.swift and click Create.

Replace the contents of Array2D.swift with the following:

class Array2D<T> {
  let columns: Int
  let rows: Int
  let array: Array<T?>  // private
 
  init(columns: Int, rows: Int) {
    self.columns = columns
    self.rows = rows
    array = Array<T?>(count: rows*columns, repeatedValue: nil)
  }
 
  subscript(column: Int, row: Int) -> T? {
    get {
      return array[row*columns + column]
    }
    set {
      array[row*columns + column] = newValue
    }
  }
}

The notation Array2D<T> means that this class is a generic; it can hold elements of any type T. You’ll use Array2D to store Cookie objects, but later on in the tutorial you’ll use another Array2D to store a different type of object, Tile.

Array2D‘s initializer creates a regular Swift Array with a count of rows x columns and sets all these elements to nil. When you want a value to be nil in Swift, it needs to be declared optional, which is why the type of the array property is Array<T?> and not just Array<T>.

What makes this class easy to use is that is supports subscripting. If you know the column and row numbers of a specific item, you can index the array as follows: myCookie = cookies[column, row]. Sweet!

Get Ready, Get Set…

There is one more helper class you need to write. In Xcode 6 beta 2, Swift does not come with a native set type. A set is a collection, like an array, but it allows each element to appear only once, and it does not store the elements in any particular order.

You could use an NSSet for this purpose, but that doesn’t take advantage of Swift’s strict type checking. Fortunately, it’s not hard to write your own.

Go to File\New\File…, choose the iOS\Source\Swift File template and click Next. Name the file Set.swift and click Create.

Replace the contents of Set.swift with the following:

class Set<T: Hashable>: Sequence, Printable {
  var dictionary = Dictionary<T, Bool>()  // private
 
  func addElement(newElement: T) {
    dictionary[newElement] = true
  }
 
  func removeElement(element: T) {
    dictionary[element] = nil
  }
 
  func containsElement(element: T) -> Bool {
    return dictionary[element] != nil
  }
 
  func allElements() -> T[] {
    return Array(dictionary.keys)
  }
 
  var count: Int {
    return dictionary.count
  }
 
  func unionSet(otherSet: Set<T>) -> Set<T> {
    var combined = Set<T>()
 
    for obj in dictionary.keys {
      combined.dictionary[obj] = true
    }
 
    for obj in otherSet.dictionary.keys {
      combined.dictionary[obj] = true
    }
 
    return combined
  }
 
  func generate() -> IndexingGenerator<Array<T>> {
    return allElements().generate()
  }
 
  var description: String {
    return dictionary.description
  }
}

There’s a lot of code here but it’s all quite straightforward. You can add elements to the set, remove elements, and inspect which elements are currently in the set. The unionSet() function is used to combine two sets into a new one.

To guarantee that each element appears only once, Set uses a dictionary. As you probably know, a dictionary stores key-value pairs, and keys must be unique. Set stores its elements as the keys of the dictionary, not as values, guaranteeing that each element appears only once.

Set also conforms to the Sequence protocol and implements generate() to return a so-called “generator” object. This allows you to use the set in for-in loops. Very handy!

The Level Class

That’s the preliminaries out of the way. Let’s put Array2D and Set to use.

Go to File\New\File…, choose the iOS\Source\Swift File template and click Next. Name the file Level.swift and click Create.

Replace the contents of Level.swift with the following:

import Foundation
 
let NumColumns = 9
let NumRows = 9
 
class Level {
  let cookies = Array2D<Cookie>(columns: NumColumns, rows: NumRows)  // private
}

This declares two constants for the dimensions of the level, NumColumns and NumRows, so you don’t have to hardcode the number 9 everywhere.

The property cookies is the two-dimensional array that holds the Cookie objects, 81 in total (9 rows of 9 columns). It is defined with let, not var, because once it’s set the value of cookies never has to change; it will always have the same Array2D instance.

The cookies array is supposed to be private, so Level needs to provide a way for others to obtain a cookie object at a specific position in the level grid. (Note: Swift currently does not support a way to mark properties as private.)

Add the code for this method to Level.swift:

func cookieAtColumn(column: Int, row: Int) -> Cookie? {
  assert(column >= 0 && column < NumColumns)
  assert(row >= 0 && row < NumRows)
  return cookies[column, row]
}

Using cookieAtColumn(3, row: 6) you can ask the Level for the cookie at column 3, row 6. Behind the scenes this asks the Array2D for the cookie and then returns it. Note that the return type is Cookie?, an optional, because not all grid squares will necessarily have a cookie (they may be nil).

Notice the use of assert() to verify that the specified column and row numbers are within the valid range of 0-8.

Note: New to assert? The idea behind assert is you give it a condition, and if the condition fails the app will crash with a log message.

“Wait a minute,” you may think, “why would I want to crash my app on purpose?!”

Crashing your app on purpose is actually a good thing if you have a condition that you don’t expect to ever happen in your app like this one. assert will help you because when the app crashes, the backtrace will point exactly to this unexpected condition, making it nice and easy to resolve the source of the problem.

Now to fill up that cookies array with some cookies! Later on you will learn how to read level designs from a JSON file but for now, you’ll fill up the array yourself, just so there is something to show on the screen.

Add the following two methods to Level.swift:

func shuffle() -> Set<Cookie> {
  return createInitialCookies()
}
 
func createInitialCookies() -> Set<Cookie> {
  var set = Set<Cookie>()
 
  // 1
  for row in 0..NumRows {
    for column in 0..NumColumns {
 
      // 2
      var cookieType = CookieType.random()
 
      // 3
      let cookie = Cookie(column: column, row: row, cookieType: cookieType)
      cookies[column, row] = cookie
 
      // 4
      set.addElement(cookie)
    }
  }
  return set
}

(Don’t worry about the error messages on the lines with Set<Cookie>. You’ll fix that in a moment.)

The shuffle method fills up the level with random cookies. Right now it just calls createInitialCookies(), where the real work happens. Here’s what it does, step by step:

  1. The method loops through the rows and columns of the 2-D array. This is something you’ll see a lot in this tutorial. Remember that column 0, row 0 is in the bottom-left corner of the 2-D grid.
  2. Then the method picks a random cookie type. This uses the random() function you added to the CookieType enum earlier.
  3. Next, the method creates a new Cookie object and adds it to the 2-D array.
  4. Finally, the method adds the new Cookie object to a Set. shuffle returns this set of cookie objects to its caller.

One of the main difficulties when designing your code is deciding how the different objects will communicate with each other. In this game, you often accomplish this by passing around a collection of objects, usually a Set or Array.

In this case, after you create a new Level object and call shuffle to fill it up with cookies, the Level replies, “Here is a set with all the new Cookie objects I just added.” You can take that set and, for example, create new sprites for all the cookie objects it contains. In fact, that’s exactly what you’ll do in the next section.

First, however, there is that error with Set<Cookie>. Because Set stores its elements as dictionary keys, the objects that you put into the set must conform to the Hashable protocol. That’s a requirement for Swift’s dictionary keys. Right now, Cookie does not conform to Hashable, which is why Xcode gets upset.

Switch to Cookie.swift and change the class declaration to include Hashable:

class Cookie: Printable, Hashable {

Add the following property inside the class:

var hashValue: Int {
  return row*10 + column
}

The Hashable protocol requires that you add a hashValue property to the object. This should return an Int value that is as unique as possible for your object. Its position in the 2-D grid is enough to identify each cookie, and you’ll use that to generate the hash value.

Also add the following function outside the Cookie class:

func ==(lhs: Cookie, rhs: Cookie) -> Bool {
  return lhs.column == rhs.column && lhs.row == rhs.row
}

Whenever you add the Hashable protocol to an object, you also need to supply the == comparison operator for comparing two objects of the same type.

Now the errors in Level.swift should be gone. Press Command+B to build the app and make sure you’re no longer getting any compilation errors.

The Scene and the View Controller

In many Sprite Kit games, the “scene” is the main object for the game. In Cookie Crunch, however, you’ll make the view controller play that role.

Why? The game will include UIKit elements, such as labels, and it makes sense for the view controller to manage them. You’ll still have a scene object—GameScene from the template—but this will only be responsible for drawing the sprites; it won’t handle any of the game logic.

MVC

Cookie Crunch will use an architecture that is very much like the model-view-controller or MVC pattern that you may know from non-game apps:

  • The data model will consist of Level, Cookie and a few other classes. The models will contain the data, such as the 2-D grid of cookie objects, and handle most of the gameplay logic.
  • The views will be GameScene and the SKSpriteNodes on the one hand, and UIViews on the other. The views will be responsible for showing things on the screen and for handling touches on those things. The scene in particular will draw the cookie sprites and detect swipes.
  • The view controller will play the same role here as in a typical MVC app: it will sit between the models and the views and coordinate the whole shebang.

All of these objects will communicate with each other, mostly by passing arrays and sets of objects to be modified. This separation will give each object only one job that it can do, totally independent of the others, which will keep the code clean and easy to manage.

Note: Putting the game data and rules in separate model objects is especially useful for unit testing. This tutorial doesn’t cover unit testing but, for a game such as this, it’s a good idea to have a comprehensive set of tests for the game rules.

If game logic and sprites are all mixed up, then it’s hard to write such tests, but in this case you can test Level separate from the other components. This kind of testing lets you add new game rules with confidence you didn’t break any of the existing ones.

Open GameScene.swift and add the following properties to the class:

var level: Level!
 
let TileWidth: CGFloat = 32.0
let TileHeight: CGFloat = 36.0
 
let gameLayer = SKNode()
let cookiesLayer = SKNode()

The scene has a public property to hold a reference to the current level. This variable is marked as Level! because it will not initially have a value.

Each square of the 2-D grid measures 32 by 36 points, so you put those values into the TileWidth and TileHeight constants. These constants will make it easier to calculate the position of a cookie sprite.

To keep the Sprite Kit node hierarchy neatly organized, GameScene uses several layers. The base layer is called gameLayer. This is the container for all the other layers and it’s centered on the screen. You’ll add the cookie sprites to cookiesLayer, which is a child of gameLayer.

Add the following lines to init to add the new layers. Put this after the code that creates the background node:

addChild(gameLayer)
 
let layerPosition = CGPoint(
    x: -TileWidth * CGFloat(NumColumns) / 2,
    y: -TileHeight * CGFloat(NumRows) / 2)
 
cookiesLayer.position = layerPosition
gameLayer.addChild(cookiesLayer)

This adds two empty SKNodes to the screen to act as layers. You can think of these as transparent planes you can add other nodes in.

Remember that earlier you set the anchorPoint of the scene to (0, 0), and the position of the scene also defaults to (0, 0). This means (0, 0) is in the center of the screen. Therefore, when you add these layers as children of the scene, the point (0, 0) in layer coordinates will also be in the center of the screen.

However, because column 0, row 0 is in the bottom-left corner of the 2-D grid, you want the positions of the sprites to be relative to the cookiesLayer’s bottom-left corner, as well. That’s why you move the layer down and to the left by half the height and width of the grid.

Note: Because NumColumns and NumRows are of type Int but CGPoint‘s x and y fields are of type CGFloat, you have to convert these values by writing CGFloat(NumColumns). You’ll see this sort of thing a lot in Swift code.

Adding the sprites to the scene happens in addSpritesForCookies(). Add it below:

func addSpritesForCookies(cookies: Set<Cookie>) {
  for cookie in cookies {
    let sprite = SKSpriteNode(imageNamed: cookie.cookieType.spriteName)
    sprite.position = pointForColumn(cookie.column, row:cookie.row)
    cookiesLayer.addChild(sprite)
    cookie.sprite = sprite
  }
}
 
func pointForColumn(column: Int, row: Int) -> CGPoint {
  return CGPoint(
          x: CGFloat(column)*TileWidth + TileWidth/2,
          y: CGFloat(row)*TileHeight + TileHeight/2)
}

addSpritesForCookies() iterates through the set of cookies and adds a corresponding SKSpriteNode instance to the cookie layer. This uses a helper method, pointForColumn(column:, row:), that converts a column and row number into a CGPoint that is relative to the cookiesLayer. This point represents the center of the cookie’s SKSpriteNode.

Hop over to GameViewController.swift and add a new property to the class:

var level: Level!

Next, add these two new methods:

func beginGame() {
  shuffle()
}
 
func shuffle() {
  let newCookies = level.shuffle()
  scene.addSpritesForCookies(newCookies)
}

beginGame() kicks off the game by calling shuffle(). This is where you call Level’s shuffle() method, which returns the Set containing new Cookie objects. Remember that these cookie objects are just model data; they don’t have any sprites yet. To show them on the screen, you tell GameScene to add sprites for those cookies.

Make sure you call beginGame() at the end of viewDidLoad to set things in motion:

override func viewDidLoad() {
   ...
   beginGame()
}

The only missing piece is creating the actual Level instance. That also happens in viewDidLoad(). Add the following lines just before the code that presents the scene:

level = Level()
scene.level = level

After creating the new Level instance, you set the level property on the scene to tie together the model and the view.

Note: The reason you declared the var level property as Level!, with an exclamation point, is that all properties must have a value by the time the class is initialized. But you can’t give level a value in init() yet; that doesn’t happen until viewDidLoad. With the ! you tell Swift that this variable won’t have a value until later (but once it’s set, it can never become nil again).

Build and run, and you should finally see some cookies:

First cookies

Loading Levels from JSON Files

Not all the levels in Candy Crush Saga have grids that are a simple square shape. You will now add support for loading level designs from JSON files. The five designs you’re going to load still use the same 9×9 grid, but they leave some of the squares blank.

Drag the Levels folder from the tutorial’s Resources folder into your Xcode project. As always, make sure Destination: Copy items if needed is checked. This folder contains five JSON files:

Levels in project navigator

Click on Level_1.json to look inside. You’ll see that the contents are structured as a dictionary containing three elements: tiles, targetScore and moves.

The tiles array contains nine other arrays, one for each row of the level. If a tile has a value of 1, it can contain a cookie; a 0 means the tile is empty.

Level_1 json

You’ll load this data in Level, but first you need to add a new class, Tile, to represent a single tile in the 2-D level grid. Note that a tile is different than a cookie — think of tiles as “slots”, and of cookies as the things inside the slots. I’ll discuss more about this in a bit.

Add a new Swift File to the project. Name it Tile.swift. Replace the contents of this file with:

class Tile {
}

You can leave this new class empty right now. Later on, I’ll give you some hints for how to use this class to add additional features to the game, such as “jelly” tiles.

Open Level.swift and add a new property and method:

let tiles = Array2D<Tile>(columns: NumColumns, rows: NumRows)  // private
 
func tileAtColumn(column: Int, row: Int) -> Tile? {
  assert(column >= 0 && column < NumColumns)
  assert(row >= 0 && row < NumRows)
  return tiles[column, row]
}

The tiles variable describes the structure of the level. This is very similar to the cookies array, except now you make it an Array2D of Tile objects.

Whereas the cookies array keeps track of the Cookie objects in the level, tiles simply describes which parts of the level grid are empty and which can contain a cookie:

JSON and tiles

Wherever tiles[a, b] is nil, the grid is empty and cannot contain a cookie.

Now that the instance variables for level data are in place, you can start adding the code to fill in the data. The top-level item in the JSON file is a dictionary, so it makes sense to add the code for loading the JSON file to Swift’s Dictionary.

Go to File\New\File…, choose the iOS\Source\Swift File template and click Next. Name the file Extensions.swift and click Create.

Add the following code in Extensions.swift:

extension Dictionary {
  static func loadJSONFromBundle(filename: String) -> Dictionary<String, AnyObject>? {
    let path = NSBundle.mainBundle().pathForResource(filename, ofType: "json")
    if !path {
      println("Could not find level file: \(filename)")
      return nil
    }
 
    var error: NSError?
    let data: NSData? = NSData(contentsOfFile: path, options: NSDataReadingOptions(), 
                               error: &error)
    if !data {
      println("Could not load level file: \(filename), error: \(error!)")
      return nil
    }
 
    let dictionary: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, 
                                     options: NSJSONReadingOptions(), error: &error)
    if !dictionary {
      println("Level file '\(filename)' is not valid JSON: \(error!)")
      return nil
    }
 
    return dictionary as? Dictionary<String, AnyObject>
  }
}

Using Swift’s extension mechanism you can add new methods to existing types. Here you have added loadJSONFromBundle() to load a JSON file from the app bundle, into a new dictionary of type Dictionary<String, AnyObject>. This means the dictionary’s keys are always strings but the associated values can be any type of object.

The method simply loads the specified file into an NSData object and then converts that to a Dictionary using the NSJSONSerialization API. This is mostly boilerplate code that you’ll find in any app that deals with JSON files.

Note: To learn more about JSON and parsing it in iOS, check out our Working with JSON Tutorial.

Next, add the new init(filename:) initializer to Level.swift:

init(filename: String) {
  // 1 
  if let dictionary = Dictionary<String, AnyObject>.loadJSONFromBundle(filename) {
    // 2
    if let tilesArray: AnyObject = dictionary["tiles"] {
      // 3
      for (row, rowArray) in enumerate(tilesArray as Int[][]) {
        // 4
        let tileRow = NumRows - row - 1
        // 5
        for (column, value) in enumerate(rowArray) {
          if value == 1 {
            tiles[column, tileRow] = Tile()
          }
        }
      }
    }
  }
}

Here’s what this initializer does, step-by-step:

  1. Load the named file into a Dictionary using the loadJSONFromBundle() helper function that you just added. Note that this function may return nil — it returns an optional — and you must use optional binding to handle this situation.
  2. The dictionary has an array named “tiles”. This array contains one element for each row of the level. Each of those row elements is itself an array containing the columns for that row. The type of tilesArray is therefore array-of-array-of-Int, or Int[][].
  3. Step through the rows using built-in enumerate() function, which is useful because it also returns the current row number.
  4. In Sprite Kit (0, 0) is at the bottom of the screen, so you have to reverse the order of the rows here. The first row you read from the JSON corresponds to the last row of the 2-D grid.
  5. Step through the columns in the current row. Every time it finds a 1, it creates a Tile object and places it into the tiles array.

At this point, the code will compile without errors but you still need to put this new tiles array to good use. Inside createInitialCookies(), add an if-clause inside the two for-loops, around the code that creates the Cookie object:

// This line is new
if tiles[column, row] != nil {
 
  var cookieType = ...
  ...
  set.addElement(cookie)
}

Now the app will only create a Cookie object if there is a tile at that spot.

One last thing remains: In GameViewController.swift’s viewDidLoad(), replace the line that creates the level object with:

level = Level(filename: "Level_1")

Build and run, and now you should have a non-square level shape:

Non-square level

Making the Tiles Visible

To make the cookie sprites stand out from the background a bit more, you can draw a slightly darker “tile” sprite behind each cookie. The texture atlas already contains an image for this (Tile.png). These new tile sprites will live on their own layer, the tilesLayer.

To do this, first add a new private property to GameScene.swift:

let tilesLayer = SKNode()

Then add this code to init(size:), right above where you add the cookiesLayer:

tilesLayer.position = layerPosition
gameLayer.addChild(tilesLayer)

It needs to be done first so the tiles appear behind the cookies (Sprite Kit nodes with the same zPosition are drawn in order of how they were added).

Add the following method to GameScene.swift, as well:

func addTiles() {
  for row in 0..NumRows {
    for column in 0..NumColumns {
      if let tile = level.tileAtColumn(column, row: row) {
        let tileNode = SKSpriteNode(imageNamed: "Tile")
        tileNode.position = pointForColumn(column, row: row)
        tilesLayer.addChild(tileNode)
      }
    }
  }
}

This loops through all the rows and columns. If there is a tile at that grid square, then it creates a new tile sprite and adds it to the tiles layer.

Next, open GameViewController.swift. Add the following line to viewDidLoad(), immediately after you set scene.level:

scene.addTiles()

Build and run, and you can clearly see where the tiles are:

Tiles layer

You can switch to another level design by specifying a different file name in viewDidLoad(). Simply change the filename: parameter to “Level_2″, “Level_3″ or “Level_4″ and build and run. Does Level 3 remind you of anything? :]

Feel free to make your own designs, too! Just remember that the “tiles” array should contain nine arrays (one for each row), with nine numbers each (one for each column).

Swiping to Swap Cookies

In Cookie Crunch Adventure, you want the player to be able to swap two cookies by swiping left, right, up or down.

Detecting swipes is a job for GameScene. If the player touches a cookie on the screen, then this might be the start of a valid swipe motion. Which cookie to swap with the touched cookie depends on the direction of the swipe.

To recognize the swipe motion, you’ll use the touchesBegan, touchesMoved and touchesEnded methods from GameScene. Even though iOS has very handy pan and swipe gesture recognizers, these don’t provide the level of accuracy and control that this game needs.

Go to GameScene.swift and add two private properties to the class:

var swipeFromColumn: Int?
var swipeFromRow: Int?

These properties record the column and row numbers of the cookie that the player first touched when she started her swipe movement.

Initialize these two properties at the bottom of init(size:):

swipeFromColumn = nil
swipeFromRow = nil

The value nil means that these properties have invalid values. In other words, they don’t yet point at any of the cookies. This is why they are declared as optionals — Int? instead of just Int — because they need to be nil when the player is not swiping.

Now add a new method touchesBegan():

override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
  // 1
  let touch = touches.anyObject() as UITouch
  let location = touch.locationInNode(cookiesLayer)
  // 2
  let (success, column, row) = convertPoint(location)
  if success {
    // 3
    if let cookie = level.cookieAtColumn(column, row: row) {
      // 4
      swipeFromColumn = column
      swipeFromRow = row
    }
  }
}

Note: This method needs to be marked override because the base class SKScene already contains a version of touchesBegan. This is how you tell Swift that you want it to use your own version.

The game will call touchesBegan() whenever the user puts her finger on the screen. Here’s what the method does, step by step:

  1. It converts the touch location to a point relative to the cookiesLayer.
  2. Then, it finds out if the touch is inside a square on the level grid by calling a method you’ll write in a moment. If so, then this might be the start of a swipe motion. At this point, you don’t know yet whether that square contains a cookie, but at least the player put her finger somewhere inside the 9×9 grid.
  3. Next, the method verifies that the touch is on a cookie rather than on an empty square.
  4. Finally, it records the column and row where the swipe started so you can compare them later to find the direction of the swipe.

The convertPoint() method is new. It’s the opposite of pointForColumn(column:, row:), so you may want to add this method right below pointForColumn() so the two methods are nearby.

func convertPoint(point: CGPoint) -> (success: Bool, column: Int, row: Int) {
  if point.x >= 0 && point.x < CGFloat(NumColumns)*TileWidth &&
     point.y >= 0 && point.y < CGFloat(NumRows)*TileHeight {
    return (true, Int(point.x / TileWidth), Int(point.y / TileHeight))
  } else {
    return (false, 0, 0)  // invalid location
  }
}

This method takes a CGPoint that is relative to the cookiesLayer and converts it into column and row numbers. The return value of this method is a tuple with three values: 1) the boolean that indicates success or failure; 2) the column number; and 3) the row number. If the point falls outside the grid, this method returns false for success.

So far, you have detected the start of a possible swipe motion. To perform a valid swipe, the player also has to move her finger out of the current square. It doesn’t really matter where the finger ends up—you’re only interested in the general direction of the swipe, not the exact destination.

The logic for detecting the swipe direction goes into touchesMoved(), so add this method next:

override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
  // 1
  if swipeFromColumn == nil { return }
 
  // 2
  let touch = touches.anyObject() as UITouch
  let location = touch.locationInNode(cookiesLayer)
 
  let (success, column, row) = convertPoint(location)
  if success {
 
    // 3
    var horzDelta = 0, vertDelta = 0
    if column < swipeFromColumn! {          // swipe left
      horzDelta = -1
    } else if column > swipeFromColumn! {   // swipe right
      horzDelta = 1
    } else if row < swipeFromRow! {         // swipe down
      vertDelta = -1
    } else if row > swipeFromRow! {         // swipe up
      vertDelta = 1
    }
 
    // 4
    if horzDelta != 0 || vertDelta != 0 {
      trySwapHorizontal(horzDelta, vertical: vertDelta)
 
      // 5
      swipeFromColumn = nil
    }
  }
}

Here is what this does step by step:

  1. If swipeFromColumn is nil, then either the swipe began outside the valid area or the game has already swapped the cookies and you need to ignore the rest of the motion. You could keep track of this in a separate boolean but using swipeFromColumn is just as easy — that’s why you made it an optional.
  2. This is similar to what touchesBegan() does to calculate the row and column numbers currently under the player’s finger.
  3. Here the method figures out the direction of the player’s swipe by simply comparing the new column and row numbers to the previous ones. Note that you’re not allowing diagonal swipes (since you’re using else if statements, only one of horzDelta or vertDelta will be set).
  4. The method only performs the swap if the player swiped out of the old square.
  5. By setting swipeFromColumn back to nil, the game will ignore the rest of this swipe motion.

Note: To read the actual values from swipeFromColumn and swipeFromRow, you have to use the exclamation point. These are optional variables, and using the ! will “unwrap” the optional. Normally you’d use optional binding to read the value of an optional but here you’re guaranteed that swipeFromRow is not nil (you checked for that at the top of the method), so using ! is perfectly safe.

The hard work of cookie-swapping goes into a new method:

func trySwapHorizontal(horzDelta: Int, vertical vertDelta: Int) {
  // 1
  let toColumn = swipeFromColumn! + horzDelta
  let toRow = swipeFromRow! + vertDelta
  // 2
  if toColumn < 0 || toColumn >= NumColumns { return }
  if toRow < 0 || toRow >= NumRows { return }
  // 3
  if let toCookie = level.cookieAtColumn(toColumn, row: toRow) {
    if let fromCookie = level.cookieAtColumn(swipeFromColumn!, row: swipeFromRow!) {
      // 4
      println("*** swapping \(fromCookie) with \(toCookie)")
    }
  }
}

This is called “try swap” for a reason. At this point, you only know that the player swiped up, down, left or right, but you don’t yet know if there are two cookies to swap in that direction.

  1. You calculate the column and row numbers of the cookie to swap with.
  2. It is possible that the toColumn or toRow is outside the 9×9 grid. This can occur when the user swipes from a cookie near the edge of the grid. The game should ignore such swipes.
  3. The final check is to make sure that there is actually a cookie at the new position. You can’t swap if there’s no second cookie. This happens when the user swipes into a gap where there is no tile.
  4. When you get here, it means everything is OK and this is a valid swap! For now, you log both cookies to the Xcode debug pane.

For completeness’s sake, you should also implement touchesEnded(), which is called when the user lifts her finger from the screen, and touchesCancelled(), which happens when iOS decides that it must interrupt the touch (for example, because of an incoming phone call).

Add the following:

override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
  swipeFromColumn = nil
  swipeFromRow = nil
}
 
override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
  touchesEnded(touches, withEvent: event)
}

If the gesture ends, regardless of whether it was a valid swipe, you reset the starting column and row numbers to the special value nil.

Great! Build and run, and try out different swaps:

Valid swap

Of course, you won’t see anything happen in the game yet, but at least the debug pane logs your attempts to make a valid swap.

Animating the Swaps

To describe the swapping of two cookies, you will create a new class, Swap. This is another model class whose only purpose it is to say, “The player wants to swap cookie A with cookie B.”

Create a new Swift File named Swap.swift. Replace the contents of Swap.swift with the following:

class Swap: Printable {
  var cookieA: Cookie
  var cookieB: Cookie
 
  init(cookieA: Cookie, cookieB: Cookie) {
    self.cookieA = cookieA
    self.cookieB = cookieB
  }
 
  var description: String {
    return "swap \(cookieA) with \(cookieB)"
  }
}

Now that you have an object that can describe an attempted swap, the question becomes: Who will handle the logic of actually performing the swap? The swipe detection logic happens in GameScene, but all the real game logic so far is in GameViewController.

That means GameScene must have a way to communicate back to GameViewController that the player performed a valid swipe and that a swap must be attempted. One way to communicate is through a delegate protocol, but since this is the only message that GameScene must send back to GameViewController, you’ll use a closure instead.

Add the following property to the top of GameScene.swift:

var swipeHandler: ((Swap) -> ())?

That looks scary… The type of this variable is ((Swap) -> ())?. Because of the -> you can tell this is a closure or function. This closure or function takes a Swap object as its parameter and does not return anything. The question mark indicates that swipeHandler is allowed to be nil (it is an optional).

It’s the scene’s job to handle touches. If it recognizes that the user made a swipe, it will call the closure that’s stored in the swipe handler. This is how it communicates back to the GameViewController that a swap needs to take place.

Still in GameScene.swift, add the following code to the bottom of trySwapHorizontal(vertical:), replacing the println() statement:

if let handler = swipeHandler {
  let swap = Swap(cookieA: fromCookie, cookieB: toCookie)
  handler(swap)
}

This creates a new Swap object, fills in the two cookies to be swapped and then calls the swipe handler to take care of the rest. Because swipeHandler can be nil, you use optional binding to get a valid reference first.

GameViewController will decide whether the swap is valid; if it is, you’ll need to animate the two cookies. Add the following method to do this in GameScene.swift:

func animateSwap(swap: Swap, completion: () -> ()) {
  let spriteA = swap.cookieA.sprite!
  let spriteB = swap.cookieB.sprite!
 
  spriteA.zPosition = 100
  spriteB.zPosition = 90
 
  let Duration: NSTimeInterval = 0.3
 
  let moveA = SKAction.moveTo(spriteB.position, duration: Duration)
  moveA.timingMode = .EaseOut
  spriteA.runAction(moveA, completion: completion)
 
  let moveB = SKAction.moveTo(spriteA.position, duration: Duration)
  moveB.timingMode = .EaseOut
  spriteB.runAction(moveB)
}

This is basic SKAction animation code: You move cookie A to the position of cookie B and vice versa.

The cookie that was the origin of the swipe is in cookieA and the animation looks best if that one appears on top, so this method adjusts the relative zPosition of the two cookie sprites to make that happen.

After the animation completes, the action on cookieA calls a completion block so the caller can continue doing whatever it needs to do. That’s a common pattern for this game: The game waits until an animation is complete and then it resumes.

() -> () is simply shorthand for a closure that returns void and takes no parameters.

Now that you’ve handled the view, there’s still the model to deal with before getting to the controller! Open Level.swift and add the following method:

func performSwap(swap: Swap) {
  let columnA = swap.cookieA.column
  let rowA = swap.cookieA.row
  let columnB = swap.cookieB.column
  let rowB = swap.cookieB.row
 
  cookies[columnA, rowA] = swap.cookieB
  swap.cookieB.column = columnA
  swap.cookieB.row = rowA
 
  cookies[columnB, rowB] = swap.cookieA
  swap.cookieA.column = columnB
  swap.cookieA.row = rowB
}

This first makes temporary copies of the row and column numbers from the Cookie objects because they get overwritten. To make the swap, it updates the cookies array, as well as the column and row properties of the Cookie objects, which shouldn’t go out of sync. That’s it for the data model.

Go to GameViewController.swift and add the following method:

func handleSwipe(swap: Swap) {
  view.userInteractionEnabled = false
 
  level.performSwap(swap)
 
  scene.animateSwap(swap) {
    self.view.userInteractionEnabled = true
  }
}

You first tell the level to perform the swap, which updates the data model—and then tell the scene to animate the swap, which updates the view. Over the course of this tutorial, you’ll add the rest of the gameplay logic to this function.

While the animation is happening, you don’t want the player to be able to touch anything else, so you temporarily turn off userInteractionEnabled on the view. You turn it back on in the completion block that is passed to animateSwap().

Note: The above uses so-called trailing closure syntax, where the closure is written behind the function call. An alternative way to write it is as follows:

scene.animateSwap(swap, completion: {
  self.view.userInteractionEnabled = true
})

Also add the following line to viewDidLoad(), just before the line that presents the scene:

scene.swipeHandler = handleSwipe

This assigns the handleSwipe() function to GameScene’s swipeHandler property. Now whenever GameScene calls swipeHandler(swap), it actually calls a function in GameViewController. Freaky! This works because in Swift you can use functions and closures interchangeably.

Build and run the app. You can now swap the cookies! Also, try to make a swap across a gap—it won’t work!

Swap cookies

Highlighting the Cookies

In Candy Crush Saga, the candy you swipe lights up for a brief moment. You can achieve this effect in Cookie Crunch Adventure by placing a highlight image on top of the sprite.

The texture atlas has highlighted versions of the cookie sprites that are brighter and more saturated. The CookieType enum already has a function to return the name of this image: highlightedSpriteName.

You will improve GameScene to add this highlighted cookie on top of the existing cookie sprite. Adding it as a new sprite, as opposed to replacing the existing sprite’s texture, makes it easier to crossfade back to the original image.

In GameScene.swift, add a new private property to the class:

var selectionSprite = SKSpriteNode()

Add the following method:

func showSelectionIndicatorForCookie(cookie: Cookie) {
  if selectionSprite.parent != nil {
    selectionSprite.removeFromParent()
  }
 
  if let sprite = cookie.sprite {
    let texture = SKTexture(imageNamed: cookie.cookieType.highlightedSpriteName)
    selectionSprite.size = texture.size()
    selectionSprite.runAction(SKAction.setTexture(texture))
 
    sprite.addChild(selectionSprite)
    selectionSprite.alpha = 1.0
  }
}

This gets the name of the highlighted sprite image from the Cookie object and puts the corresponding texture on the selection sprite. Simply setting the texture on the sprite doesn’t give it the correct size but using an SKAction does.

You also make the selection sprite visible by setting its alpha to 1. You add the selection sprite as a child of the cookie sprite so that it moves along with the cookie sprite in the swap animation.

Add the opposite method, hideSelectionIndicator():

func hideSelectionIndicator() {
  selectionSprite.runAction(SKAction.sequence([
    SKAction.fadeOutWithDuration(0.3),
    SKAction.removeFromParent()]))
}

This method removes the selection sprite by fading it out.

It remains for you to call these methods. First, in touchesBegan(), in the if let cookie = ... section, add:

showSelectionIndicatorForCookie(cookie)

And in touchesMoved(), after the call to trySwapHorizontal(), add:

hideSelectionIndicator()

There is one last place to call hideSelectionIndicator(). If the user just taps on the screen rather than swipes, you want to fade out the highlighted sprite, too. Add these lines to the top of touchesEnded():

if selectionSprite.parent != nil && swipeFromColumn != nil {
  hideSelectionIndicator()
}

Build and run, and highlight some cookies!

Highlighted cookies

A Smarter Way to Fill the Array

The purpose of this game is to make chains of three or more of the same cookie. But right now, when you run the game there may already be such chains on the screen. That’s no good—you only want there to be matches after the user swaps two cookies or after new cookies fall down the screen.

Here’s your rule: Whenever it’s the user’s turn to make a move, whether at the start of the game or at the end of a turn, no matches may be on the board already. To guarantee this is the case, you have to make the method that fills up the cookies array a bit smarter.

Go to Level.swift and find createInitialCookies(). Replace the single line that calculates the random cookieType with the following:

var cookieType: CookieType
do {
  cookieType = CookieType.random()
}
while (column >= 2 &&
        cookies[column - 1, row]?.cookieType == cookieType &&
        cookies[column - 2, row]?.cookieType == cookieType)
   || (row >= 2 &&
        cookies[column, row - 1]?.cookieType == cookieType &&
        cookies[column, row - 2]?.cookieType == cookieType)

Yowza! What is all this? This piece of logic picks the cookie type at random and makes sure that it never creates a chain of three or more.

In pseudo-code, it looks like this:

do {
  generate a new random cookie type
} 
while there are already two cookies of this type to the left
   or there are already two cookies of this type below

If the new random number causes a chain of three—because there are already two cookies of this type to the left or below—then the method tries again. The loop repeats until it finds a random number that does not create a chain of three or more. It only has to look to the left or below because there are no cookies yet on the right or above.

Try it out! Run the app and verify that there are no longer any chains in the initial state of the game.

No chains in initial state

Not All Swaps Are Allowed

You only want the let the player swap two cookies if it would result in either (or both) of these cookies making a chain of three or more.

Allowed swap

You need to add some logic to the game to detect whether a swap results in a chain. There are two ways you could do this. The most obvious way is to check at the moment the user tries the swap.

Or—and this is what you’ll do in this tutorial—you could build a list of all possible moves after the level is shuffled. Then you only have to check if the attempted swap is in that list.

Note: Building a list also makes it easy to show a hint to the player. You’re not going to do that in this tutorial, but in Candy Crush Saga, if you don’t play for a few seconds, the game lights up a possible swap. You can implement this for yourself by picking a random item from this list of possible moves.

In Level.swift, add a new property:

var possibleSwaps = Set<Swap>()  // private

Again, you’re using a Set here instead of an Array because the order of the elements in this collection isn’t important. This Set will contain Swap objects. If the player tries to swap two cookies that are not in the set, then the game won’t accept the swap as a valid move.

Xcode warns that Swap cannot be used in a Set, and that’s because Swap does not implement the Hashable protocol yet.

Open up Swap.swift and make the following changes. First, add Hashable to the class declaration:

class Swap: Printable, Hashable {

Then add the hashValue property inside the class:

var hashValue: Int {
  return cookieA.hashValue ^ cookieB.hashValue
}

This simply combines the hash values of the two cookies with the exclusive-or operator. That’s a common trick to make hash values.

And finally, add the == function outside of the class:

func ==(lhs: Swap, rhs: Swap) -> Bool {
  return (lhs.cookieA == rhs.cookieA && lhs.cookieB == rhs.cookieB) ||
         (lhs.cookieB == rhs.cookieA && lhs.cookieA == rhs.cookieB)
}

Now you can use Swap objects in a Set and the compiler error should be history.

At the start of each turn, you need to detect which cookies the player can swap. You’re going to make this happen in shuffle(). Go back to Level.swift and change the code for that method to:

func shuffle() -> Set<Cookie> {
  var set: Set<Cookie>
  do {
    set = createInitialCookies()
    detectPossibleSwaps()
    println("possible swaps: \(possibleSwaps)")
  }
  while possibleSwaps.count == 0
 
  return set
}

As before, this calls createInitialCookies() to fill up the level with random cookie objects. But then it calls a new method that you will add shortly, detectPossibleSwaps(), to fill up the new possibleSwaps set.

In the very rare case that you end up with a distribution of cookies that allows for no swaps at all, this loop repeats to try again. You can test this with a very small level, such as one with only 3×3 tiles. I’ve included such a level for you in the project called Level_4.json.

detectPossibleSwaps() will use a helper method to see if a cookie is part of a chain. Add this method now:

func hasChainAtColumn(column: Int, row: Int) -> Bool {
  let cookieType = cookies[column, row]!.cookieType
 
  var horzLength = 1
  for var i = column - 1; i >= 0 && cookies[i, row]?.cookieType == cookieType; 
        --i, ++horzLength { }
  for var i = column + 1; i < NumColumns && cookies[i, row]?.cookieType == cookieType;
        ++i, ++horzLength { }
  if horzLength >= 3 { return true }
 
  var vertLength = 1
  for var i = row - 1; i >= 0 && cookies[column, i]?.cookieType == cookieType; 
        --i, ++vertLength { }
  for var i = row + 1; i < NumRows && cookies[column, i]?.cookieType == cookieType;
        ++i, ++vertLength { }
  return vertLength >= 3
}

A chain is three or more consecutive cookies of the same type in a row or column. This method may look a little strange but that’s because it stuffs a lot of the logic inside the for-statements.

Look left right up down

Given a cookie in a particular square on the grid, this method first looks to the left. As long as it finds a cookie of the same type, it increments horzLength and keeps going left. This is expressed succinctly in a single line of code:

for var i = column - 1; i >= 0 && cookies[i, row]?.cookieType == cookieType; --i, ++horzLength { }

This for loop has an empty body. That means all the logic happens inside its parameters.

for var i = column - 1;  // start on the left of the current cookie
 
  i >= 0 &&                                   // keep going while not left-most column reached
  cookies[i, row]?.cookieType == cookieType;  // and still the same cookie type
 
  --i,                   // go to the next column on the left
  ++horzLength           // and increment the length
  { }                    // do nothing inside the loop

You can also write this out using a while statement but the for loop allows you to fit everything on a single line. :] There are also loops for looking to the right, above and below.

Note: It’s possible that cookies[column, row] will return nil because of a gap in the level design, meaning there is no cookie at that location. That’s no problem because of Swift’s optional chaining. Because of the ? operator, the loop will terminate whenever such a gap is encountered.

Now that you have this method, you can implement detectPossibleSwaps(). Here’s how it will work at a high level:

  1. It will step through the rows and columns of the 2-D grid and simply swap each cookie with the one next to it, one at a time.
  2. If swapping these two cookies creates a chain, it will add a new Swap object to the list of possibleSwaps.
  3. Then, it will swap these cookies back to restore the original state and continue with the next cookie until it has swapped them all.
  4. It will go through the above steps twice: once to check all horizontal swaps and once to check all vertical swaps.

It’s a big one, so you’ll take it in parts!

First, add the outline of the method:

func detectPossibleSwaps() {
  var set = Set<Swap>()
 
  for row in 0..NumRows {
    for column in 0..NumColumns {
      if let cookie = cookies[column, row] {
 
        // TODO: detection logic goes here
      }
    }
  }
 
  possibleSwaps = set
}

This is pretty simple: The method loops through the rows and columns, and for each spot, if there is a cookie rather than an empty square, it performs the detection logic. Finally, the method places the results into the possibleSwaps property.

The detection will consist of two separate parts that do the same thing but in different directions. First you want to swap the cookie with the one on the right, and then you want to swap the cookie with the one above it. Remember, row 0 is at the bottom so you’ll work your way up.

Add the following code where it says “TODO: detection logic goes here”:

// Is it possible to swap this cookie with the one on the right?
if column < NumColumns - 1 {
  // Have a cookie in this spot? If there is no tile, there is no cookie.
  if let other = cookies[column + 1, row] {
    // Swap them
    cookies[column, row] = other
    cookies[column + 1, row] = cookie
 
    // Is either cookie now part of a chain?
    if hasChainAtColumn(column + 1, row: row) ||
       hasChainAtColumn(column, row: row) {
      set.addElement(Swap(cookieA: cookie, cookieB: other))
    }
 
    // Swap them back
    cookies[column, row] = cookie
    cookies[column + 1, row] = other
  }
}

This attempts to swap the current cookie with the cookie on the right, if there is one. If this creates a chain of three or more, the code adds a new Swap object to the set.

Now add the following code directly below the code above:

if row < NumRows - 1 {
  if let other = cookies[column, row + 1] {
    cookies[column, row] = other
    cookies[column, row + 1] = cookie
 
    // Is either cookie now part of a chain?
    if hasChainAtColumn(column, row: row + 1) ||
       hasChainAtColumn(column, row: row) {
      set.addElement(Swap(cookieA: cookie, cookieB: other))
    }
 
    // Swap them back
    cookies[column, row] = cookie
    cookies[column, row + 1] = other
  }
}

This does exactly the same thing, but for the cookie above instead of on the right.

That should do it. In summary, this algorithm performs a swap for each pair of cookies, checks whether it results in a chain and then undoes the swap, recording every chain it finds.

Now run the app and you should see something like this in the Xcode debug pane:

possible swaps: [
swap type:SugarCookie square:(6,5) with type:Cupcake square:(7,5): true, 
swap type:Croissant square:(3,3) with type:Macaroon square:(4,3): true, 
swap type:Danish square:(6,0) with type:Macaroon square:(6,1): true, 
swap type:Cupcake square:(6,4) with type:SugarCookie square:(6,5): true, 
swap type:Croissant square:(4,2) with type:Macaroon square:(4,3): true, 
. . .

To Swap or Not to Swap…

Let’s put this list of possible moves to good use. Add the following method to Level.swift:

func isPossibleSwap(swap: Swap) -> Bool {
  return possibleSwaps.containsElement(swap)
}

This looks to see if the set of possible swaps contains the specified Swap object. But wait a minute… when you perform a swipe, GameScene creates a new Swap object. How could isPossibleSwap() possibly find that object inside its list? It may have a Swap object that describes exactly the same move, but the actual instances in memory are different.

When you run set.containsElement(object), the set calls == on that object and all the objects it contains to determine if they match. Because you already provided an == operator for Swap, this automagically works! It doesn’t matter that the Swap objects are actually different instances; the set will find a match as long as two Swaps can be considered equal.

Finally call the method in GameViewController.swift, inside the handleSwipe() function:

func handleSwipe(swap: Swap) {
  view.userInteractionEnabled = false
 
  if level.isPossibleSwap(swap) {
    level.performSwap(swap)
    scene.animateSwap(swap) {
      self.view.userInteractionEnabled = true
    }
  } else {
     view.userInteractionEnabled = true
  }
}

Now the game will only perform the swap if it’s in the list of sanctioned swaps.

Build and run to try it out. You should only be able to make swaps if they result in a chain.

Ignore invalid swap

Note that after you perform a swap, the “valid swaps” list is now invalid. You’ll fix that in the next part of the series.

It’s also fun to animate attempted swaps that are invalid, so add the following method to GameScene.swift:

func animateInvalidSwap(swap: Swap, completion: () -> ()) {
  let spriteA = swap.cookieA.sprite!
  let spriteB = swap.cookieB.sprite!
 
  spriteA.zPosition = 100
  spriteB.zPosition = 90
 
  let Duration: NSTimeInterval = 0.2
 
  let moveA = SKAction.moveTo(spriteB.position, duration: Duration)
  moveA.timingMode = .EaseOut
 
  let moveB = SKAction.moveTo(spriteA.position, duration: Duration)
  moveB.timingMode = .EaseOut
 
  spriteA.runAction(SKAction.sequence([moveA, moveB]), completion: completion)
  spriteB.runAction(SKAction.sequence([moveB, moveA]))
}

This method is similar to animateSwap(swap:, completion:), but here it slides the cookies to their new positions and then immediately flips them back.

In GameViewController.swift, change the else-clause inside handleSwipe() to:

} else {
  scene.animateInvalidSwap(swap) {
    self.view.userInteractionEnabled = true
  }
}

Now run the app and try to make a swap that won’t result in a chain:

Invalid swap

Make Some Noise

Before wrapping up the first part of this tutorial, why don’t you go ahead and add some sound effects to the game? Open the Resources folder for this tutorial and drag the Sounds folder into Xcode.

Add new properties for these sound effects to GameScene.swift:

let swapSound = SKAction.playSoundFileNamed("Chomp.wav", waitForCompletion: false)
let invalidSwapSound = SKAction.playSoundFileNamed("Error.wav", waitForCompletion: false)
let matchSound = SKAction.playSoundFileNamed("Ka-Ching.wav", waitForCompletion: false)
let fallingCookieSound = SKAction.playSoundFileNamed("Scrape.wav", waitForCompletion: false)
let addCookieSound = SKAction.playSoundFileNamed("Drip.wav", waitForCompletion: false)

Rather than recreate an SKAction every time you need to play a sound, you’ll load all the sounds just once and keep re-using them.

Then add the following line to the bottom of animateSwap()

runAction(swapSound)

And add this line to the bottom of animateInvalidSwap():

runAction(invalidSwapSound)

That’s all you need to do to make some noise. Chomp! :]

Where to Go From Here?

Here is a sample project with all of the code from the Swift tutorial up to this point.

Your game is shaping up nicely, but there’s still a way to go before it’s finished. For now, though, give yourself a cookie for making it halfway!

In the next part, you’ll implement the remaining pieces of the game flow: removing matching chains and replacing them with new cookies. You’ll also add scoring, lots of new animations and some final polish.

While you eat your cookie, take a moment to let us hear from you in the forums!

Credits: Artwork by Vicki Wenderlich. The music is by Kevin MacLeod. The sound effects are based on samples from freesound.org.

How to Make a Game Like Candy Crush with Swift Tutorial: Part 1 is a post from: Ray Wenderlich

The post How to Make a Game Like Candy Crush with Swift Tutorial: Part 1 appeared first on Ray Wenderlich.

Readers’ App Reviews – June 2014

$
0
0
captaindrumstick

June Readers’ Apps: Colors, Brains, and Games

Summer is in full swing, and you guys still haven’t taken a break!

I’ve got more great apps to share this month from the raywenderlich.com community, which apparently never stops working :]

This month we’ve got:

  • A game to confuse your brain
  • An must-have app for students
  • A shifty calendar app
  • And plenty more!

Lets get moving – there are a ton of apps to check out from your fellow readers!

Maskify

maskify
Maskify is a photo app that makes custom photos a breeze.

Maskify lets you add text, frames, and other elements to all your photos. Choose your font, border, color, and more as you dress up your photo exactly the way you want.

Maskify makes it easy to customize your photos right on your phone. Then you can easily share that new photo to all the expected places like Facebook, Twitter, iMessage, and more.

Brain Test – Stroop Effect

stroopeffect
The Stroop Effect is a phenomenon that causes most people to have trouble reading color names printed in a different color.

Brain Test turns this into a game. You’ll see a color name on the screen in a color other than its name. Your job is then to select from two options the name of the color thats printed.

Get as many correct as you can in 60 seconds. It’s amazing how quickly you second guess yourself and slow to a crawl just because of this one small distraction.

You’ve got to try this on your friends and family!

ColourColor

ColourColor
ColourColor is a real world color hunt game.

ColourColor lets you take a picture and select a color from it. You then challenge your friends to find the same color in their own photo. The closer they are the more points they get.

There is a time limit so you’ll have to be quick and clever when trying to snap a response. Every color has the chance of a comment as well, so if you like color names, now would be the time. ;]

TotoTotems

Totototem
TotoTotems is a memory game. Ryan Poolos has terrible shortterm memory. Go!

TotoTotems has a stack of blocks each with a unique color. You get a few seconds to memorize the colors before it shuffles itself. You’ve got to spin the blocks back to the right color as fast as possible.

The better you do the more blocks they add, but don’t expect extra time. If you want to be embarassed, hand this game to the nearest 5 year old and watch them destroy your highscore.

PlaceTime

placetime
PlaceTime is a challenging game that’s all about the angles.

PlaceTime has targets on the map with objects to push. Your job is to tap at just the right time and angle to launch the object into the target.

It’s simple until you have different colors, weights, distances, and timing to deal with all at different angles.

PlaceTime has over 90 puzzles to solve each with its own theme and challenge.

Price Contrast

pricecontrast
Price Contrast is a utility for comparing price rates for items at varying quantities.

For instance, if you had one kind of juice sold by the liter and another sold by the gallon, comparing prices could be difficult. But Price Contrast lets you enter the base price of each item and then see them both converted to a common scale to see the true price difference.

Price Contrast has mass, length, and volume comparison but you can also do generic comparisons for any type of units.

Follow the Line

followTheLine
Follow the Line is a simple game all about timing.

To keep the white line on the track, you’ll have to tap at the right time to turn within the track. As you advance the speed gets faster and the turns get sharper.

Follow the Line has two game modes, a 30 level challenge mode and an endless distance mode where tracks are generated on the fly. I enjoyed seeing how far I could go.

Sketchbook Motocross

motocross
This one of my favorite kind of games!

Sketchbook Motocross lets you draw your own levels and then play them! With realistic physics and the ability to draw the exact level you want, Sketchbook Motocross makes my dreams come true.

Sketchbook Motocross even lets you download levels created by others, so the fun never ends. Compete on leaderboards to see who’s the fastest in the world!

Captain Drumstick

captaindrumstick
Captain Drumstick wants to be the best pilot in the world. But first he must navigate his way through the clouds. Will you help him?

Captain Drumstick is a beautiful game with simple controls. Just slide your finger across the screen. But don’t stop there. As the clouds come you’ve got to dodge them gracefully.

Help Captain Drumstick earn medals along the way and become the most decorated captain chicken yet.

Uni

Uni
Uni is a student’s best friend. Uni offers a weighted grade calculator, reminders for classes and asignments, and a snapshot graph view of how you’re doing so far in each class.

With Uni you’ll never wonder what you grade is as teachers implement complicated weighted grades. Just input the scale and add your asignments. You’ll get reminders before asignments are due and before classes begin.

You can come back and put in your grades to see your current class grade and see which classes need extra attention on the beautiful graphs.

Shifty – Calendar Assist

shifty
Shifty makes it easy to set up your calendar with recurring schedules that might not be the same everytime.

The built in calendar lets you setup recurring events easily but only if they’re the same day each week or month. Shifty lets you quickly create events with the same name and information but on completely seperate days.

Simply tap each day you’d like to add an event. Then enter information about the recurring events all at once. Export to your favorite calendar, including iCloud shared calendars so your family and friends can keep up.



Honorable Mentions

Each month tons of our readers submit awesome applications they’re made for me to review. While I give ever app a try I don’t have time to write about them all. These are still great apps. Its not a popularity contest or even a favorite picking contest. I enjoy getting glimpse of the community through your apps. Take a moment to check out these great apps I just didn’t have time to share with you.

PayApp Social
Gelan Almagro
Hand Jive
29
Pit Board: Calendario de Carreras 2014
WiseLists
Jukebox Air
Polar Bear Ice Track Builder
Dominaedro
Cats Wars
iMergency World
Pilr
Farting Felix
Pithy Maxims
Bonkerz
#Ducky
AnimeHub



Where To Go From Here?

Another month come and gone. I love seeing what our community of readers comes up with. The apps you build are the reason we keep writing tutorials. Make sure you tell me about your next one, submit here!

If you’ve never made an app, this is your month! Check out our free tutorials to become an iOS star. What are you waiting for – I want to see your app next month!

Readers’ App Reviews – June 2014 is a post from: Ray Wenderlich

The post Readers’ App Reviews – June 2014 appeared first on Ray Wenderlich.

How to Make a Game Like Candy Crush with Swift Tutorial: Part 2

$
0
0
Learn to make a tasty match-3 game in the new Swift language

Learn to make a tasty match-3 game in the new Swift language

Welcome back to our how to make a game like Candy Crush with Swift tutorial series!

This is the second part of a two-part series that teaches you how to make a match-3 game like Candy Crush Saga or Bejeweled. Your game is called Cookie Crunch Adventure and it’s delicious!

In the first part of the tutorial, you loaded a level shape from a JSON file, placed cookie sprites on the screen and implemented the logic for detecting swipes and swapping cookies.

In this second and final part, you’ll implement the rest of the game rules, add loads of animations and polish Cookie Crunch Adventure to a top-10-quality shine. I’m getting hungry just thinking about it!

This Swift 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. You also need a copy of the resources zip (this is the same file from Part One).

Let’s crunch some cookies!

Getting Started

Everything you’ve worked on so far has been to allow the player to swap cookies. Next, your game needs to process the results of the swaps.

Swaps always lead to a chain of three or more matching cookies. The next thing to do is to remove those matching cookies from the screen and reward the player with some points.

This is the sequence of events:

Game flow

You’ve already done the first three steps: filling the level with cookies, calculating possible swaps and waiting for the player to make a swap. In this part of the Swift tutorial, you’ll implement the remaining steps.

Finding the Chains

At this point in the game flow, the player has made her move and swapped two cookies. Because the game only lets the player make a swap if it will result in a chain of three or more cookies of the same type, you know there is now at least one chain—but there could be additional chains, as well.

Before you can remove the matching cookies from the level, you first need to find all the chains. That’s what you’ll do in this section.

First, make a class that describes a chain. Go to File\New\File…, choose the iOS\Source\Swift File template and click Next. Name the file Chain.swift and click Create.

Replace the contents of Chain.swift with this:

class Chain: Hashable, Printable {
  var cookies = Array<Cookie>()  // private
 
  enum ChainType: Printable {
    case Horizontal
    case Vertical
 
    var description: String {
      switch self {
      case .Horizontal: return "Horizontal"
      case .Vertical: return "Vertical"
      }
    }
  }
 
  var chainType: ChainType
 
  init(chainType: ChainType) {
    self.chainType = chainType
  }
 
  func addCookie(cookie: Cookie) {
    cookies.append(cookie)
  }
 
  func firstCookie() -> Cookie {
    return cookies[0]
  }
 
  func lastCookie() -> Cookie {
    return cookies[cookies.count - 1]
  }
 
  var length: Int {
    return cookies.count
  }
 
  var description: String {
    return "type:\(chainType) cookies:\(cookies)"
  }
 
  var hashValue: Int {
    return reduce(cookies, 0) { $0.hashValue ^ $1.hashValue }
  }
}
 
func ==(lhs: Chain, rhs: Chain) -> Bool {
  return lhs.cookies == rhs.cookies
}

A chain has a list of cookie objects and a type: It’s either horizontal (a row of cookies) or vertical (a column). The type is defined as an enum; it is nested inside the Chain class because these two things are tightly coupled. If you feel adventurous, you can also add more complex chain types, such as L- and T-shapes.

There is a reason you’re using an array here to store the cookie objects and not a Set: It’s convenient to remember the order of the cookie objects so that you know which cookies are at the ends of the chain. This makes it easier to combine multiple chains into a single one to detect those L- or T-shapes.

Note: The chain implements Hashable so it can be placed inside a set. The code for hashValue may look strange but it simply performs an exclusive-or on the hash values of all the cookies in the chain. The reduce() function is one of Swift’s more advanced functional programming features.

To start putting these new chain objects to good use, open Level.swift. You’re going to add a method named removeMatches(), but before you get to that, you need a couple of helper methods to do the heavy lifting of finding chains.

To find a chain, you’ll need a pair of for loops that step through each square of the level grid.

Finding chains

While stepping through the cookies in a row horizontally, you want to find the first cookie that starts a chain.

You know a cookie begins a chain if at least the next two cookies on its right are of the same type. Then you skip over all the cookies that have that same type until you find one that breaks the chain. You repeat this until you’ve looked at all the possibilities.

Add this method to Level.swift to scan for horizontal cookie matches:

func detectHorizontalMatches() -> Set<Chain> {
  // 1
  let set = Set<Chain>()
  // 2
  for row in 0..NumRows {
    for var column = 0; column < NumColumns - 2 ; {
      // 3
      if let cookie = cookies[column, row] {
        let matchType = cookie.cookieType
        // 4
        if cookies[column + 1, row]?.cookieType == matchType &&
           cookies[column + 2, row]?.cookieType == matchType {
          // 5
          let chain = Chain(chainType: .Horizontal)
          do {
            chain.addCookie(cookies[column, row]!)
            ++column
          }
          while column < NumColumns && cookies[column, row]?.cookieType == matchType
 
          set.addElement(chain)
          continue
        }
      }
      // 6
      ++column
    }
  }
  return set
}

Here’s how this method works, step by step:

  1. You create a new set to hold the horizontal chains (Chain objects). Later, you’ll remove the cookies in these chains from the playing field.
  2. You loop through the rows and columns. Note that you don’t need to look at the last two columns because these cookies can never begin a new chain. Also notice that the inner for loop does not increment its loop counter; the incrementing happens conditionally inside the loop body.
  3. You skip over any gaps in the level design.
  4. You check whether the next two columns have the same cookie type. Normally you have to be careful not to step outside the bounds of the array when doing something like cookies[column + 2, row], but here that can’t go wrong. That’s why the for loop only goes up to NumColumns - 2. Also note the use of optional chaining with the question mark.
  5. At this point, there is a chain of at least three cookies but potentially there are more. This steps through all the matching cookies until it finds a cookie that breaks the chain or it reaches the end of the grid. Then it adds all the matching cookies to a new Chain object. You increment column for each match.
  6. If the next two cookies don’t match the current one or if there is an empty tile, then there is no chain, so you skip over the cookie.

Note: If there’s a gap in the grid, the use of optional chaining — the question mark after cookies[column, row]? — makes sure the while loop terminates at that point. So the logic above also works on levels with empty squares. Neat!

Next, add this method to scan for vertical cookie matches:

func detectVerticalMatches() -> Set<Chain> {
  let set = Set<Chain>()
 
  for column in 0..NumColumns {
    for var row = 0; row < NumRows - 2; {
      if let cookie = cookies[column, row] {
        let matchType = cookie.cookieType
 
        if cookies[column, row + 1]?.cookieType == matchType &&
           cookies[column, row + 2]?.cookieType == matchType {
 
          let chain = Chain(chainType: .Vertical)
          do {
            chain.addCookie(cookies[column, row]!)
            ++row
          }
          while row < NumRows && cookies[column, row]?.cookieType == matchType
 
          set.addElement(chain)
          continue
        }
      }
      ++row
    }
  }
  return set
}

The vertical version has the same kind of logic, but loops by column in the outer for loop and by row in the inner loop.

You may wonder why you don’t immediately remove the cookies from the level as soon as you detect that they’re part of a chain. The reason is that a cookie may be part of two chains at the same time: one horizontal and one vertical. So you don’t want to remove it until you’ve checked both the horizontal and vertical options.

Now that the two match detectors are ready, add the implementation for removeMatches():

func removeMatches() -> Set<Chain> {
  let horizontalChains = detectHorizontalMatches()
  let verticalChains = detectVerticalMatches()
 
  println("Horizontal matches: \(horizontalChains)")
  println("Vertical matches: \(verticalChains)")
 
  return horizontalChains.unionSet(verticalChains)
}

This method calls the two helper methods and then combines their results into a single set. Later, you’ll add more logic to this method but for now you’re only interested in finding the matches and returning the set.

You still need to call removeMatches() from somewhere and that somewhere is GameViewController.swift. Add this helper method:

func handleMatches() {
  let chains = level.removeMatches()
  // TODO: do something with the chains set
}

Later, you’ll fill out this method with code to remove cookie chains and drop other cookies into the empty tiles.

In handleSwipe(), change the call to scene.animateSwap() to this:

scene.animateSwap(swap, completion: handleMatches)

Recall that in Swift a closure and a function are really the same thing, so instead of passing a closure block to animateSwap(), you can also give it the name of a function.

Build and run, and swap two cookies to make a chain. You should now see something like this in Xcode’s debug pane:

List of matches

Removing the Chains

Level’s method is called “removeMatches”, but so far it only detects the matching chains. Now you’re going to remove those cookies from the game with a nice animation.

First, you need to update the data model—that is, remove the Cookie objects from the array for the 2-D grid. When that’s done, you can tell GameScene to animate the sprites for these cookies out of existence.

eatcookies

Removing the cookies from the model is simple enough. Add the following method to Level.swift:

func removeCookies(chains: Set<Chain>) {
  for chain in chains {
    for cookie in chain.cookies {
      cookies[cookie.column, cookie.row] = nil
    }
  }
}

Each chain has a list of cookie objects and each cookie knows its column and row in the grid, so you simply set that element in the array to nil to remove the cookie object from the data model.

Note: At this point, the Chain object is the only owner of the Cookie object. When the chain gets deallocated, so will these cookie objects.

In removeMatches(), replace the println() statements with the following:

removeCookies(horizontalChains)
removeCookies(verticalChains)

That takes care of the data model. Now switch to GameScene.swift and add the following method:

func animateMatchedCookies(chains: Set<Chain>, completion: () -> ()) {
  for chain in chains {
    for cookie in chain.cookies {
      if let sprite = cookie.sprite {
        if sprite.actionForKey("removing") == nil {        
          let scaleAction = SKAction.scaleTo(0.1, duration: 0.3)
          scaleAction.timingMode = .EaseOut
          sprite.runAction(SKAction.sequence([scaleAction, SKAction.removeFromParent()]),
                           withKey:"removing")
        }
      }
    }
  }
  runAction(matchSound)
  runAction(SKAction.waitForDuration(0.3), completion: completion)
}

This loops through all the chains and all the cookies in each chain, and then triggers the animations.

Because the same Cookie could be part of two chains (one horizontal and one vertical), you need to make sure to add only one animation to the sprite, not two. That’s why the action is added to the sprite under the key “removing”. If such an action already exists, you shouldn’t add a new animation to the sprite.

When the shrinking animation is done, the sprite is removed from the cookie layer. The waitForDuration() action at the end of the method ensures that the rest of the game will only continue after the animations finish.

Open GameViewController.swift and change handleMatches() to call this new animation:

func handleMatches() {
  let chains = level.removeMatches()
 
  scene.animateMatchedCookies(chains) {
    self.view.userInteractionEnabled = true
  }
}

Try it out. Build and run, and make some matches.

Match animation

Note: You don’t want the player to be able to tap or swipe on anything while the chain removal animations are happening. That’s why you disable userInteractionEnabled as the first thing in the swipe handler and enable it again once all the animations are done.

Dropping Cookies Into Empty Tiles

Removing the cookie chains leaves holes in the grid. Other cookies should now fall down to fill up those holes. Again, you’ll tackle this in two steps:

  1. Update the model.
  2. Animate the sprites.

Add this new method to Level.swift:

func fillHoles() -> Array<Array<Cookie>> {
  var columns = Array<Array<Cookie>>()
  // 1
  for column in 0..NumColumns {
    var array = Array<Cookie>()
    for row in 0..NumRows {
      // 2
      if tiles[column, row] != nil && cookies[column, row] == nil {
        // 3
        for lookup in (row + 1)..NumRows {
          if let cookie = cookies[column, lookup] {
            // 4
            cookies[column, lookup] = nil
            cookies[column, row] = cookie
            cookie.row = row
            // 5
            array.append(cookie)
            // 6
            break
          }
        }
      }
    }
    // 7
    if !array.isEmpty {
      columns.append(array)
    }
  }
  return columns
}

This method detects where there are empty tiles and shifts any cookies down to fill up those tiles. It starts at the bottom and scans upward. If it finds a square that should have a cookie but doesn’t, then it finds the nearest cookie above it and moves this cookie to the empty tile.

Filling holes

Here is how it all works, step by step:

  1. You loop through the rows, from bottom to top.
  2. If there’s a tile at a position but no cookie, then there’s a hole. Remember that the tiles array describes the shape of the level.
  3. You scan upward to find the cookie that sits directly above the hole. Note that the hole may be bigger than one square (for example, if this was a vertical chain) and that there may be holes in the grid shape, as well.
  4. If you find another cookie, move that cookie to the hole. This effectively moves the cookie down.
  5. You add the cookie to the array. Each column gets its own array and cookies that are lower on the screen are first in the array. It’s important to keep this order intact, so the animation code can apply the correct delay. The farther up the piece is, the bigger the delay before the animation starts.
  6. Once you’ve found a cookie, you don’t need to scan up any farther so you break out of the inner loop.
  7. If a column does not have any holes, then there’s no point in adding it to the final array.

At the end, the method returns an array containing all the cookies that have been moved down, organized by column.

Note: The return type of fillHoles() is Array<Array<Cookie>>, or an array-of-array-of-cookies. You can also write this as Cookie[][].

You’ve already updated the data model for these cookies with the new positions, but the sprites need to catch up. GameScene will animate the sprites and GameViewController is the in-between object to coordinate between the the model (Level ) and the view (GameScene).

Switch to GameScene.swift and add a new animation method:

func animateFallingCookies(columns: Array<Array<Cookie>>, completion: () -> ()) {
  // 1
  var longestDuration: NSTimeInterval = 0
  for array in columns {
    for (idx, cookie) in enumerate(array) {
      let newPosition = pointForColumn(cookie.column, row: cookie.row)
      // 2
      let delay = 0.05 + 0.15*NSTimeInterval(idx)
      // 3
      let sprite = cookie.sprite!
      let duration = NSTimeInterval(((sprite.position.y - newPosition.y) / TileHeight) * 0.1)
      // 4
      longestDuration = max(longestDuration, duration + delay)
      // 5
      let moveAction = SKAction.moveTo(newPosition, duration: duration)
      moveAction.timingMode = .EaseOut
      sprite.runAction(
        SKAction.sequence([
          SKAction.waitForDuration(delay),
          SKAction.group([moveAction, fallingCookieSound])]))
      }
  }
  // 6
  runAction(SKAction.waitForDuration(longestDuration), completion: completion)
}

Here’s how this works:

  1. As with the other animation methods, you should only call the completion block after all the animations are finished. Because the number of falling cookies may vary, you can’t hardcode this total duration but instead have to compute it.
  2. The higher up the cookie is, the bigger the delay on the animation. That looks more dynamic than dropping all the cookies at the same time. This calculation works because fillHoles() guarantees that lower cookies are first in the array.
  3. Likewise, the duration of the animation is based on how far the cookie has to fall (0.1 seconds per tile). You can tweak these numbers to change the feel of the animation.
  4. You calculate which animation is the longest. This is the time the game has to wait before it may continue.
  5. You perform the animation, which consists of a delay, a movement and a sound effect.
  6. You wait until all the cookies have fallen down before allowing the gameplay to continue.

Now you can tie it all together. Open GameViewController.swift. Replace the contents of handleMatches() with the following:

func handleMatches() {
  let chains = level.removeMatches()
  scene.animateMatchedCookies(chains) {
    let columns = self.level.fillHoles()
    self.scene.animateFallingCookies(columns) {
      self.view.userInteractionEnabled = true
    }
  }
}

This now calls fillHoles() to update the model, which returns the array that describes the fallen cookies and then passes that array onto the scene so it can animate the sprites to their new positions.

Note: To access a property or call a method in Objective-C you always had to use self. In Swift you don’t have to do this, except inside closures. That’s why inside handleMatches() you see self a lot. Swift insists on this to make it clear that the closure actually captures the value of self with a strong reference. In fact, if you don’t specify self inside a closure, the Swift compiler will give an error message.

Try it out!

Falling animation

It’s raining cookies! Notice that the cookies even fall properly across gaps in the level design.

Adding New Cookies

There’s one more thing to do to complete the game loop. Falling cookies leave their own holes at the top of each column.

Holes at top

You need to top up these columns with new cookies. Add a new method to Level.swift:

func topUpCookies() -> Array<Array<Cookie>> {
  var columns = Array<Array<Cookie>>()
  var cookieType: CookieType = .Unknown
 
  for column in 0..NumColumns {
    var array = Array<Cookie>()
    // 1
    for var row = NumRows - 1; row >= 0 && cookies[column, row] == nil; --row {
      // 2
      if tiles[column, row] != nil {
        // 3
        var newCookieType: CookieType
        do {
          newCookieType = CookieType.random()
        } while newCookieType == cookieType
        cookieType = newCookieType
        // 4
        let cookie = Cookie(column: column, row: row, cookieType: cookieType)
        cookies[column, row] = cookie
        array.append(cookie)
      }
    }
    // 5
    if !array.isEmpty {
      columns.append(array)
    }
  }
  return columns
}

Where necessary, this adds new cookies to fill the columns to the top. It returns an array with the new Cookie objects for each column that had empty tiles.

If a column has X empty tiles, then it also needs X new cookies. The holes are all at the top of the column now, so you can simply scan from the top down until you find a cookie.

Here’s how it works, step by step:

  1. You loop through the column from top to bottom. This for loop ends when cookies[column, row] is not nil—that is, when it has found a cookie.
  2. You ignore gaps in the level, because you only need to fill up grid squares that have a tile.
  3. You randomly create a new cookie type. It can’t be equal to the type of the last new cookie, to prevent too many “freebie” matches.
  4. You create the new Cookie object and add it to the array for this column.
  5. As before, if a column does not have any holes, you don’t add it to the final array.

The array that topUpCookies() returns contains a sub-array for each column that had holes. The cookie objects in these arrays are ordered from top to bottom. This is important to know for the animation method coming next.

Switch to GameScene.swift and the new animation method:

func animateNewCookies(columns: Array<Array<Cookie>>, completion: () -> ()) {
  // 1
  var longestDuration: NSTimeInterval = 0
 
  for array in columns {
    // 2
    let startRow = array[0].row + 1
 
    for (idx, cookie) in enumerate(array) {
      // 3
      let sprite = SKSpriteNode(imageNamed: cookie.cookieType.spriteName)
      sprite.position = pointForColumn(cookie.column, row: startRow)
      cookiesLayer.addChild(sprite)
      cookie.sprite = sprite
      // 4
      let delay = 0.1 + 0.2 * NSTimeInterval(array.count - idx - 1)
      // 5
      let duration = NSTimeInterval(startRow - cookie.row) * 0.1
      longestDuration = max(longestDuration, duration + delay)
      // 6
      let newPosition = pointForColumn(cookie.column, row: cookie.row)
      let moveAction = SKAction.moveTo(newPosition, duration: duration)
      moveAction.timingMode = .EaseOut
      sprite.alpha = 0
      sprite.runAction(
        SKAction.sequence([
          SKAction.waitForDuration(delay),
          SKAction.group([
            SKAction.fadeInWithDuration(0.05),
            moveAction,
            addCookieSound])
          ]))
    }
  }
  // 7
  runAction(SKAction.waitForDuration(longestDuration), completion: completion)
}

This is very similar to the “falling cookies” animation. The main difference is that the cookie objects are now in reverse order in the array, from top to bottom. Step by step, this is what the method does:

  1. The game is not allowed to continue until all the animations are complete, so you calculate the duration of the longest animation to use later in step 7.
  2. The new cookie sprite should start out just above the first tile in this column. An easy way to find the row number of this tile is to look at the row of the first cookie in the array, which is always the top-most one for this column.
  3. You create a new sprite for the cookie.
  4. The higher the cookie, the longer you make the delay, so the cookies appear to fall after one another.
  5. You calculate the animation’s duration based on far the cookie has to fall.
  6. You animate the sprite falling down and fading in. This makes the cookies appear less abruptly out of thin air at the top of the grid.
  7. You wait until the animations are done before continuing the game.

Finally, in GameViewController.swift, replace the chain of completion blocks in handleMatches() with the following:

func handleMatches() {
  let chains = level.removeMatches()
  scene.animateMatchedCookies(chains) {
    let columns = self.level.fillHoles()
    self.scene.animateFallingCookies(columns) {
      let columns = self.level.topUpCookies()
      self.scene.animateNewCookies(columns) {
        self.view.userInteractionEnabled = true
      }
    }
  }
}

Try it out!

Adding new cookies

A Cascade of Cookies

You may have noticed a couple of oddities after playing for a while. When the cookies fall down to fill up the holes and new cookies drop from the top, these actions sometimes create new chains of three or more. But what happens then?

You also need to remove these matching chains and ensure other cookies take their place. This cycle should continue until there are no matches left on the board. Only then should the game give control back to the player.

Handling these possible cascades may sound like a tricky problem, but you’ve already written all the code to do it! You just have to call handleMatches() again and again and again until there are no more chains.

In GameViewController.swift, inside handleMatches(), change the line that sets userInteractionEnabled to:

self.handleMatches()

Yep, you’re seeing that right: handleMatches() calls itself. This is called recursion and it’s a powerful programming technique. There’s only one thing you need to watch out for with recursion: At some point, you need to stop it, or the app will go into an infinite loop and eventually crash.

For that reason, add the following to the top of handleMatches(), right after the line that calls removeMatches() on the level:

if chains.count == 0 {
  beginNextTurn()
  return
}

If there are no more matches, the player gets to move again and the function exits to prevent another recursive call.

Finally, add this new beginNextTurn() method:

func beginNextTurn() {
  view.userInteractionEnabled = true
}

Try it out. If removing a chain creates another chain elsewhere, the game should now remove that chain, as well:

Cascade

There’s another problem. After a while, the game no longer seems to recognize swaps that it should consider valid. There’s a good reason for that. Can you guess what it is?

Solution Inside: Solution SelectShow>

The logic for this sits in Level.swift, in detectPossibleSwaps(). You need to call this method from beginNextTurn() in GameViewController.swift:

func beginNextTurn() {
  level.detectPossibleSwaps()
  view.userInteractionEnabled = true
}

Excellent! Now your game loop is complete. It has an infinite supply of cookies!

Scoring Points

In Cookie Crunch Adventure, the player’s objective is to score a certain number of points within a maximum number of swaps. Both of these values come from the JSON level file. The game should show these numbers on the screen so the player knows how well she’s doing.

First, add the following properties to GameViewController.swift:

var movesLeft: Int = 0
var score: Int = 0
 
@IBOutlet var targetLabel: UILabel
@IBOutlet var movesLabel: UILabel
@IBOutlet var scoreLabel: UILabel

The movesLeft and score variables keep track of how well the player is doing (model data), while the outlets show this on the screen (views).

Open Main.storyboard to add these labels to the view. Design the view controller to look like this:

View controller with labels

(This is an Xcode 5 screenshot but it looks similar in Xcode 6.)

Make sure to uncheck Use Auto Layout in the File inspector, the first tab on the right. In the dialog that appears, choose Disable Size Classes. This will turn the square scene into an iPhone-sized one, as in the image above.

To make the labels easier to see, give the main view a gray background color. Make the font for the labels Gill Sans Bold, size 20.0 for the number labels and 14.0 for the text labels. You may also wish to set a slight drop shadow for the labels so they are easier to see.

It looks best if you set center alignment on the number labels. Connect the three number labels to their respective outlets.

Because the target score and the maximum number of moves are stored in the JSON level file, you should load them into Level. Add the following properties to Level.swift:

let targetScore: Int!
let maximumMoves: Int!

These properties will store the values from the JSON data. They are marked with ! because it is possible that they get no value (if loading the level fails for some reason).

In Level.swift, add these two lines to the bottom of init(filename:):

init(filename: String) {
    ...
    if let tilesArray: AnyObject = dictionary["tiles"] {
      ...
      // Add these two lines:
      targetScore = (dictionary["targetScore"] as NSNumber).integerValue
      maximumMoves = (dictionary["moves"] as NSNumber).integerValue
    }
  }
}

By this point, you’ve parsed the JSON into a dictionary, so you grab the two values and store them.

Back in GameViewController.swift, add the following method:

func updateLabels() {
  targetLabel.text = NSString(format: "%ld", level.targetScore)
  movesLabel.text = NSString(format: "%ld", movesLeft)
  scoreLabel.text = NSString(format: "%ld", score)
}

You’ll call this method after every turn to update the text inside the labels.

Add the following lines to the top of beginGame(), before the call to shuffle():

movesLeft = level.maximumMoves
score = 0
updateLabels()

This resets everything to the starting values. Build and run, and your display should look like this:

Game with labels

Calculating the Score

The scoring rules are simple:

  • A 3-cookie chain is worth 60 points.
  • Each additional cookie in the chain increases the chain’s value by 60 points.

Thus, a 4-cookie chain is worth 120 points, a 5-cookie chain is worth 180 points and so on.

It’s easiest to store the score inside the Chain object, so each chain knows how many points it’s worth.

Add the following to Chain.swift:

var score: Int = 0

The score is model data, so it needs to be calculated by Level. Add the following method to Level.swift:

func calculateScores(chains: Set<Chain>) {
  // 3-chain is 60 pts, 4-chain is 120, 5-chain is 180, and so on
  for chain in chains {
    chain.score = 60 * (chain.length - 2)
  }
}

Now call this method from removeMatches(), just before the return statement:

calculateScores(horizontalChains)
calculateScores(verticalChains)

You need to call it twice because there are two sets of chain objects.

Now that the level object knows how to calculate the scores and stores them inside the Chain objects, you can update the player’s score and display it onscreen.

This happens in GameViewController.swift. Inside handleMatches(), just before the call to self.level.fillHoles(), add the following lines:

for chain in chains {
  self.score += chain.score
}
self.updateLabels()

This simply loops through the chains, adds their scores to the player’s total and then updates the labels.

Try it out. Swap a few cookies and observe your increasing score:

Score

Animating Point Values

It would be fun to show the point value of each chain with a cool little animation. In GameScene.swift, add a new method:

func animateScoreForChain(chain: Chain) {
  // Figure out what the midpoint of the chain is.
  let firstSprite = chain.firstCookie().sprite!
  let lastSprite = chain.lastCookie().sprite!
  let centerPosition = CGPoint(
    x: (firstSprite.position.x + lastSprite.position.x)/2,
    y: (firstSprite.position.y + lastSprite.position.y)/2 - 8)
 
  // Add a label for the score that slowly floats up.
  let scoreLabel = SKLabelNode(fontNamed: "GillSans-BoldItalic")
  scoreLabel.fontSize = 16
  scoreLabel.text = NSString(format: "%ld", chain.score)
  scoreLabel.position = centerPosition
  scoreLabel.zPosition = 300
  cookiesLayer.addChild(scoreLabel)
 
  let moveAction = SKAction.moveBy(CGVector(dx: 0, dy: 3), duration: 0.7)
  moveAction.timingMode = .EaseOut
  scoreLabel.runAction(SKAction.sequence([moveAction, SKAction.removeFromParent()]))
}

This creates a new SKLabelNode with the score and places it in the center of the chain. The numbers will float up a few pixels before disappearing.

Call this new method from animateMatchedCookies(), in between the two for loops:

for chain in chains {
 
  // Add this line:
  animateScoreForChain(chain)
 
  for cookie in chain.cookies {

When using SKLabelNode, Sprite Kit needs to load the font and convert it to a texture. That only happens once, but it does create a small delay, so it’s smart to pre-load this font before the game starts in earnest.

At the bottom of GameScene‘s init(), add the following line:

SKLabelNode(fontNamed: "GillSans-BoldItalic")

Now try it out. Build and run, and score some points!

Floating score

Combos!

What makes games like Candy Crush Saga fun is the ability to make combos, or more than one match in a row.

Of course, you should reward the player for making a combo by giving her extra points. To that effect, you’ll add a combo “multiplier”, where the first chain is worth its normal score, but the second chain is worth twice its score, the third chain is worth three times its score, and so on.

In Level.swift, add the following private property:

var comboMultiplier: Int = 0  // private

Update calculateScores() to:

func calculateScores(chains: Set<Chain>) {
  // 3-chain is 60 pts, 4-chain is 120, 5-chain is 180, and so on
  for chain in chains {
    chain.score = 60 * (chain.length - 2) * comboMultiplier
    ++comboMultiplier
  }
}

The method now multiplies the chain’s score by the combo multiplier and then increments the multiplier so it’s one higher for the next chain.

You also need a method to reset this multiplier on the next turn. Add the following method to Level.swift:

func resetComboMultiplier() {
  comboMultiplier = 1
}

Open GameViewController.swift and find beginGame(). Add this line just before the call to shuffle():

level.resetComboMultiplier()

Add the same line at the top of beginNextTurn().

And now you have combos. Try it out!

Combo

Challenge: How would you detect an L-shaped chain and make it count double the value for a row?

Solution Inside: Solution SelectShow>

Winning and Losing

The player only has so many moves to reach the target score. If she doesn’t, it’s game over. The logic for this isn’t difficult to add.

Create a new method in GameViewController.swift:

func decrementMoves() {
  --movesLeft
  updateLabels()
}

This simply decrements the counter keeping track of the number of moves and updates the onscreen labels.

Call it from the bottom of beginNextTurn():

decrementMoves()

Build and run to see it in action. After each swap, the game clears the matches and decreases the number of remaining moves by one.

Moves

Of course, you still need to detect when the player runs out of moves (game over!) or when she reaches the target score (success and eternal fame!), and respond accordingly.

First, though, the storyboard needs some work.

The Look of Victory or Defeat

Open Main.storyboard and drag an image view into the view. Make it 320×150 points and center it vertically.

Image view in storyboard

This image view will show either the “Game Over!” or “Level Complete!” message.

Go to the Size inspector and make the Autosizing mask for the image view look like this:

Autosizing mask image view

This will keep the image centered regardless of the screen size.

Now connect this image view to a new outlet on GameViewController.swift named gameOverPanel.

@IBOutlet var gameOverPanel: UIImageView

Also, add a property for a gesture recognizer:

var tapGestureRecognizer: UITapGestureRecognizer!

In viewDidLoad(), before you present the scene, make sure to hide this image view:

gameOverPanel.hidden = true

Now add a new method to show the game over panel:

func showGameOver() {
  gameOverPanel.hidden = false
  scene.userInteractionEnabled = false
 
  tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "hideGameOver")
  view.addGestureRecognizer(tapGestureRecognizer)
}

This un-hides the image view, disables touches on the scene to prevent the player from swiping and adds a tap gesture recognizer that will restart the game.

Add one more method:

func hideGameOver() {
  view.removeGestureRecognizer(tapGestureRecognizer)
  tapGestureRecognizer = nil
 
  gameOverPanel.hidden = true
  scene.userInteractionEnabled = true
 
  beginGame()
}

This hides the game over panel again and restarts the game.

The logic that detects whether it’s time to show the game over panel goes into decrementMoves(). Add the following lines to the bottom of that method:

if score >= level.targetScore {
  gameOverPanel.image = UIImage(named: "LevelComplete")
  showGameOver()
} else if movesLeft == 0 {
  gameOverPanel.image = UIImage(named: "GameOver")
  showGameOver()
}

If the current score is greater than or equal to the target score, the player has won the game! If the number of moves remaining is 0, the player has lost the game.

In either case, the method loads the proper image into the image view and calls showGameOver() to put it on the screen.

Try it out. When you beat the game, you should see this:

Level complete

Likewise, when you run out of moves, you should see a “Game Over” message.

Animating the Transitions

It looks a bit messy with this banner on top of all those cookies, so let’s add a little animation here as well. Add these two methods to GameScene.swift:

func animateGameOver(completion: () -> ()) {
  let action = SKAction.moveBy(CGVector(dx: 0, dy: -size.height), duration: 0.3)
  action.timingMode = .EaseIn
  gameLayer.runAction(action, completion: completion)
}
 
func animateBeginGame(completion: () -> ()) {
  gameLayer.hidden = false
  gameLayer.position = CGPoint(x: 0, y: size.height)
  let action = SKAction.moveBy(CGVector(dx: 0, dy: -size.height), duration: 0.3)
  action.timingMode = .EaseOut
  gameLayer.runAction(action, completion: completion)
}

animateGameOver() animates the entire gameLayer out of the way. animateBeginGame() does the opposite and slides the gameLayer back in from the top of the screen.

The very first time the game starts, you also want to call animateBeginGame() to perform this same animation. It looks better if the game layer is hidden before that animation begins, so add the following line to GameScene.swift in init(size:), immediately after you create the gameLayer node:

gameLayer.hidden = true

Now open GameViewController.swift and call animateGameOver() in showGameOver():

func showGameOver() {
  gameOverPanel.hidden = false
  scene.userInteractionEnabled = false
 
  scene.animateGameOver() {
    self.tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "hideGameOver")
    self.view.addGestureRecognizer(self.tapGestureRecognizer)
  }
}

Note that the tap gesture recognizer is now added after the animation is complete. This prevents the player from tapping while the game is still performing the animation.

Finally, in GameViewController.swift’s beginGame(), just before the call to shuffle(), call animateBeginGame():

scene.animateBeginGame() { }

The completion block for this animation is currently empty, but you’ll put something there soon.

Now when you tap after game over, the cookies should drop down the screen to their starting positions. Sweet!

Too many cookies

Whoops! Something’s not right. It appears you didn’t properly remove the old cookie sprites.

Add this new method to GameScene.swift to perform the cleanup:

func removeAllCookieSprites() {
  cookiesLayer.removeAllChildren()
}

And call it as the very first thing from shuffle() inside GameViewController.swift:

scene.removeAllCookieSprites()

That solves that! Build and run and your game should reset cleanly.

Manual Shuffling

There’s one more situation to manage: It may happen—though only rarely—that there is no way to swap any of the cookies to make a chain. In that case, the player is stuck.

There are different ways to handle this. For example, Candy Crush Saga automatically reshuffles the cookies. But in Cookie Crunch, you’ll give that power to the player. You will allow her to shuffle at any time by tapping a button, but it will cost her a move.

shufflecomic

Add an outlet property in GameViewController.swift:

@IBOutlet var shuffleButton: UIButton

And add an action method:

@IBAction func shuffleButtonPressed(AnyObject) {
  shuffle()
  decrementMoves()
}

Tapping the shuffle button costs a move, so this also calls decrementMoves().

In showGameOver(), add the following line to hide the shuffle button:

shuffleButton.hidden = true

Also do the same thing in init(size:), so the button is hidden when the game first starts.

In beginGame(), in the animation's completion block, put the button back on the screen again:

scene.animateBeginGame() {
  self.shuffleButton.hidden = false
}

Now open Main.storyboard and add a button to the bottom of the screen:

Shuffle button storyboard

Set the title to “Shuffle” and make the button 100x36 points big. To style the button, give it the font Gill Sans Bold, 20 pt. Make the text color white with a 50% opaque black drop shadow. For the background image, choose “Button”, an image you added to the asset catalog in Part One.

Set the autosizing to make this button stick to the bottom of the screen so it will also work on 3.5-inch phones:

Autosizing shuffle button

Finally, connect the shuffleButton outlet to the button and its Touch Up Inside event to the shuffleButtonPressed: action.

Try it out!

Shuffle button in the game

Note: When shuffling a deck of cards, you take the existing cards, change their order and deal out the same cards again in a different order. In this game, however, you simply get all new—random!—cookies. Finding a distribution of the same set of cookies that allows for at least one swap is an extremely difficult computational problem, and after all, this is only a casual game.

The shuffle is a bit abrupt, so let’s make the new cookies appear with a cute animation. In GameScene.swift, go to addSpritesForCookies() and add the following lines inside the for loop, after the existing code:

// Give each cookie sprite a small, random delay. Then fade them in.
sprite.alpha = 0
sprite.xScale = 0.5
sprite.yScale = 0.5
 
sprite.runAction(
  SKAction.sequence([
    SKAction.waitForDuration(0.25, withRange: 0.5),
    SKAction.group([
      SKAction.fadeInWithDuration(0.25),
      SKAction.scaleTo(1.0, duration: 0.25)
      ])
    ]))

This gives each cookie sprite a small, random delay and then fades them into view. It looks like this:

Shuffle animation

Bring on the Muzak

Let’s give the player some smooth, relaxing music to listen to while she crunches cookies. Add this line to the top of GameViewController.swift to include the AVFoundation framework:

import AVFoundation

Also add the following property:

var backgroundMusic: AVAudioPlayer!

Add these lines to viewDidLoad(), just before the call to beginGame():

// Load and start background music.
let url = NSBundle.mainBundle().URLForResource("Mining by Moonlight", withExtension: "mp3")
backgroundMusic = AVAudioPlayer(contentsOfURL: url, error: nil)
backgroundMusic.numberOfLoops = -1
backgroundMusic.play()

This loads the background music MP3 and sets it to loop forever. That gives the game a whole lot more swing!

Drawing Better Tiles

If you compare your game closely to Candy Crush Saga, you’ll notice that the tiles are drawn slightly differently. The borders in Candy Crush look much nicer:

Border comparison

Also, if a cookie drops across a gap, your game draws it on top of the background, but candies in Candy Crush appear to fall behind the background:

Masked sprite comparison

Recreating this effect isn’t too difficult but it requires a number of new sprites. You can find these in the tutorial’s Resources in the Grid.atlas folder. Drag this folder into your Xcode project. This creates a second texture atlas with just these images.

In GameScene.swift, add two new properties:

let cropLayer = SKCropNode()
let maskLayer = SKNode()

In init(size:), add these lines below the code that creates the tilesLayer:

gameLayer.addChild(cropLayer)
 
maskLayer.position = layerPosition
cropLayer.maskNode = maskLayer

This makes two new layers: cropLayer, which is a special kind of node called an SKCropNode, and a mask layer. A crop node only draws its children where the mask contains pixels. This lets you draw the cookies only where there is a tile, but never on the background.

Replace this line:

gameLayer.addChild(cookiesLayer)

With this:

cropLayer.addChild(cookiesLayer)

Now, instead of adding the cookiesLayer directly to the gameLayer, you add it to this new cropLayer.

To fill in the mask of this crop layer, make two changes to addTiles():

  • Replace "Tile" with "MaskTile"
  • Replace tilesLayer with maskLayer

Wherever there’s a tile, the method now draws the special MaskTile sprite into the layer functioning as the SKCropNode’s mask. The MaskTile is slightly larger than the regular tile.

Build and run. Notice how the cookies get cropped when they fall through a gap:

Cookie is cropped

Tip: If you want to see what the mask layer looks like, add this line to init(size:)
cropLayer.addChild(maskLayer)

Don’t forget to remove it again when you’re done!

For the final step, add the following code to the bottom of addTiles():

for row in 0...NumRows {
  for column in 0...NumColumns {
    let topLeft     = (column > 0) && (row < NumRows) 
                                   && level.tileAtColumn(column - 1, row: row)
    let bottomLeft  = (column > 0) && (row > 0)      
                                   && level.tileAtColumn(column - 1, row: row - 1)
    let topRight    = (column < NumColumns) && (row < NumRows)
                                            && level.tileAtColumn(column, row: row)
    let bottomRight = (column < NumColumns) && (row > 0) 
                                            && level.tileAtColumn(column, row: row - 1)
 
    // The tiles are named from 0 to 15, according to the bitmask that is
    // made by combining these four values.
    let value = Int(topLeft) | Int(topRight) << 1 | Int(bottomLeft) << 2 | Int(bottomRight) << 3
 
    // Values 0 (no tiles), 6 and 9 (two opposite tiles) are not drawn.
    if value != 0 && value != 6 && value != 9 {
      let name = String(format: "Tile_%ld", value)
      let tileNode = SKSpriteNode(imageNamed: name)
      var point = pointForColumn(column, row: row)
      point.x -= TileWidth/2
      point.y -= TileHeight/2
      tileNode.position = point
      tilesLayer.addChild(tileNode)
    }
  }
}

This draws a pattern of border pieces in between the level tiles. As a challenge, try to decipher for yourself how this method works. :]

Solution Inside: Solution SelectShow>

Build and run, and you should now have a game that looks and acts just like Candy Crush Saga!

Final game

Where to Go From Here?

Congrats for making it to the end! This has been a long but Swift tutorial, and you are coming away with all the basic building blocks for making your own match-3 games.

You can download the final Xcode project here.

Here are ideas for other features you could add:

  • Special cookies when the player matches a certain shape. For example, Candy Crush Saga gives you a cookie that can clear an entire row when you match a 4-in-a-row chain.
  • Detection of special chains, such as L- or T-shapes, that reward the player with bonus points or special power-ups.
  • Boosts, or power-ups the player can use any time she wants. For example, one boost might remove all the cookies of one type from the screen at once.
  • Jelly levels: On these levels, some tiles are covered in jelly. You have X moves to remove all the jelly. This is where the Tile class comes in handy. You can give it a BOOL jelly property and if the player matches a cookie on this tile, set the jelly property to NO to remove the jelly.
  • Hints: If the player doesn’t make a move for two seconds, light up a pair of cookies that make a valid swap.
  • Automatically move the player to the next level if she completes the current one.
  • Shuffle the cookies automatically if there are no possible moves.

As you can see, there’s still plenty to play with. Have fun!

Credits: Artwork by Vicki Wenderlich. The music is by Kevin MacLeod. The sound effects are based on samples from freesound.org.

Some of the techniques used in this source code are based on a blog post by Emanuele Feronato.

How to Make a Game Like Candy Crush with Swift Tutorial: Part 2 is a post from: Ray Wenderlich

The post How to Make a Game Like Candy Crush with Swift Tutorial: Part 2 appeared first on Ray Wenderlich.

Video Tutorial: Saving Data in iOS Part 5: App Settings and User Defaults


Reminder: Free Live Tech Talk (Facebook Pop Animation Library) Tomorrow (Tuesday)!

$
0
0
Free live tech talk (Facebook Pop Animation Library) tomorrow!

Free live tech talk (Facebook Pop Animation Library) tomorrow!

This is a reminder that we are having a free live tech talk on the Facebook Pop Animation Library tomorrow (Tuesday Jul 1), and you’re all invited! Here are the details:

  • When: Tuesday, Jul 1th at 2:00 PM EST – 3:00 PM EST
  • What: Facebook Pop Tech Talk/Demo followed by live Q&A (come w/ questions!)
  • Who: Ryan Nystrom (raywenderlich.com Tech Editor) with Kimon Tsinteris and Oliver Rickard (Facebook Pop developers)
  • 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 (Facebook Pop Animation Library) Tomorrow (Tuesday)! is a post from: Ray Wenderlich

The post Reminder: Free Live Tech Talk (Facebook Pop Animation Library) Tomorrow (Tuesday)! appeared first on Ray Wenderlich.

Dynamic Table View Cell Height and Auto Layout

$
0
0
Learn how to create dynamic height table view cells using auto layout.

Learn how to create dynamic height table view cells using auto layout.

If you wanted to create a customized table view complete with dynamic table view cell height in the past, you had to write a lot of sizing code. You had to calculate the height of every label, image view, text field, and everything else within the cell–manually.

Frankly, this was arduous, drool-inducing and error-prone.

In this dynamic table view cell height tutorial, you’ll learn how to create custom cells and dynamically size them to fit their contents. If you’ve worked with custom cells before, you’re probably thinking, “That’s going to take a lot of sizing code.”

However, what if we told you that you’re not going to write any sizing code at all?

“Preposterous!” you might say. Oh, but you can. Furthermore, you will!

By the time you’re at the end of this tutorial, you’ll know how to save yourself from hundreds of lines of code by leveraging auto layout.

Note: This tutorial works with existing iOS 7 apps and Xcode 5. It does not cover the iOS 8 Beta features for auto-sizing table view cells.

This tutorial assumes you have basic familiarity with using both auto layout and UITableView (including its data source and delegate methods). If you need to brush up on these topics before continuing, we’ve got you covered!

Getting Started

iOS 6 introduced a wonderful new technology: auto layout. Developers rejoiced; parties commenced in the streets; bands performed songs to recognize its greatness…

Okay, so those might be overstatements, but it was a big deal.

While it inspired hope for countless developers, initially, auto layout was cumbersome to use. Manually writing auto layout code was, and still is, a great example of the verbosity of Objective-C, and Interface Builder was often counter-productive in terms of its helpfulness with constraints.

With the introduction of Xcode 5.1 and iOS 7, auto layout support in Interface Builder took a great leap forward. In addition to that, iOS 7 introduced a very important delegate method in UITableViewDelegate:

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;

This method allows a table view to calculate the actual height of each cell lazily, even deferring until the moment that table’s needed. At long last, auto layout can do the heavy lifting in dynamically calculating cell height. (Cue the bands!)

But, you probably don’t want to get bogged down in theory right now, do you? You’re probably ready to get down to coding, so let’s get down to business with the tutorial app.

Tutorial App Overview

Imagine that your top client has come to you and said, “Our users are clamoring for a way to view their favorite Deviant Artists’ submissions.”

Did you mention that you’ve not heard of Deviant Art?

“Well, it’s a popular social networking platform where artists can share their art creations, called deviations and blog posts too.” your client explains. “You really should check out the Deviant Art website.”

Fortunately, Deviant Art offers a Media RSS endpoint, which you can use to access deviations and posts by artist.

“We started making the app, but we’re stumped at how to display the content in a table view,” your client admits. “Can you make it work?”

Suddenly you feel the urge to slide into the nearest phone booth and change into your cape. Now, you just need the tools to make it happen, so you can be your client’s hero.

First download the “client’s code” (the starter project for this tutorial) here.

This project uses CocoaPods, so open DeviantArtBrowser.xcworkspace (not the .xcodeproj file). The pods are included in the zip for you, so you don’t need to run pod install.

If you’d like to learn more about CocoaPods, you should go through our Introduction to CocoaPods tutorial.

The starter project successfully downloads content from the Deviant Art RSS feed, but doesn’t have a way of displaying any of that yet. That’s where you come in :]

Open Main.storyboard (under the Views group), and you’ll see four scenes:

Main.storyboard

From left to right, they are:

  • A top-level navigation controller
  • RWFeedViewController, titled Deviant Browser
  • Two scenes for RWDetailViewController (one displays only text content and the other displays both text and an image); their titles are Deviant Article and Deviant Media

Build and run. You’ll see the console log output, but nothing else works yet.

The log output should look like this:

2014-05-28 00:52:01.588 DeviantArtBrowser[1191:60b] GET 'http://backend.deviantart.com/rss.xml?q=boost%3Apopular'

2014-05-28 00:52:03.144 DeviantArtBrowser[1191:60b] 200 'http://backend.deviantart.com/rss.xml?q=boost%3Apopular' [1.5568 s]

The app is making a network request and getting a response, but does nothing with the response data.

Now, open RWFeedViewController.m. This is where most of the new code will go. Have a look at this snippet from parseForQuery::

  [self.parser parseRSSFeed:RWDeviantArtBaseURLString
                 parameters:[self parametersForQuery:query]
                    success:^(RSSChannel *channel) {
                      [weakSelf convertItemsPropertiesToPlainText:channel.items];
                      [weakSelf setFeedItems:channel.items];
 
                      [weakSelf reloadTableViewContent];
                      [weakSelf hideProgressHUD];
                    } failure:^(NSError *error) {
                      [weakSelf hideProgressHUD];
                      NSLog(@"Error: %@", error);
                    }];

self.parser is an instance of RSSParser, which is part of MediaRSSParser.

This method initiates a network request to Deviant Art to get the RSS feed, and then it returns an RSSChannel to the success block. After formatting , which is simply converting HTML to plain text, the success stores the channel.items as a local property called feedItems.

The channel.items array populates with RSSItem objects, each of which represents a single item element in an RSS feed. Great, you now know what you’ll need to display in the table view: the feedItems array!

Lastly, notice that there are three warnings in the project. What are these about?

It looks like someone put #warning statements showing what to implement. Well, that’s convenient, now isn’t it?

Well, that's convenient!

Create a Basic Custom Cell

After a quick dive into the source code, you’ve found the app is already fetching data, but it’s not displaying anything. To do that, you first need to create a custom table view cell to show the data.

Add a new class to the DeviantArtBrowser project and name it RWBasicCell. Make it a subclass of UITableViewCell; and make sure Also create xib file is not checked.

Open RWBasicCell.h, and add the following properties right after @interface RWBasicCell : UITableViewCell :

@property (nonatomic, weak) IBOutlet UILabel *titleLabel;
@property (nonatomic, weak) IBOutlet UILabel *subtitleLabel;

Next open Main.storyboard, and drag and drop a new UITableViewCell onto the table view of RWFeedViewController.

Set the Custom Class of the cell to RWBasicCell.

Set RWBasicCell Class

Set the Identifier (Reuse Identifier) to RWBasicCell.

Set RWBasicCell Identifier

Set the Row Height of the cell to 83.

Set RWBasicCell Row Height

Drag and drop a new UILabel onto the cell, and set its text to “Title”.

Add Title Label to RWBasicCell

Set the title label’s Lines (the number of lines the label can have at most) to 0, meaning “unlimited”.

Set RWBasicCell Title Label's Number Lines

Set the title label’s size and position to the values of the screenshot below.

Set RWBasicCell Title Label's Frame

Connect the titleLabel outlet of RWBasicCell to the title label on the cell.

Connect Outlet RWBasicCell Title Label's Outlet

Next, drag and drop a second UILabel onto the cell, right below the title label, and set its text to “Subtitle”.

Add Subtitle Label to RWBasicCell

As you did with the title label, change the values of the size and position to match the values of the screenshot below.

Set RWBasicCell Subtitle Label's Frame

Set the subtitle label’s Color to Light Gray Color; its Font to System 15.0; and its Lines to 0.

Set RWBasicCell Subtitle Label's Properties

Connect the subtitleLabel outlet of RWBasicCell to the subtitle label on the cell.

Connect RWBasicCell Subtitle Label Outlet

Awesome, you’ve laid out and configured RWBasicCell. Now you just need to add auto layout constraints. Select the title label and pin the top, trailing and leading edges to 20 points from the superview.

Set RWBasicCell Title Label's Pin Constraints

This ensures that no matter how big or small the cell may be, the title label is always:

  • 20 points from the top
  • Spans the entire width of the cell, minus 20 points of padding on the left and right

Now select the subtitle label and this time, pin the leading, bottom, and trailing edges to 20 points from the superview.

Set RWBasicCell Subtitle Label's Pin Constraints

Similar to the title label, these constraints work together to ensure the subtitle label is always at 20 points from the bottom and spans the entire width of the cell, minus a little padding.

Pro tip: the trick to getting auto layout working on a UITableViewCell is to ensure there are constraints that pin each subview to all the sides – that is, each subview should have leading, top, trailing and bottom constraints.

Further, there should be a clear line of constraints going from the top to the bottom of the contentView. This way, auto layout will correctly determine the height of the contentView, based on its subviews.

The tricky part is that Interface Builder often doesn’t warn you if you’re missing some of these constraints…

Auto layout will simply not return the correct heights when you try to run the project. For example, it may always return 0 for the cell height, which is a strong clue that indicates your constraints are probably wrong.

If you run into issues in your own projects, try adjusting your constraints until the above criteria are met.

Select the subtitle label, hold down ⌃ control and drag to the title label. Choose Vertical Spacing to pin the top of the subtitle label to the bottom of the title label.

You probably have some warnings related to auto layout, but you’re about to fix those.

On the title and the subtitle labels, set the Horizontal and Vertical constraints for Content Hugging Priority and Content Compression Resistance Priority to 1000.

Set RWBasicCell Labels' Content Hugging and Compression Resistance Priorities

By setting these priorities to 1000, you’re forcing these constraints to be prioritized over all others. This tells auto layout that you want the labels to fit to show all of their text.

Lastly, select the title label and change its Intrinsic Size from Default (System Defined) to Placeholder. Do the same to the subtitle label’s Intrinsic Size.

Change Labels' Intrinsic Content Size to Placeholder

This tells Interface Builder to assume the current size of the labels is also their intrinsic content size. By doing that, the constraints are once again unambiguous and the warnings will disappear.

In the end, the auto layout constraints should look like this on RWBasicCell:

RWBasicCell's Final Constraints

Review: Does this satisfy the previous auto layout criteria?

  1. Does each subview have constraints that pin all of their sides? Yes.
  2. Are there constraints going from the top to the bottom of the contentView? Yes.

The titleLabel connects to the top by 20 points, it’s connected to the subtitleLabel by 0 points, and the subtitleLabel connects to the bottom by 20 points.

So auto layout can now determine the height of the cell!

Next, you need to create a segue from RWBasicCell to the scene titled Deviant Article:

Select RWBasicCell and control-drag to the Deviant Article scene. Select Push from the Selection Segue options.

Interface Builder will also automatically change the cell Accessory property to Disclosure Indicator, which doesn’t look great with the design of the app. Change the Accessory back to None.

Set RWBasicCell Accessory to None

Now, whenever a user taps on an RWBasicCell, the app will segue to RWDetailViewController.

Awesome, RWBasicCell is setup! If you build and run the app now, you’ll see that… nothing has changed. What the what?!

Say What...!?

Remember those #warning statements from before? Yep, those are your troublemakers. You need to implement the table view data source and delegate methods.

Implement UITableView Delegate and Data Source

Add the following to the top of RWFeedViewController.m, right below the #import statements:

#import "RWBasicCell.h"
static NSString * const RWBasicCellIdentifier = @"RWBasicCell";

You’ll be using RWBasicCell in both the data source and delegate methods and will need to be able to identify it by the Reuse Identifier you set to RWBasicCellIdentifier in the Storyboard.

Next, start by implementing the data source methods.

Replace tableView:numberOfRowsInSection: with the following:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  return [self.feedItems count];
}

Then replace tableView:cellForRowAtIndexPath: with the following set of methods:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [self basicCellAtIndexPath:indexPath];
}
 
- (RWBasicCell *)basicCellAtIndexPath:(NSIndexPath *)indexPath {
  RWBasicCell *cell = [self.tableView dequeueReusableCellWithIdentifier:RWBasicCellIdentifier forIndexPath:indexPath];
  [self configureBasicCell:cell atIndexPath:indexPath];
  return cell;
}
 
- (void)configureBasicCell:(RWBasicCell *)cell atIndexPath:(NSIndexPath *)indexPath {
  RSSItem *item = self.feedItems[indexPath.row];
  [self setTitleForCell:cell item:item];
  [self setSubtitleForCell:cell item:item];
}
 
- (void)setTitleForCell:(RWBasicCell *)cell item:(RSSItem *)item {
  NSString *title = item.title ?: NSLocalizedString(@"[No Title]", nil);
  [cell.titleLabel setText:title];
}
 
- (void)setSubtitleForCell:(RWBasicCell *)cell item:(RSSItem *)item {
  NSString *subtitle = item.mediaText ?: item.mediaDescription;
 
  // Some subtitles can be really long, so only display the
  // first 200 characters
  if (subtitle.length > 200) {
    subtitle = [NSString stringWithFormat:@"%@...", [subtitle substringToIndex:200]];
  }
 
  [cell.subtitleLabel setText:subtitle];
}

Here’s what’s happening above:

  • In tableView:cellForRowAtIndexPath:, you call basicCellAtIndexPath: to get an RWBasicCell. As you’ll see later on, it’s easier to add additional types of custom cells if you create a helper method like this instead of returning a cell directing by the data source method.
  • In basicCellAtIndexPath:, you dequeue an RWBasicCell, configure it with configureBasicCell:atIndexPath:, and finally, return the configured cell.
  • In configureBasicCell:atIndexPath:, you get a reference to the item at the indexPath, which then gets and sets the titleLabel and subtitleLabel texts on the cell.

Now replace tableView:heightForRowAtIndexPath: with the following set of methods:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  return [self heightForBasicCellAtIndexPath:indexPath];
}
 
- (CGFloat)heightForBasicCellAtIndexPath:(NSIndexPath *)indexPath {
  static RWBasicCell *sizingCell = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sizingCell = [self.tableView dequeueReusableCellWithIdentifier:RWBasicCellIdentifier];
  });
 
  [self configureBasicCell:sizingCell atIndexPath:indexPath];
  return [self calculateHeightForConfiguredSizingCell:sizingCell];
}
 
- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
  [sizingCell setNeedsLayout];
  [sizingCell layoutIfNeeded];
 
  CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
  return size.height;
}

You’re finally using auto layout to calculate the cell height for you! Here’s what’s going on:

  1. In tableView:heightForRowAtIndexPath:, similar to the data source method, you simply return another method that does the actual calculation, heightForBasicCellAtIndexPath:. Once again, this makes it easier to add additional types of custom cells later on.
  2. You might have noticed that heightForBasicCellAtIndexPath: is pretty interesting.
  • This method instantiates a sizingCell using GCD to ensure it’s created only once
  • Calls configureBasicCell:atIndexPath: to configure the cell
  • Returns another method: calculateHeightForConfiguredSizingCell:. You probably already guessed it–this was also extracted to make it easier to add additional cells later on.
  • Lastly, in calculateHeightForConfiguredSizingCell:, you:
    • First request the cell to lay out its content by calling setNeedsLayout and layoutIfNeeded.
    • Then you ask auto layout to calculate the systemLayoutSizeFittingSize:, passing in the parameter UILayoutFittingCompressedSize, and that means “use the smallest possible size” that fits the auto layout constraints.

    Build and Run, and you should see a populated table view!

    Populated Table View

    This looks great! Try rotating to landscape mode, however, and you might notice something peculiar:

    Table View Landscape Problem

    “Why is there so much extra space around the subtitle label?” you might ask.

    Well, there’s this preferredMaxLayoutWidth property on UILabel that affects its layout. Whenever the screen orientation changes, preferredMaxLayoutWidth does not update.

    Fortunately, you can fix this by creating a subclass of UILabel. Add a new class to the project, name it RWLabel and make it a subclass of UILabel.

    In RWLabel.m, replace everything within @implementation RWLabel with the following:

    - (void)layoutSubviews {
      [super layoutSubviews];
     
      if (self.numberOfLines == 0) {
     
        // If this is a multiline label, need to make sure 
        // preferredMaxLayoutWidth always matches the frame width 
        // (i.e. orientation change can mess this up)
     
        if (self.preferredMaxLayoutWidth != self.frame.size.width) {
          self.preferredMaxLayoutWidth = self.frame.size.width;
          [self setNeedsUpdateConstraints];
        }
      }
    }
     
    - (CGSize)intrinsicContentSize {
      CGSize size = [super intrinsicContentSize];
     
      if (self.numberOfLines == 0) {
     
        // There's a bug where intrinsic content size 
        // may be 1 point too short
     
        size.height += 1;
      }
     
      return size;
    }

    As noted in the comments, RWLabel fixes two issues with UILabel:

    1. If the label has multiple lines, it makes sure that preferredMaxLayoutWidth always equals the frame width.
    2. Sometimes, the intrinsicContentSize can be 1 point too short. It adds 1 point to the intrinsicContentSize if the label has multiple lines.

    Now you need to update RWBasicCell to use this custom subclass. In RWBasicCell.h, add the following right after the other #import statements:

    #import "RWLabel.h"

    Then replace both of the @property lines with the following:

    @property (nonatomic, weak) IBOutlet RWLabel *titleLabel;
    @property (nonatomic, weak) IBOutlet RWLabel *subtitleLabel;

    In Main.storyboard, navigate to the Feed View Controller scene, select the title label and change its class to RWLabel.

    Change RWBasicCell's labels class to RWLabel

    Do the same for the subtitle label.

    In RWFeedViewController.m, add the following inside calculateHeightForConfiguredSizingCell: (make it the first line in the method):

    sizingCell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.bounds), 0.0f);

    This will make each RWLabel update its preferredMaxLayoutWidth property.

    Build and Run; the labels should now be sized and positioned correctly in both portrait and landscape orientation.

    Oh Yeah!

    Where are the Images?

    The app is looking great, but doesn’t it feel like there’s something missing? Oh snap, where are the images?

    Deviant Art is all about images, but the app doesn’t show any of them. You need to fix that or your client will think you’ve lost your mind!

    A simple approach you might take is to add an image view to RWBasicCell. While this might work for some apps, Deviant Art has both deviations (posts with images) and blog posts (without images), so in this instance, it’s actually better to create a new custom cell.

    Add a new class to the project. Name it RWImageCell and make it a subclass of RWBasicCell. The reason it’s a subclass of RWBasicCell is because it too will need a titleLabel and subtitleLabel.

    Open RWImageCell.h and add the following right after @interface RWImageCell : RWBasicCell:

    @property (nonatomic, weak) IBOutlet UIImageView *customImageView;

    This property’s name iscustomImageView rather than imageView, because there is already an imageView property on UITableViewCell.

    Open Main.storyboard, select the basic cell you were working on before and copy it using ⌘C, or navigate to Edit > Copy.

    Select the table view and press ⌘P or click Edit > Paste to create a new copy of the cell.

    Select the new cell and change its Custom Class to RWImageCell. Likewise, change its Reuse Identifier to RWImageCell.

    Select RWImageCell’s title label and change its x position to 128 and width to 172. Do the same to the subtitle label.

    Interface Builder should give a warning about ambiguous auto layout constraints, because the constraints on those labels put them at a different position than what you just defined.

    To correct this, first select RWImageCell’s title label to show its constraints, and then select its leading constraint and delete it. Delete the subtitle label’s leading constraint as well.

    Now select RWImageCell’s title label and change its Intrinsic Size to Placeholder. Do the same to the subtitle label’s Intrinsic Size.

    These tell Interface Builder to update the Placeholder to the current frame of the view. Check again, because the warnings should now be gone.

    You need to add an image view to the cell next, but the height is currently a bit too small for it. So, select RWImageCell and change its Row Height to 141.

    Now drag and drop an image view on RWImageCell. Set its size and position to the values shown in the screenshot below.

    RWImageCell-ImageView-Rect

    Select the image view and press pin the leading, top and bottom to 20. Pin the width and height to 100, and press Add 5 constraints.

    Set ImageView Pin Constraints

    Select the image view to show its constraints and then select its bottom constraint to edit it. In the attributes editor, change its Relation to Greater Than or Equal and its Priority to 999.

    Set ImageView's Bottom Constraint

    Similarly, select the subtitleLabel to show its constraints, and then select its bottom constraint. In the attributes editor, change its Relation to Greater Than or Equal, yet leave its Priority set to 1000.

    This basically says to auto layout, “There should be at least 20 points below both imageView and subtitleLabel, but if you must, you may break the bottom constraint on imageView to show a longer subtitle.”

    Next, select RWImageCell’s title label, and press pin leading to 8, and press Add 1 constraint. Do the same for the subtitle label.

    In the end, the auto layout constraints should look like this on RWImageCell.

    RWImageCell Constraints

    You also need to select RWImageCell and actually connect the customImageView outlet to the image view.

    Lastly, you need to create a segue from RWImageCell to the scene titled Deviant Media, so the app shows this screen when a user clicks on an RWImageCell.

    Similar to how you setup the basic cell, select RWImageCell, control-drag to the scene titled Deviant Media, and then select Push from the Selection Segue options.

    Make sure you also change the Accessory to None.

    Great, RWImageCell is setup! Now you just need to add the code to display it.

    Show Me the Images!

    Add the following to the top of RWFeedViewController.m, right below the #import statements:

    #import "RWImageCell.h"
    static NSString * const RWImageCellIdentifier = @"RWImageCell";

    You’ll add RWImageCell to both the data source and delegate methods. This will need to be able to identify it by the previously specified Reuse Identifier, which is set to RWImageCellIdentifier.

    Still in RWFeedViewController.m, replace tableView:cellForRowAtIndexPath: with the following:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      if ([self hasImageAtIndexPath:indexPath]) {
        return [self galleryCellAtIndexPath:indexPath];
      } else {
        return [self basicCellAtIndexPath:indexPath];
      }
    }
     
    - (BOOL)hasImageAtIndexPath:(NSIndexPath *)indexPath {
      RSSItem *item = self.feedItems[indexPath.row];
      RSSMediaThumbnail *mediaThumbnail = [item.mediaThumbnails firstObject];
      return mediaThumbnail.url != nil;
    }
     
    - (RWImageCell *)galleryCellAtIndexPath:(NSIndexPath *)indexPath {
      RWImageCell *cell = [self.tableView dequeueReusableCellWithIdentifier:RWImageCellIdentifier forIndexPath:indexPath];
      [self configureImageCell:cell atIndexPath:indexPath];
      return cell;
    }
     
    - (void)configureImageCell:(RWImageCell *)cell atIndexPath:(NSIndexPath *)indexPath {
      RSSItem *item = self.feedItems[indexPath.row];
      [self setTitleForCell:cell item:item];
      [self setSubtitleForCell:cell item:item];
      [self setImageForCell:(id)cell item:item];
    }
     
    - (void)setImageForCell:(RWImageCell *)cell item:(RSSItem *)item {
      RSSMediaThumbnail *mediaThumbnail = [item.mediaThumbnails firstObject];
     
      // mediaThumbnails are generally ordered by size,
      // so get the second mediaThumbnail, which is a
      // "medium" sized image
     
      if (item.mediaThumbnails.count >= 2) {
        mediaThumbnail = item.mediaThumbnails[1];
      } else {
        mediaThumbnail = [item.mediaThumbnails firstObject];
      }
     
      [cell.customImageView setImage:nil];
      [cell.customImageView setImageWithURL:mediaThumbnail.url];
    }

    Sweet! Because you kept your methods concise, you could reuse a lot of them.

    Most of the above is similar to how you created and configured RWBasicCell earlier, but here’s a quick walkthrough of the new code:

    1. hasImageAtIndexPath checks if the item at the indexPath has a mediaThumbnail with a non-nil URL–Deviant Art generates thumbnails automatically for all uploaded deviations.
    • If so, it has an image you’ll need to display using an RWImageCell.
  • configureImageCell:atIndexPath: is similar to the configure method for a basic cell, but it also sets an image via setImageForCell:item:.
  • setImageForCell:item: attempts to get the second media thumbnail, which in general is a “medium” sized image and works wells for the given image view size.
    • The image is then set on customImageView by using a convenience method provided by AFNetworking, setImageWithURL:.

    Next, you need to update the delegate method for calculating the cell height.

    Replace tableView:heightForRowAtIndexPath: with the following:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
      if ([self hasImageAtIndexPath:indexPath]) {
        return [self heightForImageCellAtIndexPath:indexPath];
      } else {
        return [self heightForBasicCellAtIndexPath:indexPath];
      }
    }
     
    - (CGFloat)heightForImageCellAtIndexPath:(NSIndexPath *)indexPath {
      static RWImageCell *sizingCell = nil;
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
        sizingCell = [self.tableView dequeueReusableCellWithIdentifier:RWImageCellIdentifier];
      });
     
      [self configureImageCell:sizingCell atIndexPath:indexPath];
      return [self calculateHeightForConfiguredSizingCell:sizingCell];
    }

    This is very similar to what you already did with RWBasicCell; you’re just reusing methods like a boss. ‘Nuff said!

    Build and run, and some pretty sweet art will greet you! By default, the app retrieves items from the “popular” category on Deviant Art, but you can search by artist too.

    Try searching for the artist who goes by CheshireCatArt (case insensitive). This is a friend of mine, Devin Kraft, who’s an excellent graphic artist (check out his website).

    He’s very active on Deviant Art and posts a good mix of deviations and blog posts. So, bias aside, his account is a good test case to show the app displays both cells with and without images.

    CheshireCatArt Posts

    Sweet!

    If you select a cell, the app will push a new RWDetailViewController onto the navigation controller stack. You may have noticed, however, that when you rotate to landscape orientation, the label’s height is incorrect.

    Hmmmm….this seems familiar. Is it deja vu?

    Kind of. This is the same issue RWBasicCell had, caused by UILabel‘s preferredMaxLayoutWidth property not being updated on device rotation.

    To fix this issue, open Main.storyboard. In each of the scenes for RWDetailViewController, select the label and change its Class to RWLabel.

    Awesome, the app is looking pretty sharp, but you can still improve on it to truly stun and amaze your client.

    Optimizing the Table View

    While you can use auto layout to calculate cell height starting in iOS 6, it’s most beneficial in iOS 7 because of a new delegate method:

    - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;

    If you don’t implement this method, the table view proactively calls tableView:heightForRowAtIndexPath: for every cell–even the cells that haven’t been displayed yet and may never be displayed.

    If your table view has a lot of cells, this can be very expensive and result in poor initial loading, reloading or scrolling performance.

    If you do implement this method, the table view will call it proactively.

    Pro tip: You can greatly improve the performance of a table view that has many cells by implementing tableView:estimatedHeightForRowAtIndexPath:.

    However, be warned that this delegate method comes with its own set of caveats.

    If your cell estimates are inaccurate, then scrolling might be jumpy, the scroll indicator may be misleading, or the content offset may get messed up. For example, if the user rotates the device or taps the status bar to scroll to the top, the result could be a gap at the top/bottom of the table view.

    If your table view has only a few cells, or if your cell heights are difficult to estimate in advance, you can opt to skip implementation of this method. However, you won’t have to worry about these issues.

    If you notice poor loading, reloading, or scrolling, try implementing this method.

    The key to success is finding a good balance between accurately estimating cell height and the performance cost of such calculation.

    Ideally, you want to keep this method simple. Start with the simplest case: return a constant value. Add the following to RWFeedViewController.m:

    - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
      return 100.0f;
    }

    Build and run, and try scrolling the table view all the way to the bottom. You may notice the scrolling is jumpy, especially as you get towards the middle/end of the table view.

    You might also notice that the scroll indicator isn’t very accurate. It seems to indicate there are fewer items are actually in the table view.

    Now try rotating the simulator. You’ll likely see that the content offset is messed up e.g. white space at the top/bottom of the table view.

    Inaccurate Cell Height Estimate

    These are all symptoms of poor cell height estimation. Through better estimates, you greatly alleviate or even eliminate these issues.

    Replace tableView:estimatedHeightForRowAtIndexPath: with the following:

    - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
      if ([self isLandscapeOrientation]) {
        if ([self hasImageAtIndexPath:indexPath]) {
          return 140.0f;
        } else {
          return 120.0f;
        }
      } else {
        if ([self hasImageAtIndexPath:indexPath]) {
          return 235.0f;
        } else {
          return 155.0f;
        }
      }
    }
     
    - (BOOL)isLandscapeOrientation {
      return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
    }

    You’re still using magic constants such as 140 and 235 to estimate height, but now you’re picking the best estimate based on the cell contents and the device orientation. You could get even fancier with your estimation but remember: the point of this method is to be fast. You want to return an estimate as quickly as possible.

    Build and run. And try scrolling through the list and rotating the device. This new estimation method adds a tiny bit of overhead, but improves the performance. Scrolling is smooth, the scroll indicator is accurate and content offset issues are very infrequent.

    It’s hard to eliminate the content offset issue altogether because it’s directly caused by the table view not knowing how large its content area should be. But even with this current implementation, it’s unlikely users would ever, or at least infrequently, experience this issue.

    Where to Go From Here

    You can download the completed project from here.

    Table views are perhaps the most fundamental of structured data views in iOS. As apps become more complex, table views are being used to show all kinds of content in all kinds of custom layouts. Getting your table view cells properly formatted with different kinds and amounts of content is now much easier with auto layout, and the performance can be much better with proper row height estimation.

    Auto layout is becoming more important as we get more device sizes and interface builder becomes more powerful. And there’s less code too! There should be plenty of ways to apply the concepts in this tutorial to your own apps to make them more responsive, faster, and better looking.

    If you have any comments or questions, please respond below! I’d love to hear from you.

    Dynamic Table View Cell Height and Auto Layout is a post from: Ray Wenderlich

    The post Dynamic Table View Cell Height and Auto Layout appeared first on Ray Wenderlich.

  • Facebook Pop 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.

    Today in our June Tech Talk, Ryan Nystrom (raywenderlich.com Tech Editor), Kimon Tsinteris (lead Facebook Pop developer), Brian Amerige (lead Facebook Paper developer), and Grant Paul (Shimmer/Tweaks maintainer and Facebook Pop contributor) gave an excellent talk and Q&A on the Facebook Pop Animation library.

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

    Helpful Links

    Here are some handy links to learn more about Facebook Pop:

    Want to Join Us Next Month?

    Thanks again Ryan, Kimon, Brian, and Grant for giving a great talk and Q&A, having the guts to present to a live audience :] And thank you to everyone who attended – we hope you enjoyed it!

    Stay tuned for next month’s tech talk – we’ll put a signup link on the sidebar once it’s announced. Hope to see some of you there! :]

    Facebook Pop Tech Talk Video is a post from: Ray Wenderlich

    The post Facebook Pop Tech Talk Video appeared first on Ray Wenderlich.

    Video Tutorial: Introduction to Swift Part 6: Functions

    Introducing the raywenderlich.com Swift Style Guide

    $
    0
    0
    Check out our official Swift style guide!

    Check out our official Swift style guide!

    Ever since WWDC, we’ve been working very hard to create a ton of new Swift tutorials, video tutorials, and books.

    But we realized pretty quickly – we needed a Swift style guide to keep our code organized and consistent!

    So today, the team and I are very pleased to announce the first draft of the official raywenderlich.com Swift style guide!

    Keep in mind Swift is still in beta and we’re still figuring a lot of things out, so consider this only the start. We’d appreciate your feedback and advice on where to take this next – feel free to submit issues or pull requests!

    Note that this style guide is different than other style guides out there, in that its focus is on readability for printed books and the web. Many of the decisions were made with an eye toward conserving space for print, easy legibility, and tutorial writing.

    So without further ado, check out the style guide or keep reading for the highlights of the guide, who worked on it, and how you can contribute!

    Guide Highlights

    Here are some of the highlights of the guide:

    • Avoid unnecessary code. In general, avoid using unnecessary (but optional) code in Swift. For example, don’t use self unless there’s a reason, avoid semicolons, prefer inferred typing.
    • Closure syntax. Use trailing closure syntax wherever possible, and prefer to use the closure parameters descriptive names.
    • Constants. Prefer to make your values constants with let – make variable with var only when necessary.
    • The Smiley Face. There is only one true smiley face :]

    To learn more, check out the full Swift style guide.

    Note we still have some unanswered questions and points of debate in the issues – we’d love to hear your thoughts!

    How Was This Guide Developed?

    This style guide was developed as a group effort from across the team.

    Greg Heo served as the project manager for the style guide, and Colin Eberhardt (one of the co-authors of Swift by Tutorials) was an especially active committer.

    In addition to those two, the style guide team members included Soheil Moayedi Azarpour, Scott Berrevoets, Eric Cerney, Sam Davies, Evan Dekhayser, Jean-Pierre Distler, Matthijs Hollemans, Erik Kerber, Christopher LaPollo, Andy Pereira, Ryan Nystrom, Cesare Rocchi, Ellen Shapiro, Marin Todorov, Chris Wagner, Ray Wenderlich, and Jack Wu.

    How We’ll Use This Guide

    We will strongly recommend tutorial authors on our site use this style guide moving forward. However, note that not every tutorial will be 100% compliant to the style guide – it’s asking a bit much for authors and editors to do that for free tutorials.

    However, we will enforce the style guide rules in books and starter kits, as we always aim for a higher bar with those.

    Comments on the Guide?

    As we learned from developing this guide, there are always going to be strong opinions on code style issues.

    And we’d love to hear yours! Do you love/hate any decisions we’ve made in the style guide? Post comments on this thread or file GitHub pull requests – I’ll be watching for them, and if you can convince me (and the rest of the team), we’ll change the guide.

    We hope you enjoy our Swift style guide – and stay tuned for some more consistent tutorials and books! :]

    Introducing the raywenderlich.com Swift Style Guide is a post from: Ray Wenderlich

    The post Introducing the raywenderlich.com Swift Style Guide appeared first on Ray Wenderlich.

    MVVM Tutorial with ReactiveCocoa: Part 1/2

    $
    0
    0

    You might have seen this joke on Twitter a while back:

    “iOS Architecture, where MVC stands for Massive View Controller” via Colin Campbell

    This is a light-hearted ‘jab’ at iOS developers, but I am sure you have all seen this problem in practice; the overweight and unmanageable view controller.

    This MVVM tutorial takes a look at a different pattern for structuring applications, Model-View-ViewModel, or MVVM. This pattern, facilitated by ReactiveCocoa, provides an excellent alternative to MVC, and guarantees sleek and lightweight view controllers!

    Throughout the course of this MVVM tutorial, you’re going to be building a simple Flickr search application, shown below:

    FinishedApp

    Note: This tutorial is written in Objective-C, not Swift. It also targets Xcode 5, not Xcode 6, and all screenshots are made with Xcode 5. For best results, use Xcode 5 when going through this tutorial.

    Before you start writing code, it’s time for a bit of theory!

    A Brief Recap of ReactiveCocoa

    This tutorial is primarily about MVVM, and assumes you have a working knowledge of ReactiveCocoa. If you haven’t used ReactiveCocoa before, I strongly advise following my earlier tutorial series that will introduce you to the topic.

    If you’re in need of a quick refresher of ReactiveCocoa, I’ll briefly recap the salient points.

    At the very core of ReactiveCocoa are signals, represented by the RACSignal class. Signals emit a stream of events, which are all one of three types, next, completed and error.

    Using this simple model, ReactiveCocoa can serve as a replacement for the delegate pattern, target-action pattern, key-value observing, and more.

    The signal API creates code that is more homogenous, and hence easier to read, but the real power of ReactiveCocoa is the higher-level operations you can apply to these signals. These operations allow you to perform complex filtering, transformation and signal coordination in a highly concise fashion.

    Within the context of MVVM, ReactiveCocoa performs a very specific role. It provides the ‘glue’ that binds the ViewModel to the View. But you’re getting a little ahead of yourself now…

    An Introduction to the MVVM pattern

    The Model-View-ViewModel, or MVVM pattern as it’s commonly known, is a UI design pattern. It’s a member of a larger family of patterns collectively known as MV*, these include Model View Controller (MVC), Model View Presenter (MVP) and a number of others.

    Each of these patterns is concerned with separating UI logic from business logic in order to make applications easier to develop and test.

    Note: For a detailed look at design patterns in general, I recommend Eli’s or Ash Furrow’s articles.

    To understand the MVVM pattern better, it helps to look back at its origins.

    MVC was the first UI design pattern, and its origins track back to the Smalltalk language of the 1970s. The image below illustrates the main components of the MVC pattern:

    MVCPattern-2

    This pattern separates the UI into the Model that represents the application state, the View, which in turn is composed of UI controls, and a Controller that handles user interactions and updates the model accordingly.

    One of the big problems with the MVC pattern is that it’s quite confusing. The concepts look good, but often when people come to implement MVC, the seemingly circular relationships illustrated above result in the Model, View and Controller. In turn, they conflate into a big, horrible mess.

    More recently Martin Fowler introduced a variation of the MVC pattern termed the Presentation Model, which was adopted and popularized by Microsoft under the name MVVM.

    MVVMPattern

    At the core of this pattern is the ViewModel, which is a special type of model that represents the UI state of the application.

    It contains properties that detail the state of each and every UI control. For example, the current text for a text field, or whether a specific button is enabled. It also exposes the actions the view can perform, like button taps or gestures.

    It can help to think of the ViewModel as the model-of-the-view.

    The relationships between the three components of the MVVM pattern are simpler than the MVC equivalents, following these strict rules:

    1. The View has a reference to the ViewModel, but not vice-versa.
    2. The ViewModel has a reference to the Model, but not vice-versa.

    If you break either of these rules, you’re doing MVVM wrong!

    A couple of immediate advantages of this pattern are as follows:

    1. Lightweight Views – All your UI logic resides within the ViewModel, resulting in a very lightweight view.
    2. Testing – you should be able to run your entire application without the View, greatly enhancing its testability.
    Note: Testing views is notoriously difficult because tests run as small, contained chunks of code. Usually, controllers add and configure views to the scene that rely on other application state. This means that meaning running small tests can become a fragile and cumbersome proposition.

    At this point, you might have spotted a problem. If the View has a reference to the ViewModel but not vice-versa, how does the ViewModel update the View?

    Ah-ha!!! This is where the secret-sauce of the MVVM pattern comes in.

    MVVM and Data Binding

    The MVVM pattern relies on data-binding, a framework level feature that automatically connects object properties to UI controls.

    As an example, within Microsoft’s WPF framework, the following markup binds the Text property of a TextField to the Username property on the ViewModel:

    <TextField Text=”{DataBinding Path=Username, Mode=TwoWay}/>

    The WPF framework ‘binds’ these two properties together.

    The TwoWay binding ensures that changes to the ViewModel’s Username property propagate to the Text property of the TextField, and vice-versa, i.e. user-input is reflected in the ViewModel.

    As another example, with the popular web-based MVVM framework Knockout, you can see the same the same binding feature in action:

    <input data-bind=”value: username”/>

    The above binds a property of an HTML element with a JavaScript model.

    Unfortunately, iOS lacks a data-binding framework, but this is where ReactiveCocoa acts as the ‘glue’ that connects the ViewModel together.

    Looking at the MVVM pattern specifically from the perspective of iOS development, the ViewController and its associated UI — whether that is a nib, storyboard or constructed though code — composes the View:

    MVVMReactiveCocoa

    …with ReactiveCocoa binding the two together.

    Note: For a more detailed historical analysis of UI patterns I would highly recommend Martin Fowler’s GUI Architectures article.

    Have you had enough theory? Well, if not feel free to go back and read it again. Oh, you’re good? Okay, then it’s time to have a go at creating your own ViewModels.

    Starter Project Structure

    First download the following starter project:

    It uses CocoaPods to manage dependencies (if you’re new to CocoaPods, we have a tutorial for you!). Run pod install to obtain the dependencies, verifying that you see the following output:

    $ pod install
    Analyzing dependencies
    Downloading dependencies
    Installing LinqToObjectiveC (2.0.0)
    Installing ReactiveCocoa (2.1.8)
    Installing SDWebImage (3.6)
    Installing objectiveflickr (2.0.4)
    Generating Pods project
    Integrating client project

    You’ll learn more about each dependency as it’s put to use.

    The starter project for this tutorial already contains the views for your application, implemented using view controllers and nib files. Open up the RWTFlickrSearch.xcworkspace generated by CocoaPods, build and run the starter project and you’ll see one of these views:

    BlankView

    Take a little time to familiarize yourself with the project structure:

    EmptyInterface

    The Model and ViewModel groups are empty; you’ll be adding files to these shortly. The View group contains the following:

    • RWTFlickSearchViewController: The main screen of the application, which contains a search text field and the ‘Go’ button.
    • RWTRecentSearchItemTableViewCell: A table cell that renders recent search results within the main screen.
    • RWTSearchResultsViewController: The search results screen that shows a table of images from Flickr.
    • RWTSearchResultsTableViewCell: A table cell that renders individual images from Flickr.

    Time to add your first view model!

    Your First ViewModel

    Add a new class to the ViewModel group, naming it RWTFlickrSearchViewModel and making it a subclass of NSObject.

    Open the newly added header file, and add a couple of properties:

    @interface RWTFlickrSearchViewModel : NSObject
     
    @property (strong, nonatomic) NSString *searchText;
    @property (strong, nonatomic) NSString *title;
     
    @end

    The searchText property represents the text displayed in the text field, and the title property represents the title displayed in the navigation bar.

    Note: In order to make it easier to understand the application structure, the Views and ViewModels share a common name with a different suffix. For example RWTFlickrSearch-ViewModel and RWTFlickrSearch-ViewController.

    Open RWTFlickrSearchViewModel.m and add the following:

    @implementation RWTFlickrSearchViewModel
     
    - (instancetype)init {
      self = [super init];
      if (self) {
        [self initialize];
      }
      return self;
    }
     
    - (void)initialize {
      self.searchText = @"search text";
      self.title = @"Flickr Search";
    }
     
    @end

    This simply sets the initial state of this ViewModel.

    The next step is to connect the ViewModel to the View. Remember that the View holds a reference to the ViewModel. In this case, it makes sense to add an initializer that constructs the View, given the respective ViewModel.

    Note: We’re referring to our controllers as “Views” in this tutorial to be more semantic with the “View” in MVVM. This differs from the default naming that UIKit uses.

    Open RWTFlickrSearchViewController.h and import the ViewModel header:

    #import "RWTFlickrSearchViewModel.h"

    Then add the following initializer:

    @interface RWTFlickrSearchViewController : UIViewController
     
    - (instancetype)initWithViewModel:(RWTFlickrSearchViewModel *)viewModel;
     
    @end

    Within RWTFlickrSearchViewController.m, add the following private property to the same class extension that holds the UI outlets:

    @property (weak, nonatomic) RWTFlickrSearchViewModel *viewModel;

    Then add an init method as follows:

    - (instancetype)initWithViewModel:(RWTFlickrSearchViewModel *)viewModel {
      self = [super init];
      if (self ) {
        _viewModel = viewModel;
      }
      return self;
    }

    This stores a reference to the ViewModel that backs this View

    Note: This is a weak reference; the View references the ViewModel, but doesn’t own it.

    Add the following to the end of viewDidLoad:

    [self bindViewModel];

    Then implement this method as follows:

    - (void)bindViewModel {
      self.title = self.viewModel.title;
      self.searchTextField.text = self.viewModel.searchText;
    }

    The above code executes when the UI initializes and applies the ViewModel state to the View.

    The final step is to create an instance of the ViewModel, and then supply it the View.

    Within RWTAppDelegate.m, add the following import:

    #import "RWTFlickrSearchViewModel.h"

    And a private property (within the class extension at the top of the file):

    @property (strong, nonatomic) RWTFlickrSearchViewModel *viewModel;

    You’ll find that this class already has a createInitialViewController method, update its implementation as follows:

    - (UIViewController *)createInitialViewController {
      self.viewModel = [RWTFlickrSearchViewModel new];
      return [[RWTFlickrSearchViewController alloc] initWithViewModel:self.viewModel];
    }

    This creates a new instance of the ViewModel, and then it constructs and returns the View. This serves as the initial view for the application’s navigation controller.

    Build and run to see that the View now has some state!

    ViewWithState

    Congratulations, this is your first ViewModel. I’m going to have to ask you to contain your excitement! There’s still much to learn here ;]

    You might have noticed you haven’t used any ReactiveCocoa yet. In its present form, any text the user enters into the search text field will not reflect in the ViewModel.

    Detecting Valid Search State

    In this section, you’re going to use ReactiveCocoa to bind the ViewModel and View together in order to connect both the search text field and button to the ViewModel.

    Within RWTFlickrSearchViewController.m, update bindViewModel as follows:

    - (void)bindViewModel {
      self.title = self.viewModel.title;
      RAC(self.viewModel, searchText) = self.searchTextField.rac_textSignal;
    }

    You’re adding the rac_textSignal property to the UITextField class by using a category within ReactiveCocoa. It’s a signal that emits a next event containing the current text each time the text field updates.

    The RAC macro is a binding; the above code updates the searchText property of the viewModel with the contents of each next event emitted by the rac_textSignal.

    In short, it ensures that the searchText property always reflects the current UI state. If the above sounds like utter gibberish, you really need to re-visit the first two ReactiveCocoa tutorials!

    The search button should only be enabled if the text the user has entered is valid. We’ll keep things simple and enforce the rule that they must enter more than three characters before they can execute a search.

    Within RWTFlickrSearchViewModel.m add the following import:

    #import <ReactiveCocoa/ReactiveCocoa.h>

    And update the initialize method as follows:

    - (void)initialize {
      self.title = @"Flickr Search";
     
      RACSignal *validSearchSignal =
        [[RACObserve(self, searchText)
          map:^id(NSString *text) {
             return @(text.length > 3);
          }]
          distinctUntilChanged];
     
      [validSearchSignal subscribeNext:^(id x) {
        NSLog(@"search text is valid %@", x);
      }];
    }

    Build, run and have a go at entering some text into the text field. You’ll now see a log message each time the text transitions between a valid or invalid state:

    2014-05-27 18:03:26.299 RWTFlickrSearch[13392:70b] search text is valid 0
    2014-05-27 18:03:28.379 RWTFlickrSearch[13392:70b] search text is valid 1
    2014-05-27 18:03:29.811 RWTFlickrSearch[13392:70b] search text is valid 0

    The above code uses the RACObserve macro to create a signal from the ViewModel’s searchText property (this is a ReactiveCocoa wrapper over KVO). A map operation converts the text into a stream of true and false values.

    Finally, distinctUntilChanges is used to ensure this signal only emits values when the state changes.

    Note: If you’re having difficulty following, try breaking it down into smaller pieces. First add a RACObserve, logging the output, then add the map followed by the distinctUntilChanged operation as separate steps.

    What you have seen so far is that ReactiveCocoa is used to bind the View to the ViewModel, ensuring that the two remain synchronized. Furthermore, ReactiveCocoa is used internally within the ViewModel to observe its own state and perform other operations.

    This is a pattern you’ll see emerging throughout this MVVM tutorial. ReactiveCocoa is vital for binding the View and ViewModel together, but it’s also very useful within the other layers of your applications.

    Adding a search command

    In this section you’ll do something more useful with the validSearchSignal: use it to create a command that is bound to the View.

    Open RWTFlickrSearchViewModel.h and add the following import:

    #import <ReactiveCocoa/ReactiveCocoa.h>

    And the following property:

    @property (strong, nonatomic) RACCommand *executeSearch;

    RACCommand is a ReactiveCocoa concept that represents a UI action. It comprises a signal, which is the result of the UI action, and the current state, which indicates whether the action is currently being executed.

    Within RWTFlickrSearchViewModel.m add the following to the end of the initialize method:

    self.executeSearch =
      [[RACCommand alloc] initWithEnabled:validSearchSignal
        signalBlock:^RACSignal *(id input) {
          return  [self executeSearchSignal];
        }];

    This creates a command that is enabled when the validSearchSignal emits true.

    Further down the same file, add the following method that provides the execution signal for the command:

    - (RACSignal *)executeSearchSignal {
      return [[[[RACSignal empty]
               logAll]
               delay:2.0]
               logAll];
    }

    Within this method, you’ll perform some business logic as a result of the command executing, and will return the result asynchronously via the signal.

    For now, the above is merely a dummy implementation; the empty signal completes immediately. The delay operation adds a two-second delay to any next and completed events it receives. This is a very cunning way of making this code more realistic!

    Note: The above signal pipeline has a couple of additional logAll operations, and these are side effects that log all events that pass through them.

    While they are rather helpful for debugging ReactiveCocoa code, feel free to remove these logging operations if you don’t find them to be necessary.

    The final step is to wire this command to the View. Open RWTFlickrSearchViewController.m and add the following to the end of the bindViewModel method:

    self.searchButton.rac_command = self.viewModel.executeSearch;

    The rac_command property is a ReactiveCocoa addition to UIButton. The above code ensures that button taps result in the given command executing, and that the enabled state of the button reflects the enabled state of the command.

    Build and run, enter some text and hit Go:

    GoButtonEnabled

    You’ll see that the button is only enabled when there are more than three characters in the text field. Furthermore, when you tap the button it’s disabled for two seconds, and becomes re-enabled when the execution signal completes.

    From the console, you can also see the empty signal completing immediately, with the delay operation emitting this event two seconds later:

    09:31:25.728 RWTFlickrSearch ... name: +empty completed
    09:31:27.730 RWTFlickrSearch ... name: [+empty] -delay: 2.0 completed

    How cool is that?

    Bindings, Bindings and More Bindings

    The RACCommand takes care of updating the state of the search button, but it’s on you to handle the visibility of the activity indicator.

    RACCommand exposes an executing property, and that’s a signal that emits true and false events to indicate when the command starts and ends execution. You can use these to reflect the current command state elsewhere in your application.

    Within RWTFlickrSearchViewController.m, add the following to the end of bindViewModel:

    RAC([UIApplication sharedApplication], networkActivityIndicatorVisible) =
      self.viewModel.executeSearch.executing;

    This binds the networkActivityIndicatorVisible property of UIApplication to the command’s executing signal. This ensures that whenever the command executes, the small network activity indicator in the status bar will show itself.

    Next, add the following:

    RAC(self.loadingIndicator, hidden) =
      [self.viewModel.executeSearch.executing not];

    When the command executes, the loading indicator should be hidden; this is the opposite of the property you just bound.

    Fortunately, ReactiveCocoa has this covered with a not operation that can invert signals.

    Finally, add the following:

    [self.viewModel.executeSearch.executionSignals
      subscribeNext:^(id x) {
        [self.searchTextField resignFirstResponder];
      }];

    The above ensures that the keyboard hides whenever the command executes. The executionSignals property emits the signals that generate each time the command executes.

    This property is a signal of signals (as introduced in the previous tutorial). Whenever a new command execution signal is created and emitted, the keyboard is hidden.

    Build and run the application to see the above code in action.

    Where’s the Model?

    At this point you have a clearly identifiable View (RWTFlickrSearchViewController) and ViewModel (RWTFlickrSearchViewModel), but, ummm, where is the Model?

    The answer is simple: there isn’t one!

    The current app executes a command in response to the user tapping the search button, but the implementation does nothing ‘ of value.

    What needs to happen is for the ViewModel to search Flickr using the current searchText, and return a list of matching pictures.

    You could add this logic directly into the ViewModel, but trust me, you would regret it! If this was a view controller, I would be willing to bet you would do exactly that.

    The ViewModel exposes properties that represent the UI state, it also exposes commands — and often methods — that represent UI actions. It’s responsible for managing changes to the UI state based on user interactions.

    However, it’s not responsible for the actual business logic that executes because of these interactions. That is the job of the Model.

    In this next step, you’re going to add a Model layer to the application. There’s a fair bit of ‘scaffolding code’ required, so just work through and you’ll get to something more interesting shortly.

    Within the Model group, add a new protocol named RWTFlickrSearch and provide the following implementation:

    #import <ReactiveCocoa/ReactiveCocoa.h>
    @import Foundation;
     
    @protocol RWTFlickrSearch <NSObject>
     
    - (RACSignal *)flickrSearchSignal:(NSString *)searchString;
     
    @end

    This protocol defines the initial interface for the Model layer, and moves the responsibility of searching Flickr out of the ViewModel.

    Next add a new NSObject subclass, RWTFlickrSearchImpl to the same group, and then update the interface to adopt this newly added protocol:

    @import Foundation;
    #import "RWTFlickrSearch.h"
     
    @interface RWTFlickrSearchImpl : NSObject <RWTFlickrSearch>
     
    @end

    Open RWTFlickrSearchImpl.m and provide the following implementation:

    @implementation RWTFlickrSearchImpl
     
    - (RACSignal *)flickrSearchSignal:(NSString *)searchString {
      return [[[[RACSignal empty]
                logAll]
                delay:2.0]
                logAll];
    }
     
    @end

    Are you feeling like this might be dejavu? If so, that’s because this is the same ‘dummy’ implementation that was located within the ViewModel previously.

    The next step is to make use of the Model layer from within the ViewModel. In the ViewModel group add a new protocol named RWTViewModelServices and update it as follows:

    @import Foundation;
    #import "RWTFlickrSearch.h"
     
    @protocol RWTViewModelServices <NSObject>
     
    - (id<RWTFlickrSearch>) getFlickrSearchService;
     
    @end

    This protocol defines a single method that allows the ViewModel to obtain a reference to an implementation of the RWTFlickrSearch protocol.

    Open RWTFlickrSearchViewModel.h and import this new protocol:

    #import "RWTViewModelServices.h"

    And update the initializer to take this as an argument:

    - (instancetype) initWithServices:(id<RWTViewModelServices>)services;

    Within RWTFlickrSearchViewModel.m, add a class extension and a private property to hold a reference to the view model services:

    @interface RWTFlickrSearchViewModel ()
     
    @property (nonatomic, weak) id<RWTViewModelServices> services;
     
    @end

    Further down the same file, update the initializer:

    - (instancetype) initWithServices:(id<RWTViewModelServices>)services {
      self = [super init];
      if (self) {
        _services = services;
        [self initialize];
      }
      return self;
    }

    This simply stores a reference to the services.

    Finally, update the executeSearchSignal method as follows.

    - (RACSignal *)executeSearchSignal {
      return [[self.services getFlickrSearchService]
               flickrSearchSignal:self.searchText];
    }

    The above method now delegates to the model to perform the search.

    The final step is to connect the Model and ViewModel.

    Within the ‘root’ group of the project RWTFlickrSearch, add a new NSObject subclass named RWTViewModelServicesImpl. Open RWTViewModelServicesImpl.h and adopt the RWTViewModelServices protocol:

    @import Foundation;
    #import "RWTViewModelServices.h"
     
    @interface RWTViewModelServicesImpl : NSObject <RWTViewModelServices>
     
    @end

    Open RWTViewModelServicesImpl.m and implement as follows:

    #import "RWTViewModelServicesImpl.h"
    #import "RWTFlickrSearchImpl.h"
     
    @interface RWTViewModelServicesImpl ()
     
    @property (strong, nonatomic) RWTFlickrSearchImpl *searchService;
     
    @end
     
    @implementation RWTViewModelServicesImpl
     
    - (instancetype)init {
      if (self = [super init]) {
        _searchService = [RWTFlickrSearchImpl new];
      }
      return self;
    }
     
    - (id<RWTFlickrSearch>)getFlickrSearchService {
      return self.searchService;
    }
     
    @end

    This class simply creates an instance of RWTFlickrSearchImpl, the Model layer service for searching Flickr, and provides it to the ViewModel upon request.

    Finally, open RWTAppDelegate.m and add the following import:

    #import "RWTViewModelServicesImpl.h"

    And a new private property:

    @property (strong, nonatomic) RWTViewModelServicesImpl *viewModelServices;

    And update the createInitialViewController method as follows:

    - (UIViewController *)createInitialViewController {
      self.viewModelServices = [RWTViewModelServicesImpl new];
      self.viewModel = [[RWTFlickrSearchViewModel alloc]
                        initWithServices:self.viewModelServices];
      return [[RWTFlickrSearchViewController alloc]
              initWithViewModel:self.viewModel];
    }

    Build and run, and verify the application works in exactly the same way it did previously!

    No, this was not the most exciting change to make, but take a moment to look at the ‘shape’ of this new code.

    The Model layer exposes a ‘service’ that the ViewModel consumes. A protocol defines this service interface, providing loose coupling.

    You could use this approach to provide a dummy service implementation for unit tests. The application now has the correct Model-View-ViewModel structure. To briefly recap:

    1. The Model layer exposes services and is responsible for providing business logic for the application. In this case, it provides a service to search Flickr.
    2. The ViewModel layer represents the view-state of the application. It also responds to user interactions and ‘events’ that come from the Model layer, each of which are reflected by changes in view-state.
    3. The View layer is very thin and simply provides a visualisation of the ViewModel state and forwards user interactions.

    Note: In this application the Model layer exposes its services using ReactiveCocoa signals. This framework is useful for far more than just bindings!

    Searching Flickr

    In this section you’re going to provide a real Flickr search implementation, yes, things are about to become exciting ;]

    The first step is to create the model objects that represent the search results.

    Within the Model group, add a new NSObject subclass called RWTFlickrPhoto, adding three properties to the interface as follows:

    @interface RWTFlickrPhoto : NSObject
     
    @property (strong, nonatomic) NSString *title;
    @property (strong, nonatomic) NSURL *url;
    @property (strong, nonatomic) NSString *identifier;
     
    @end

    This is a model object that represents a single photo returned by Flickr’s search APIs.

    Open RWTFlickrPhoto.m and add an implementation for the describe method:

    - (NSString *)description {
      return self.title;
    }

    This allows you to test the search implementation by logging the results before you move on to the required UI changes.

    Next, add another model object RWTFlickrSearchResults that is also an NSObject subclass, adding the following properties to the interface:

    @import Foundation;
     
    @interface RWTFlickrSearchResults : NSObject
     
    @property (strong, nonatomic) NSString *searchString;
    @property (strong, nonatomic) NSArray *photos;
    @property (nonatomic) NSUInteger totalResults;
     
    @end

    This represents a collection of photos as returned by a Flickr search.

    Open RWTFlickrSearchResults.m and add the following implementation for the description method (again for logging purposes):

    - (NSString *)description {
      return [NSString stringWithFormat:@"searchString=%@, totalresults=%lU, photos=%@",
              self.searchString, self.totalResults, self.photos];
    }

    It’s time to write the code that searches Flickr!

    Open RWTFlickrSearchImpl.m and add the following imports:

    #import "RWTFlickrSearchResults.h"
    #import "RWTFlickrPhoto.h"
    #import <objectiveflickr/ObjectiveFlickr.h>
    #import <LinqToObjectiveC/NSArray+LinqExtensions.h>

    This imports the model objects you just created, plus a couple of external dependencies that CocoaPods loaded:

    • ObjectiveFlickr – this is an Objective-C API that wraps the Flickr API. It handles authentication and parses the API responses. It’s much easier to use this library than Flickr’s APIs directly.
    • LinqToObjectiveC – this library provides a fluent and functional interface for querying, filtering and transforming arrays and dictionaries.

    Still within RWTFlickrSearchImpl.m add a class extension as follows:

    @interface RWTFlickrSearchImpl () <OFFlickrAPIRequestDelegate>
     
    @property (strong, nonatomic) NSMutableSet *requests;
    @property (strong, nonatomic) OFFlickrAPIContext *flickrContext;
     
    @end

    This adopts the OFFlickrAPIRequestDelegate protocol, from the ObjectiveFlickr library and adds a couple of private properties. You’ll see how these are used very soon.

    Further down the same file add the following initializer:

    - (instancetype)init {
      self = [super init];
      if (self) {
        NSString *OFSampleAppAPIKey = @"YOUR_API_KEY_GOES_HERE";
        NSString *OFSampleAppAPISharedSecret = @"YOUR_SECRET_GOES_HERE";
     
        _flickrContext = [[OFFlickrAPIContext alloc] initWithAPIKey:OFSampleAppAPIKey
                                                      sharedSecret:OFSampleAppAPISharedSecret];
        _requests = [NSMutableSet new];
      }
      return  self;
    }

    This creates a Flickr ‘context’ to store the data ObjectiveFlickr requires to generate API requests.

    Note: To use ObjectiveFlickr you’ll need to create a Flickr App Key via the Flickr App Garden. It’s free and only takes a few steps.

    Be sure to request a non-commercial key.

    The ObjectiveFlickr API is pretty typical. You create API requests and the resulting success or failure is reported via delegate methods, as defined by the OFFlickrAPIRequestDelegate protocol mentioned earlier.

    The current API exposed by your Model-layer service class, as defined by the RWTFlickrSearch protocol, has a single method that searches for photos based on a text search string.

    However, in the near future you’re going to be adding further methods.

    Because of this, you’re going to dive straight in with a generic approach for adapting this delegate-based API to use signals. Hang on tight!

    Still within RWTFlickrSearchImpl.m add the following method:

    - (RACSignal *)signalFromAPIMethod:(NSString *)method
                             arguments:(NSDictionary *)args
                             transform:(id (^)(NSDictionary *response))block {
     
      // 1. Create a signal for this request
      return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
     
        // 2. Create a Flick request object
        OFFlickrAPIRequest *flickrRequest =
          [[OFFlickrAPIRequest alloc] initWithAPIContext:self.flickrContext];
        flickrRequest.delegate = self;
        [self.requests addObject:flickrRequest];
     
        // 3. Create a signal from the delegate method
        RACSignal *successSignal =
          [self rac_signalForSelector:@selector(flickrAPIRequest:didCompleteWithResponse:)
                         fromProtocol:@protocol(OFFlickrAPIRequestDelegate)];
     
        // 4. Handle the response
        [[[successSignal
          map:^id(RACTuple *tuple) {
            return tuple.second;
          }]
          map:block]
          subscribeNext:^(id x) {
            [subscriber sendNext:x];
            [subscriber sendCompleted];
          }];
     
        // 5. Make the request
        [flickrRequest callAPIMethodWithGET:method
                                  arguments:args];
     
        // 6. When we are done, remove the reference to this request
        return [RACDisposable disposableWithBlock:^{
          [self.requests removeObject:flickrRequest];
        }];
      }];
    }

    This method takes an API request, as detailed by the method name and passed arguments, and then transforms the response using the given block argument. You’ll see how this works very soon.

    Regarding the method itself, there’s quite a lot going on here. Consider each step, in turn:

    1. The createSignal method creates a new signal. The subscriber that passes to the signal block exposes methods that allow you to send next, error and completed events to the signals’ subscribers.
    2. An ObjectiveFlickr request is constructed, and a reference to this request is stored in the requests set. Without this code, the OFFlickrAPIRequest would not be retained!
    3. The rac_signalForSelector:fromProtocol: method creates a signal from the delegate method that indicates the completion of the Flickr API request.
    4. The signal is subscribed to, and the result transforms and gets sent as the result of the signal that is being created (more on this later).
    5. The ObjectiveFlickr API request is invoked.
    6. When the signal is disposed, this block ensures that the reference to the Flickr request is removed, avoiding memory leaks.

    Now let’s have a look at the pipeline from step (4) in more detail:

    [[[successSignal
      // 1. Extract the second argument
      map:^id(RACTuple *tuple) {
        return tuple.second;
      }]
      // 2. transform the results
      map:block]
      subscribeNext:^(id x) {
        // 3. send the results to the subscribers
        [subscriber sendNext:x];
        [subscriber sendCompleted];
      }];

    rac_signalForSelector:fromProtocol: method creates the successSignal, and it also creates signals from delegate method invocations.

    Each time the delegate method is invoked, a next event emits with a RACTuple that contains the method arguments. The pipeline above performs the following steps:

    1. A map operation extracts the second argument from the flickrAPIRequest:didCompleteWithResponse: delegate method, the NSDictionary response.
    2. The block that passes as an argument to this method transforms the result. You’ll shortly see how this converts from the dictionary to the model object representation.
    3. Finally, the transformed response is sent as a next event, and the signal completes.

    Note: There is a subtle issue with this code, think about what might happen if there are concurrent requests. You’ll fix that in the second part of this MVVM tutorial, but if you’re feeling adventurous, why not try to tackle it now?

    The final step is to implement the Flickr search method as follows:

    - (RACSignal *)flickrSearchSignal:(NSString *)searchString {
      return [self signalFromAPIMethod:@"flickr.photos.search"
                             arguments:@{@"text": searchString,
                                         @"sort": @"interestingness-desc"}
                             transform:^id(NSDictionary *response) {
     
        RWTFlickrSearchResults *results = [RWTFlickrSearchResults new];
        results.searchString = searchString;
        results.totalResults = [[response valueForKeyPath:@"photos.total"] integerValue];
     
        NSArray *photos = [response valueForKeyPath:@"photos.photo"];
        results.photos = [photos linq_select:^id(NSDictionary *jsonPhoto) {
          RWTFlickrPhoto *photo = [RWTFlickrPhoto new];
          photo.title = [jsonPhoto objectForKey:@"title"];
          photo.identifier = [jsonPhoto objectForKey:@"id"];
          photo.url = [self.flickrContext photoSourceURLFromDictionary:jsonPhoto
                                                                  size:OFFlickrSmallSize];
          return photo;
        }];
     
        return results;
      }];
    }

    The above method uses the signalFromAPIMethod:arguments:transform: method that you added in the previous step. The flickr.photos.search API method searches for photos, with the search criteria supplied as a dictionary.

    The block passed to the transform argument simply converts the NSDictionary response into an equivalent model-object representation, making it easier to use within the ViewModel.

    This code makes use of the linq_select method that is added to NSArray via LinqToObjectiveC, providing a functional API for array transformation.

    Note: For more complex JSON-to-object mapping I’d recommend having a look at Mantle, although this particular model would require a Mantle 2.0 feature in order to map it correctly!

    The final step is to open RWTFlickrSearchViewModel.m, and then update the search signal to log its results:

    - (RACSignal *)executeSearchSignal {
      return [[[self.services getFlickrSearchService]
               flickrSearchSignal:self.searchText]
               logAll];
    }

    Build, run and enter a search string to see the result of this signal logging to the console:

    2014-06-03 [...] <RACDynamicSignal: 0x8c368a0> name: +createSignal: next: searchString=wibble, totalresults=1973, photos=(
        "Wibble, wobble, wibble, wobble",
        "unoa-army",
        "Day 277: Cheers to the freakin' weekend!",
        [...]
        "Angry sky",
        Nemesis
    )

    Note: If you don’t get results, double check your Flickr API key and shared secret.

    This brings part one of this MVVM tutorial to a close, but before finishing off there is a very important aspect of the current application code that you’ve not fully explored…

    Memory Management

    If you recall from the earlier tutorials, I mentioned you have to use the block-based ReactiveCocoa API with some caution in order to avoid creating retain-cycles, which ultimately led to memory leaks.

    If a block uses ‘self’, and ‘self’ has a strong reference to the block a retain cycle forms. In the previous tutorial you saw how to use the @weakify and @strongify macros in order to break these retain cycles.

    Are you wondering why the implementation of the signalFromAPIMethod:arguments:transform: doesn’t use these macros when referencing self?

    It’s because the block serves as an argument to the createSignal: method, which doesn’t form a strong reference between self and the block.

    Confused? Well, you don’t have to take my word for it, just test the code to find those memory leaks.

    Run the application using the Product / Profile option, select the Allocations profile. When the application starts, use the filter in the top right corner to search for classes that contain the text ‘OFF’, i.e. those from the ObjectiveFlickr framework.

    You’ll find that on start-up it creates a single instance of OFFlickrAPIContext . When you search, you’ll see that an instance of OFFlickrAPIRequest is created and exists for the lifetime of the query:

    ApplicationProfile

    And the good news is that the OFFlickrAPIRequest instance is de-allocated when the API request completes. Now you have conclusive proof that the block that initiates the Flickr request is not retained!

    I would encourage you to repeat this profiling process periodically to ensure that the heap only contains the objects you expect.

    Where To Go From Here?

    Here is an example project with the code from this tutorial so far.

    This concludes the first part of this MVVM Tutorial with ReactiveCocoa!

    In the next part, you’ll look at how to initiate a view controller transition from the ViewModel and implement further Flickr API requests in order to make this application more feature rich.

    In the meantime, if you have any questions or comments please join the forum discussion below!

    MVVM Tutorial with ReactiveCocoa: Part 1/2 is a post from: Ray Wenderlich

    The post MVVM Tutorial with ReactiveCocoa: Part 1/2 appeared first on Ray Wenderlich.

    MVVM Tutorial with ReactiveCocoa: Part 2/2

    $
    0
    0

    Model-View-ViewModel (MVVM) is a UI design pattern that is becoming a popular alternative to Model-View-Controller (MVC).

    In the first part of this MVVM tutorial series, you saw how ReactiveCocoa forms the ‘glue’ that binds ViewModels to their respective Views:

    MVVMReactiveCocoa

    Here’s the application you’re building, it’s a Flickr search app:

    FinishedApp

    In this second part and final part of this MVVM tutorial series, you’re going to look at how you can ‘drive’ the navigation between view controllers from the application’s ViewModel.

    So far, the application you’ve developed so far allows you to search Flickr with a simple search string. If you need a copy of current project, download it here.

    A Model layer service that uses ReactiveCocoa supplies the search results, and the ViewModel simply logs the response.

    Now, it’s time to work out how to navigate to the results page!

    Implementing ViewModel Navigation

    When a Flickr search successfully returns the desired behavior, the application navigates to a new view controller that displays the search results.

    The current application has a single ViewModel, described by the RWTFlickrSearchViewModel class. In order to implement this desired behavior, you’re going add a new ViewModel to ‘back’ the search results View.

    Add a new NSObject subclass named RWTSearchResultsViewModel to the ViewModel group. Update the header as follows:

    @import Foundation;
    #import "RWTViewModelServices.h"
    #import "RWTFlickrSearchResults.h"
     
    @interface RWTSearchResultsViewModel : NSObject
     
    - (instancetype)initWithSearchResults:(RWTFlickrSearchResults *)results services:(id<RWTViewModelServices>)services;
     
    @property (strong, nonatomic) NSString *title;
    @property (strong, nonatomic) NSArray *searchResults;
     
    @end

    The above adds a couple of properties that describe the View, and an initializer constructed from the RWTFlickrSearchResults model object (which is returned by the Model layer service).

    Open RWTSearchResultsViewModel.m and implement the initializer as follows:

    - (instancetype)initWithSearchResults:(RWTFlickrSearchResults *)results services:(id<RWTViewModelServices>)services {
      if (self = [super init]) {
        _title = results.searchString;
        _searchResults = results.photos;
      }
      return self;
    }

    This completes RWTSearchResultsViewModel.

    If you’ll recall from part one, the ViewModels are constructed before their View counterparts to ‘drive’ the application. The next step is to wire the ‘passive’ View to its respective ViewModel:

    Open RWTSearchResultsViewController.h, import the ViewModel, and add an initializer as shown below:

    #import "RWTSearchResultsViewModel.h"
     
    @interface RWTSearchResultsViewController : UIViewController
     
    - (instancetype)initWithViewModel:(RWTSearchResultsViewModel *)viewModel;
     
    @end

    Open RWTSearchResultsViewController.m and add the following private property to the class extension at the top of the file:

    @property (strong, nonatomic) RWTSearchResultsViewModel *viewModel;

    Further down the same file, implement the initializer:

    - (instancetype)initWithViewModel:(RWTSearchResultsViewModel *)viewModel {
      if (self = [super init]) {
        _viewModel = viewModel;
      }
      return self;
    }

    In this step, you’re focusing on how navigation works. You’ll return to this view controller shortly to bind the ViewModel to the UI.

    Your application now has two ViewModels, but you’re facing a conundrum here! How do you navigate from one ViewModel to the other, while also navigating between their respective view controllers?

    The ViewModel cannot have a direct reference to the View, so what kind of crazy magic do you need to perform?

    The answer is already present in the RWTViewModelServices protocol. It’s currently used to obtain a reference to the Model layer, and now you’re going to use this same protocol to allow the ViewModel to initiate the navigation.

    Open RWTViewModelServices.h and add the following method to the protocol:

    - (void)pushViewModel:(id)viewModel;

    Conceptually, the ViewModel layer drives the application; logic within this layer determines what displays in the View, as well as how and when navigation should occur.

    This method allows the ViewModel layer to initiate navigation by ‘pushing’ a ViewModel in the same way that a UINavigationController allows you to navigate by ‘pushing’ a UIViewController.

    Before updating this procotol implementation, you’re going to put it to work within the ViewModel layer.

    Open RWTFlickrSearchViewModel.m and import the newly added ViewModel:

    #import "RWTSearchResultsViewModel.h"

    Further down the same file, update the implementation of executeSearchSignal as follows:

    - (RACSignal *)executeSearchSignal {
      return [[[self.services getFlickrSearchService]
        flickrSearchSignal:self.searchText]
        doNext:^(id result) {
          RWTSearchResultsViewModel *resultsViewModel =
            [[RWTSearchResultsViewModel alloc] initWithSearchResults:result services:self.services];
          [self.services pushViewModel:resultsViewModel];
        }];
    }

    The above adds a doNext operation to the signal the search command creates when it executes. The doNext block creates the new ViewModel that displays the search results, and then pushes it via the ViewModel-services.

    Now, it’s time to update the code that implements this protocol, so when a ViewModel is pushed it navigates to the required view controller. To do this, the code needs a reference to the navigation controller.

    Open RWTViewModelServicesImpl.h and add the following initializer:

    - (instancetype)initWithNavigationController:(UINavigationController *)navigationController;

    Open RWTViewModelServicesImpl.m and import the following header:

    #import "RWTSearchResultsViewController.h"

    A little further down, add the following private property:

    @property (weak, nonatomic) UINavigationController *navigationController;

    Further down the same file, update the initializer as follows:

    - (instancetype)initWithNavigationController:(UINavigationController *)navigationController {
      if (self = [super init]) {
        _searchService = [RWTFlickrSearchImpl new];
        _navigationController = navigationController;
      }
      return self;
    }

    This simply updates the initializer to store a reference to the passed navigation controller.

    Finally, add the new method:

    - (void)pushViewModel:(id)viewModel {
      id viewController;
     
      if ([viewModel isKindOfClass:RWTSearchResultsViewModel.class]) {
        viewController = [[RWTSearchResultsViewController alloc] initWithViewModel:viewModel];
      } else {
        NSLog(@"an unknown ViewModel was pushed!");
      }
     
      [self.navigationController pushViewController:viewController animated:YES];
    }

    The above method uses this type of the supplied ViewModel to determine which View is required.

    In the trivial case above, there is only one concrete ViewModel-View pair, but I’m sure you can see how you could extend this pattern. The navigation controller pushes the resulting View.

    Onto the final step; open RWTAppDelegate.m, locate the line within createInitialViewController where the RWTViewModelServicesImpl instance is created, and update the code to pass the navigation controller to the initializer as follows:

    self.viewModelServices = [[RWTViewModelServicesImpl alloc] initWithNavigationController:self.navigationController];

    Build, run, type in a search term, hit ‘Go’ and watch as the application transitions to the new ViewModel / View:

    BlankView

    …which is currently blank! Don’t be disheartened; you’ll fix that in a few moments.

    Instead, give yourself a pat on the back; you now have an application with multiple ViewModels where the navigation is entirely controller via the ViewModel-layer!

    Note: John Gossman, one of the Microsoft developers who worked on WPF and helped create the MVVM pattern, often says that a litmus test for MVVM is whether the application runs without the UI.

    Your application passes this test. If you’re feeling adventurous, prove it by writing a unit test that executes a search and navigates from one ViewModel to the next.

    Now that you have a ‘pure’ solution, it’s time to get back to UI binding!

    Rendering the Results Table

    The View for the search results, as defined in RWTSearchResultsViewController, has a UITableView defined within its corresponding nib file. The next step is to render the ViewModel contents within this table.

    Open RWTSearchResultsViewController.m and locate the class extension. Update it to implement the UITableViewDataSource protocol:

    @interface RWTSearchResultsViewController () <UITableViewDataSource>

    Further down the same file, override viewDidLoad as follows:

    - (void)viewDidLoad {
      [super viewDidLoad];
     
      [self.searchResultsTable registerClass:UITableViewCell.class
                      forCellReuseIdentifier:@"cell"];
      self.searchResultsTable.dataSource = self;
     
      [self bindViewModel];
    }

    This performs the one-time initialization of the table view and binds the view model. Please ignore the hard-coded cell identifier constant, this will be removed shortly.

    Further down the same file, add the bindViewModel method:

    - (void)bindViewModel {
      self.title = self.viewModel.title;
    }

    Right now, this doesn’t do much. The ViewModel has two properties: the title that the above code handles, and the searchResults array that will render within the table.

    So how do you bind this array to the table view? Unfortunately the answer is, you don’t. Huh, what??

    ReactiveCocoa can bind simple properties on UIKit controls, but can’t handle the more complex interactions required to feed data into a table view :[ I know, it’s kind of a buzzkill.

    There’s no need to panic, because there’s another way. It’s time to roll-up your sleeves and take the manual approach.

    Within the same file, add the two mandatory data source methods as follows:

    - (NSInteger)tableView:(UITableView *)tableView
     numberOfRowsInSection:(NSInteger)section {
      return self.viewModel.searchResults.count;
    }
     
    - (UITableViewCell *)tableView:(UITableView *)tableView
             cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
      cell.textLabel.text = [self.viewModel.searchResults[indexPath.row] title];
      return cell;
    }

    The first simply reports the number of search results, while the second dequeues a cell and sets its title based on data from the ViewModel. That was simple enough wasn’t it?

    Build and run to see your table now populated with data:

    PopulatedTable

    Better Table View Binding

    The lack of table view binding can quickly result in a lean view controller growing in size. Manually ‘binding’ it to the table view makes this approach less elegant.

    This problem bugged me (As it should!), so I set about solving it.

    Conceptually, each item within the ViewModel’s searchResults array is a ViewModel in its own right, with each cell being the View for its respective ViewModel instance.

    In a recent blog post I created a generic binding-helper, named CETableViewBindingHelper, that allows you to define the View used for each child ViewModel, with the helper taking care of implementing the datasource protocol. You’ll find this helper in the Util group of the current project.

    The class constructor for CETableViewBindingHelper is shown below:

    + (instancetype) bindingHelperForTableView:(UITableView *)tableView
                                  sourceSignal:(RACSignal *)source
                              selectionCommand:(RACCommand *)selection
                                  templateCell:(UINib *)templateCellNib;

    To bind an array to the view, you simply create an instance of the helper class. The arguments are:

    1. The table view which renders the array of ViewModels
    2. A source signal that relays changes to the array
    3. An optional command to execute when a row is selected
    4. The nib for the cell Views.

    The cell that the given nib file defines within must implement the CEReactiveView protocol.

    The project already contains a table view cell that you can use for rendering the search results. Open RWTSearchResultsTableViewCell.h and import the required protocol:

    #import "CEReactiveView.h"

    And adopt it:

    @interface RWTSearchResultsTableViewCell : UITableViewCell <CEReactiveView>

    The next step is to implement this protocol. Open RWTSearchResultsTableViewCell.m and add the following imports:

    #import <SDWebImage/UIImageView+WebCache.h>
    #import "RWTFlickrPhoto.h"

    Then add the following method:

    - (void)bindViewModel:(id)viewModel {
      RWTFlickrPhoto *photo = viewModel;
      self.titleLabel.text = photo.title;
     
      self.imageThumbnailView.contentMode = UIViewContentModeScaleToFill;
     
      [self.imageThumbnailView setImageWithURL:photo.url];
    }

    Currently the searchResults property of RWTSearchResultsViewModel contains an array of RWTFlickrPhoto instances. Rather than wrapping these Model objects in ViewModels, they are bound directly to the view.

    Note: In instances where you don’t need any View-related logic, there’s nothing wrong with exposing Model objects to your View. Keep it simple!

    The bindViewModel method you just added also makes use of the SDWebImage pod that was added via CocoaPods. This useful utility downloads and decodes images on background threads, greatly improving scroll performance.

    The setImageWithURL: method on UIImageView is a category method added by SDWebImage.

    The final step is to use the binding helper to render the table.

    Open RWTSearchResultsViewController.m and import the helper:

    #import "CETableViewBindingHelper.h"

    Further down the same file, remove the UITableDataSource protocol implementation. You may also remove the implementation of the two methods from this protocol.

    Next, add the following private property within the class extension:

    @property (strong, nonatomic) CETableViewBindingHelper *bindingHelper;

    Further down the same file, remove the code you added in viewDidLoad to configure the table view and return this method to its original form:

    - (void)viewDidLoad {
      [super viewDidLoad]; 
      [self bindViewModel];
    }

    Finally, add the following to the end of bindViewModel:

    UINib *nib = [UINib nibWithNibName:@"RWTSearchResultsTableViewCell" bundle:nil];
     
    self.bindingHelper =
      [CETableViewBindingHelper bindingHelperForTableView:self.searchResultsTable
                                             sourceSignal:RACObserve(self.viewModel, searchResults)
                                         selectionCommand:nil
                                             templateCell:nib];

    This creates a UINib instance from the nib file and constructs the binding helper. The sourceSignal is created by observing changes to the searchResults property of the ViewModel.

    Build, run and admire the new UI:

    UsingTheBindingHelper

    This is a much more elegant way to bind arrays to table views!

    Some UI Flair

    So far, this tutorial has focused on structuring your application to follow the MVVM pattern. I bet you’re ready to take a break and add some flair!

    Since the release of iOS7, just over a year ago, ‘motion design‘ has gained greater visibility, and many designers now favor subtle animations and fluid actions.

    In this step, you’re going to add a subtle parallax scrolling effect to the photos. Fancy!

    Open RWTSearchResultsTableViewCell.h and add the following method:

    - (void) setParallax:(CGFloat)value;

    The table view that hosts these cells will use this to provide each cell with its parallax offset.

    Open RWTSearchResultsTableViewCell.m and implement the following:

    - (void)setParallax:(CGFloat)value {
      self.imageThumbnailView.transform = CGAffineTransformMakeTranslation(0, value);
    }

    That’s a nice, easy, simple transform.

    Open RWTSearchResultsViewController.m and import the following header:

    #import "RWTSearchResultsTableViewCell.h"

    A little further down the same file, adopt the UITableViewDelegate protocol via the class extension:

    @interface RWTSearchResultsViewController () <UITableViewDataSource, UITableViewDelegate>

    You just added a binding helper that sets itself as the delegate for the table view so it can respond to row selection. However, it also forwards delegate method invocations to its own delegate property so you can still add a custom behavior.

    Within the bindViewModel method, set the binding helper delegate:

    self.bindingHelper.delegate = self;

    Further down the same file, add an implementation of scrollViewDidScroll:

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
      NSArray *cells = [self.searchResultsTable visibleCells];
      for (RWTSearchResultsTableViewCell *cell in cells) {
        CGFloat value = -40 + (cell.frame.origin.y - self.searchResultsTable.contentOffset.y) / 5;
        [cell setParallax:value];
      }
    }

    Each time the table view scrolls, it calls this method. It iterates over all the visible cells, computing an offset value that applies a parallax effect. The actual offset applied to each cell depends on its location within the visible portion of the table view.

    The ’40′ and ’5′ in this code are magic numbers, and I am sure some of you’ll treat them with a mixture of disgust and contempt. I’m not going to lie, I found suitable values by a process of trial and error!

    Build, run and enjoy the parallax scrolling!

    ParallaxAnimation

    Back to the business of Views and ViewModels…

    Querying for Comment and Favorite Count

    The interface should show the number of comments and favorites for each photo on the bottom right. However, it simply shows the dummy text ’123′ from the nib file at the moment.

    You need to add this functionality to the Model layer before you can replace these with the real value. Follow the same process as before to add a Model object that represents the result of querying the Flickr API.

    Add a new NSObject subclass called RWTFlickrPhotoMetadata to the Model group. Open RWTFlickrPhotoMetadata.h and add the following properties:

    @property (nonatomic) NSUInteger favorites;
    @property (nonatomic) NSUInteger comments;

    Then open RWTFlickrPhotoMetadata.m and add and implement the description method:

    - (NSString *)description {
      return [NSString stringWithFormat:@"metadata: comments=%lU, faves=%lU",
              self.comments, self.favorites];
    }

    This will be useful for testing the functionality before the View-layer changes complete.

    Next open the RWTFlickrSearch.h and add the following method to the protocol:

    - (RACSignal *)flickrImageMetadata:(NSString *)photoId;

    ViewModel will use this to request the metadata for a given photo, like comments and favorites.

    Now, move on to the implementation! Open RWTFlickrSearchImpl.m and add the following imports:

    #import "RWTFlickrPhotoMetadata.h"
    #import <ReactiveCocoa/RACEXTScope.h>

    The next step is to implement the flickrImageMetadata method.

    Unfortunately, there is a bit of a problem: in order to obtain the number of comments related to a photo, you need to call the flickr.photos.getInfo API method; to obtain the number of favorites you need to call the flickr.photos.getFavorites method.

    That complicates matters a bit, with the flickrImageMetadata method requiring two API requests in order to provide the required data.

    Fortunately, ReactiveCocoa makes this very easy!

    Add the following method implementation:

    - (RACSignal *)flickrImageMetadata:(NSString *)photoId {
     
      RACSignal *favorites = [self signalFromAPIMethod:@"flickr.photos.getFavorites"
                                              arguments:@{@"photo_id": photoId}
                                              transform:^id(NSDictionary *response) {
                                                NSString *total = [response valueForKeyPath:@"photo.total"];
                                                return total;
                                              }];
     
      RACSignal *comments = [self signalFromAPIMethod:@"flickr.photos.getInfo"
                                            arguments:@{@"photo_id": photoId}
                                            transform:^id(NSDictionary *response) {
                                              NSString *total = [response valueForKeyPath:@"photo.comments._text"];
                                              return total;
                                            }];
     
      return [RACSignal combineLatest:@[favorites, comments] reduce:^id(NSString *favs, NSString *coms){
        RWTFlickrPhotoMetadata *meta = [RWTFlickrPhotoMetadata new];
        meta.comments = [coms integerValue];
        meta.favorites = [favs integerValue];
        return  meta;
      }];
    }

    The above code makes use of the signalFromAPIMethod:arguments:transform: to create signals from the underlying delegate-based ObjectiveFLickr API. The code above creates a pair of signals, one that obtains the number of favorites, and the other obtains the number of comments.

    Once you create both signals, the combineLatest:reduce: method generates a new signal that is a combination of both.

    This method waits for a next event from each of the source signals. The reduce block is invoked with their contents, and the result becomes the next event for the combined signal.

    Nice and simple!

    Before you celebrate the simplicity of this method, return to signalFromAPIMethod:arguments:transform: and fix a little error I alluded to earlier. Did you spot it?

    This method creates a new OFFlickrAPIRequest instance for each API request. However, the results of each request return via the delegate object, and in this case it happens to be self.

    As a result, in the case of concurrent requests there is no way to tell which invocation of flickrAPIRequest:didCompleteWithResponse: corresponds to which request.

    Fortunately, the ObjectiveFlickr delegate method signature includes the corresponding request as its first argument, so this problem is quite easy to fix with a bit more ReactiveCocoa.

    Within signalFromAPIMethod:arguments:transform:, replace the pipeline that handles the successSignal with the following:

    @weakify(flickrRequest)
    [[[[successSignal
      filter:^BOOL(RACTuple *tuple) {
        @strongify(flickrRequest)
        return tuple.first == flickrRequest;
      }]
      map:^id(RACTuple *tuple) {
        return tuple.second;
      }]
      map:block]
      subscribeNext:^(id x) {
        [subscriber sendNext:x];
        [subscriber sendCompleted];
      }];

    This simply adds a filter operation that removes any delegate method invocations that relate to requests other than the one that generates the current signal.

    Note: The above code uses @weakify to create a weak reference to flickrRequest. This is because the flickrRequest variable is in the same scope as the block, and mirrors the classic retain-self issue.

    I recommend profiling this code with and without these macros to see the memory leak for yourself. This one is a pretty subtle and could easily make it into a final build!

    The final step is to use this signal within the ViewModel layer.

    Open RWTSearchResultsViewModel.m and import the following header:

    #import "RWTFlickrPhoto.h"

    Further down the same file, add the following to the end of the initializer:

    RWTFlickrPhoto *photo = results.photos.firstObject;
    RACSignal *metaDataSignal = [[services getFlickrSearchService]
                                flickrImageMetadata:photo.identifier];
        [metaDataSignal subscribeNext:^(id x) {
         NSLog(@"%@", x);
       }];

    This tests the newly added method for obtaining image metadata on the first photo in the returned results. The output of this signal is just logged.

    Build, run and search for some photos. When the results display, you’ll see a log message similar to the one below:

    2014-06-04 07:27:26.813 RWTFlickrSearch[76828:70b] metadata: comments=120, faves=434

    Fetching Metadata for Visible Cells

    You could extend on the current code to fetch the metadata for all the search results.

    However, with 100 photos in a typical result, this would immediately fire 200 API requests, or two per photo. Most APIs have a rate limit, and this kind of usage is almost certainly going to get your API key blocked, at least temporarily.

    You only need to fetch metadata for the photos that are currently visible within the table. So how do you implement this behavior? You guessed it, you need a ViewModel that is aware of its own visibility!

    Currently RWTSearchResultsViewModel exposes an array of RWTFlickrPhoto instances that are bound to the View, and these are Model-layer objects that are exposed to the View. In order to add the concept of visibility, you’re going to wrap these model objects in ViewModels that add this View-centric state.

    Within the ViewModel group add an NSObject subclass called RWTSearchResultsItemViewModel. Open the newly added header file and update as follows:

    @import Foundation;
    #import "RWTFlickrPhoto.h"
    #import "RWTViewModelServices.h"
     
    @interface RWTSearchResultsItemViewModel : NSObject
     
    - (instancetype) initWithPhoto:(RWTFlickrPhoto *)photo services:(id<RWTViewModelServices>)services;
     
    @property (nonatomic) BOOL isVisible;
    @property (strong, nonatomic) NSString *title;
    @property (strong, nonatomic) NSURL *url;
    @property (strong, nonatomic) NSNumber *favorites;
    @property (strong, nonatomic) NSNumber *comments;
     
    @end

    As you can see from the initializer, this ViewModel wraps an instance of the RWTFlickrPhoto model object.

    The properties of this ViewModel are a mixture of:

    • Those which expose the underlying Model properties (title, url)
    • Those that update dynamically when the metadata is fetched (favorites, comments)
    • And isVisible that indicates whether this ViewModel is visible or not.

    Open RWTSearchResultsItemViewModel.m and import the following headers:

    #import <ReactiveCocoa/ReactiveCocoa.h>
    #import <ReactiveCocoa/RACEXTScope.h>
    #import "RWTFlickrPhotoMetadata.h"

    Below, the imports add a class extension with a private property:

    @interface RWTSearchResultsItemViewModel ()
     
    @property (weak, nonatomic) id<RWTViewModelServices> services;
    @property (strong, nonatomic) RWTFlickrPhoto *photo;
     
    @end

    Further down the same file, implement the initializer as follows:

    - (instancetype)initWithPhoto:(RWTFlickrPhoto *)photo services:(id<RWTViewModelServices>)services {
      self = [super init];
      if (self) {
        _title = photo.title;
        _url = photo.url;
        _services = services;
        _photo = photo;
     
        [self initialize];
      }
      return  self;
    }

    This bases the title and url properties on the values from the Model object, and then stores reference to the services and photo arguments via the private properties.

    Next add the initialize method. Get ready, this is where the clever stuff happens!

    - (void)initialize {
      RACSignal *fetchMetadata =
        [RACObserve(self, isVisible)
         filter:^BOOL(NSNumber *visible) {
           return [visible boolValue];
         }];
     
      @weakify(self)
      [fetchMetadata subscribeNext:^(id x) {
        @strongify(self)
        [[[self.services getFlickrSearchService] flickrImageMetadata:self.photo.identifier]
         subscribeNext:^(RWTFlickrPhotoMetadata *x) {
           self.favorites = @(x.favorites);
           self.comments = @(x.comments);
         }];
      }];
    }

    The first part of this method creates a signal named fetchMetadata by observing the isVisible property and filtering for ‘true’ values. As a result, this signal emits a next value when the isVisible property is set to true.

    The next part subscribes to this signal in order to initiate the request to the flickrImageMetadata method. When this nested signal fires a next event, the favorites and comments properties update with the result.

    In summary, when isVisible is set to true, the Flickr API requests are fired, and the comments and favorites properties update at some point in the future.

    To put this new ViewModel to use, open RWTSearchResultsViewModel.m and import the following:

    #import <LinqToObjectiveC/NSArray+LinqExtensions.h>
    #import "RWTSearchResultsItemViewModel.h"

    Within the initializer, remove the code that currently sets _searchResults and replace with the following:

    _searchResults =
      [results.photos linq_select:^id(RWTFlickrPhoto *photo) {
        return [[RWTSearchResultsItemViewModel alloc]
                  initWithPhoto:photo services:services];
      }];

    This simply wraps each Model object with a ViewModel.

    The final step is to set the isVisible property via the View and to make use of these new properties.

    Open RWTSearchResultsTableViewCell.m and add the following import:

    #import "RWTSearchResultsItemViewModel.h"

    Further down the same file, change the first line of the bindViewModel method to use the newly added ViewModel:

    RWTSearchResultsItemViewModel *photo = viewModel;

    Further down the same method, add the following:

    [RACObserve(photo, favorites) subscribeNext:^(NSNumber *x) {
      self.favouritesLabel.text = [x stringValue];
      self.favouritesIcon.hidden = (x == nil);
    }];
     
    [RACObserve(photo, comments) subscribeNext:^(NSNumber *x) {
      self.commentsLabel.text = [x stringValue];
      self.commentsIcon.hidden = (x == nil);
    }];
     
    photo.isVisible = YES;

    This observes the new comments and favorites properties, when they are updated the corresponding labels and images are updated.

    Finally, the isVisible property of the ViewModel is set to YES. The table view binding helper only binds visible cells, so only the first few ViewModels will request their metadata.

    Build, run and watch as the metadata now dynamically fetches as you scroll:

    WithMetadata

    That’s pretty cool isn’t it?

    Throttling

    Unfortunately, there is still another problem to address — a developer’s job is never done. If you scroll really fast you can trick the application into fetching metadata for many images almost simultaneously.

    Not only does this irritate your API, but it also hinders performance. Scrolling becomes choppy as these requests take a bigger and bigger chunk out of your processing power.

    In order to solve this problem, the application should only initiate a metadata request when a photo is on display for a short period of time. Currently the isVisible property of the ViewModel is set to YES, but never gets set back to NO. That’s the first thing to fix.

    Open RWTSearchResultsTableViewCell.m and change the code you just added in bindViewModel: to set the isVisible property as follows:

    photo.isVisible = YES;
    [self.rac_prepareForReuseSignal subscribeNext:^(id x) {
      photo.isVisible = NO;
    }];

    The isVisible property is set to YES when the ViewModel binds to the View as before. But it reverts to NO when the cell is removed from the table view and recycled.

    You accomplish this via the rac_prepareForReuseSignal signal that you added to UITableViewCell by ReactiveCocoa.

    Return to RWTSearchResultsItemViewModel. This ViewModel needs to observe changes to the isVisible property, and fire a request for metadata when this property is YES for more than one second.

    ReactiveCocoa provides you an elegant way to achieve this.

    Within RWTSearchResultsItemViewModel.m, update the initialize method by , removing the creation of the fetchMetadata signal. Then, replace it with the following:

    // 1. 
    RACSignal *visibleStateChanged = [RACObserve(self, isVisible) skip:1];
     
    // 2. 
    RACSignal *visibleSignal = [visibleStateChanged filter:^BOOL(NSNumber *value) {
      return [value boolValue];
    }];
     
    RACSignal *hiddenSignal = [visibleStateChanged filter:^BOOL(NSNumber *value) {
      return ![value boolValue];
    }];
     
    // 3.
    RACSignal *fetchMetadata = [[visibleSignal delay:1.0f]
                               takeUntil:hiddenSignal];

    Taking each step in turn:

    1. A signal is created by observing the isVisible property. The first next event fired by this signal will contain the initial state of this property. Since you’re only interested in changes to this property value, the skip operation suppresses the first event.
    2. A pair of signals are created by filtering the visibleStateChanged signal, one indicating the transition from visible to hidden, and the other from hidden to visible.
    3. Here’s the magic part! The fetchMetadata signal is created by delaying the visibleSignal by one second, providing a pause before fetching metadata. The takeUntil operation ensures that if the cell becomes hidden again before the one second time interval, the next event from the visibleSignal is suppressed and metadata is not fetched.

    Can you imagine how much more complex that would be without ReactiveCocoa?

    Build, run and celebrate the fact that favorites and comments for an image only load after a short pause. As a result, you’ll enjoy silky-smooth scrolling. You could also add logging, via the logAll operation, to observe the frequency of the API requests.

    Error Handling

    Currently the code that searches Flickr only handles the flickrAPIRequest:didCompleteWithResponse: method defined on the OFFlickrAPIRequestDelegate protocol.

    Unfortunately, network requests can — and do — go wrong for a number of reasons. Any good application must handle such errors with elegance and keep the users informed so they don’t do something drastic out of frustration, like chuck their iPhones into the nearest body of water.

    The delegate also defines a method, flickrAPIRequest:didFailWithError:, which is invoked if something goes wrong with a request. In this section, you’re going to use this to handle errors and display an alert to the user.

    RWTFlickrSearch protocol defines your Model-layer interface for searching Flickr, and it returns results via signals.

    You might recall from the earlier tutorials that signals emit next, completed and error events. As a result, there are no changes required to this interface to relay error conditions.

    Open RWTFlickrSearchImpl.m and locate the signalFromAPIMethod:arguments:transform: method for all the API requests.

    Within this method, add the following just before the creation of the successSignal variable:

    RACSignal *errorSignal =
      [self rac_signalForSelector:@selector(flickrAPIRequest:didFailWithError:)
                     fromProtocol:@protocol(OFFlickrAPIRequestDelegate)];
     
    [errorSignal subscribeNext:^(RACTuple *tuple) {
      [subscriber sendError:tuple.second];
    }];

    The above code creates a signal from the delegate method, subscribes to the signal, and sends an error if any error events occur.

    The tuple passed to the subscribeNext block contains the variables passed to the flickrAPIRequest:didFailWithError: method. As a result, tuple.second obtains the source error and uses it for the error event.

    This is a nifty solution, don’t you think? Now all of the API requests have built-in error handling! The next step is to put this to use.

    The RWTFlickrSearchViewModel does not expose signals directly to the View, instead it exposes state (via properties) and a command. You need to expand the interface for this class to provide error reporting.

    Open RWTFlickrSearchViewModel.h and add the following property:

    @property (strong, nonatomic) RACSignal *connectionErrors;

    Open RWTFlickrSearchViewModel.m and add the following to the end of the initialize method:

    self.connectionErrors = self.executeSearch.errors;

    The executeSearch property is a RACCommand from the ReactiveCocoa framework. The RACCommand class has an errors property that forwards any errors that occur when the command is executes. Incidentally, RACCommand is also a signal.

    To handle these errors, open RWTFlickrSearchViewController.m and add the following to the bottom of the initWithViewModel: method.

    [_viewModel.connectionErrors subscribeNext:^(NSError *error) {
      UIAlertView *alert =
      [[UIAlertView alloc] initWithTitle:@"Connection Error"
                                 message:@"There was a problem reaching Flickr."
                                delegate:nil
                       cancelButtonTitle:@"OK"
                       otherButtonTitles:nil];
      [alert show];
    }];

    This shows an alert view whenever an error occurs.

    Build, run, disconnect your network and enjoy the new error handling support:

    ErrorMessages

    Are you wondering why the requests that fetch favorites and comments don’t report errors? It’s by design; those incomplete requests don’t significantly affect the application’s usability.

    Note: For a fun challenge, you might want to try handling these errors.

    Personally, I’d add error signals to the RWTSearchResultsItemViewModel. Rather than handle these directly in the corresponding View, RWTSearchResultsTableViewCell, I’d centralize the error handling by aggregating the errors within the parent RWTFlickrSearchViewModel, allowing consistent error handling.

    Adding a Recent Search list

    You know your user is going to want to go back and ogle at some of the same pictures repeatedly. So, why not make it easy for him or her?

    If you recall the images at the start of this article, the final application has a list of recent searches that display below the search text field:

    FinishedApp

    Now you just need to add this functionality, and it’s a step I’m going to issue as a challenge!

    Yes, that’s right. I’m leaving you to fend for yourself, but I have total confidence that you know enough to handle it. It’s time to put your MVVM skills to practice…

    To start you off in the right direction, here’s a brief summary of how I would do it:

    1. I’d create a ViewModel that represents each of the previous searches, with properties that detail the search text, number of matches and the first matching image.
    2. I’d modify RWTFlickrSearchViewModel to expose an array of these new ViewModels as a property. Whenever a search executes successfully, this property would update with the new results.
    3. Rendering this array of ViewModels is quite simple with the CETableViewBindingHelper, and I’ve already added a suitable cell within the project, RWTRecentSearchItemTableViewCell.

    If you want to take it further, how about making it so that tapping a recent search repeats the search? This will involve exposing a command that executes when the user taps one of these recent search items.

    If you got stuck at any point within this MVVM tutorial, you can find all the code on GitHub with a commit for each build-and-run step. It also contains a solution to this challenge, but please try it for yourself first!

    Where To Go From Here?

    Again, here is the final example project from this MVVM tutorial series (including the challenge results).

    This two-part tutorial has covered quite a lot of ground, so I thought it might help to recap some of the key points in easily-digestible bullet points.

    Regarding the MVVM pattern:

    • MVVM is a recent variant on the MVC pattern that is gaining popularity.
    • The MVVM pattern results in very ‘thin’ Views that improve code clarity and enhance testability.
    • The visibility is strictly View => ViewModel => Model, with updates in the ViewModel reflecting in the View via bindings.
    • The ViewModel should never hold a reference to the View.
    • The ViewModel can be thought of as the model-of-the-view, it exposes properties that directly reflect the state of the View, together with commands that execute because of user interaction.
    • The Model layer typically exposes services, in this case an API for querying Flickr. Although another good example is a service that provides persistence, why not try persisting the recent searches in this app via a service?
    • A good litmus test for an MVVM application is that it can run without the UI.
    • ReactiveCocoa provides a powerful mechanism for binding ViewModels to Views. However it’s also used extensively within the ViewModel and Model layers.

    Next time you create an app, why not give MVVM a try?

    I hope you enjoyed learning about MVVM! If you have any questions or thoughts, please share them in the comments below.

    MVVM Tutorial with ReactiveCocoa: Part 2/2 is a post from: Ray Wenderlich

    The post MVVM Tutorial with ReactiveCocoa: Part 2/2 appeared first on Ray Wenderlich.


    Video Tutorial: Saving Data in iOS Part 6: Using XML

    How to Make a Game Like Jetpack Joyride in Unity 2D – Part 1

    $
    0
    0

    Flying MouseWith the release of Unity 4.3, developers no longer had to use third party libraries or create their own solutions to build 2D games. They now had access to a 2D toolset right out of the box. Combined with some of the standard Unity tools, making 2D games in Unity was no longer a painful process, it was … dare I say … fun. :]

    In this tutorial, you are going to make something equally as fun. It’s a game that doesn’t feature any irritated or flailing birds, rather it involves a mouse and his jetpack. Can you guess the game? (Hint: it’s in the title of this tutorial)

    Yes, it’s Jetpack Joyride. Cue the oohs and ahhs.

    Jetpack Joyride is a game that was released from Halfbrick Studios in 2011. It’s a very easy game to understand. Steal a jetpack, and fly away with it. Collect the low hanging coins and avoid the lasers. In essence, it’s a fun twist on an endless runner that controls well with touch screens. Touch the screen to fly up. Release the screen to drop back down. Avoid the obstacles to stay alive as long as you can.

    In your game, you will be steering a mouse through a very long house, similarly collecting coins and avoiding lasers. Granted, not everyone hangs coins from their walls, but I’m guessing a few of you have at least a couple high wattage lasers hanging about their house. :]

    This is the first part of a three part series. In this tutorial, you’ll learn how to work with physics in Unity, how to use sorting layers to organize your sprites, how to use colliders to define your world limits, and even how to kill a mouse or two. :]

    In part two, you’re going to move the mouse forward through randomly generated rooms simulating an endless level. In addition, you’ll add a fun animation to make the mouse run when it is grounded.

    In part three, you will add lasers, coins, sound effects, music and even parallax scrolling. By the end of this part, you will have fully functional game (albeit with a lower mice population, for sure).

    Interested in making this game in another framework? There are a few tutorials on how to make to it using Cocos2D 2.x and Corona. In case you’re interested in completing those tutorials you can find them here: Cocos2D 2.x edition and Corona edition. But we both know those tutorials are old school. The cool kids only use Unity. Am I right or am I right?

    Getting Started

    To get started you will need some art, sound effects and music for the game. I’ve prepared a package that contains everything that you need. You can download it here: RocketMouse_Unity_Resources

    Solution Inside: See contents of the package SelectShow>

    Note: This tutorial requires you to have at least some basic experience with Unity. You need to know how to work with Unity interface, add game assets, add components to Game Objects and so on.

    Completing tutorials from Unity 4.3 2D tutorial series by Chris LaPollo should be more then enough. I know that he also states that you need to have some experience, but I’ve shown his tutorial to my cat, and now he works for one big game studio :]

    In other words I strongly recommend you to read Chris LaPollo’s tutorial series whether you have zero experience or already have done a couple sample projects.

    And one more thing, I’m using OS X version of Unity, but since Unity works very similar on OS X and Windows, you shouldn’t have any problems completing this tutorial on Windows.

    Creating and Configuring the Project

    Open Unity and create new project by choosing File\New Project….

    Note: If you created enough Unity 2D projects before, you can just download the starter project, unpack it and skip to the Adding Game Assets section.

    Also Note: As you probably know you can support many different platforms and resolutions when creating games with Unity. However, for simplicity sake, in this tutorial you will work with resources intended for iPhone Retina.

    rocket_mouse_unity_02_created_projects

    When the Project Wizard window opens, click Set in Create new Project tab to set a directory for your project. Type RocketMouse in Save As field to name the project and choose a folder in which you want to store this project. Click Save when you’re done.

    Don’t rush clicking Create Project button. Before that, you need to choose 2D in the combo box labeled Set up defaults for:. After that, you can safely click Create Project.

    rocket_mouse_unity_03_project_wizard

    Although setting defaults to 2D when creating new project supposed to be the only thing you need to change to switch Unity to 2D mode, sometimes this doesn’t work. Well, at least it didn’t work several times for me, including this one. So it is better to make sure you are in 2D mode.

    Making Sure You’re in 2D Mode

    Here is a list of things you need to check to make sure everything is setup for a 2D project:

    1. Select Edit\Project Settings\Editor to open the Editor Settings in the Inspector.
      In the Default Behavior Mode section, make sure Mode is set to 2D, as it is shown on the screenshot below.
      rocket_mouse_unity_04_2d_behaviour_mode
    2. In the Scene view’s control bar make sure 2D mode is enabled.

      rocket_mouse_unity_05_2d_mode_scene_view

    3. Select Main Camera in the Hierarchy and make sure that the Projection is set to Orthographic and its Position is set to (0,0, -10).

      rocket_mouse_unity_06_camera_orthographic

    After checking and adjusting all settings, it is a good time to save the scene.
    Create a folder named Scenes in the Project browser. Then open the Save Scene dialog by selecting File\Save Scene or using a ⌘S (Ctrl+S on Windows) shortcut. Navigate to the Scenes folder you just created, name the scene RocketMouse.unity and click Save.

    rocket_mouse_unity_07_save_scene

    Configuring Game View

    Switch to the Game view and set your Game view size to a fixed resolution of 1136×640. If you don’t have this resolution option in the list, create it and name it iPhone Landscape.

    rocket_mouse_unity_08_game_view

    Select Main Camera in the Hierarchy. In the Inspector, inside the Camera component, set Size to 3.2.

    rocket_mouse_unity_09_camera_size

    Save the scene. There are no big changes in Unity window from the project creation, but you’ve done several very important configuration steps. Without them the game wouldn’t work the way it should.

    Adding the Player character

    In this section of the tutorial you will add the player character who happens to be a cool mouse with a jetpack. Hell yeah!

    Note: If you skipped the project configuration part and just downloaded the starter project you should continue from here.

    Before you start, make sure you have downloaded the package with resources for this game. After unpacking the archive you should see two directories inside: Sprites and Audio. While you will be using a bunch of the Sprite files, you will not be using any of the audio files until another part of the tutorial. Just remember that you have them :]

    Importing Game Assets

    To add all the assets in one go simply select both Sprites and Audio folders and drag them to the Project browser, next to the Scenes folder that you created earlier.

    rocket_mouse_unity_11_adding_assets

    Note: Of course the folders won’t appear in the Project browser as fast as it is shown on the GIF image. You will have to wait until the files are processed by Unity, bit this won’t take more than half a minute.
    rocket_mouse_unity_12_processing_assets

    That’s it, you’ve just added all required assets. At this point it might seem that there are many strange files. Don’t worry, most of the images are just decorations and backgrounds. Apart from that there are images for the mouse character, the laser and the coin objects.

    Adding Player to the Scene

    It is time to actually add something to the scene. Open the Sprites folder in the Project browser and find a sprite named mouse_fly within this folder. Drag this sprite to the scene.

    rocket_mouse_unity_13_add_mouse_go

    Doing this will create an object in the Hierarchy named mouse_fly (just as the image used to create it).

    rocket_mouse_unity_14_mouse_fly_in_heararchy

    Select mouse_fly in the Hierarchy and make following changes in the Insepector:

    1. Change its name to mouse, since this will better describe the player character.
    2. Set its Position to (0, 0, 0). You will move the mouse to its final position a bit later, but right now it is better to place it right at the screen center, so that you can see it better.
    3. Add a Circle Collider 2D component, by clicking Add Component in the Inspector. In the drop down menu select Physics 2D and then choose Circle Collider 2D.
    4. Set the Radius of the Circle Collider 2D component to 0.5.
    5. Add a Rigidbody 2D component, by clicking Add Component and selecting Physics 2D\Rigidbody 2D.
    6. Enable the Fixed Angle checkbox inside the Rigidbody 2D component.

    Here is an image demonstrating all the steps:
    rocket_mouse_unity_15_mouse_inspector_settings

    The green circle in the Scene view shows the collider. You must have noticed that its size changed when you changed the Radius property of the Circle Collider 2D component.

    Colliders define a shape that are used by the physics engine to determine collisions with other objects. You could have created a more pixel-perfect collider by using a Polygon Collider 2D component, like in screenshot below:

    rocket_mouse_unity_16

    However, using complex colliders makes it harder for the physics engine to detect collisions, which in turn, creates a performance penalty. Just imagine how much easier it is to check if a circle collides with a rectangle, as opposed to when two complex polygons collide.

    This is why it is recommended that you use simple colliders whenever possible. As you will see, a circle collider works really well for this game. The only adjustment was the radius of the collider, in order for the collider to match the original mouse image.

    While colliders define the shape of the object, the Rigidbody is what puts your game object under the control of the physics engine. Without a Rigidbody component, the GameObject is not affected by gravity. Thus, you cannot apply force and torque to the GameObject, and so on.

    In fact you even can’t detect collisions between two GameObject, even though both may have Collider components. One of the objects must have a Rigidbody component.

    However, while you want the mouse to be affected by the gravity and collide with other objects, you don’t want its rotation to be changed. Fortunately, it is easy to solve by enabling the Fixed Angle property of the Rigidbody 2D component.

    Run the scene and watch as the mouse falls down, affected by the gravity force.

    rocket_mouse_unity_17

    But wait! Why did the mouse fall down at all? You didn’t add any gravity to the Rigidbody… or did you? In fact, when you added the Rigidbody 2D component, it was given a default Gravity Scale of 1. This tells the system to make the character fall using the default gravity of the physics engine.

    Creating Script to Control Jetpack

    Don’t let that mouse fall down into the abyss. Not on my shift :]

    Why do the Good Mice Die Young?

    You need to add a script that will enable the jetpack and apply force to the mouse object to move it up and keep from falling.

    To add a script to the mouse object follow these steps:

    1. In the Project browser, create a new folder called Scripts, just as you created the Scenes folder before. Make sure that this folder is selected, since Unity will add a new script to the currently selected folder.
      rocket_mouse_unity_19
    2. Select Assets\Create\C# script in the top menu and name the script MouseController.
    3. Drag MouseController script over mouse in the Hierarchy to add it to the mouse game object. This will add a Script component and set its Script property to MouseController script.

    rocket_mouse_unity_20

    Note: Make sure you correctly name the script when you first create it as opposed to creating a NewBehaviourScript and then renaming it.

    Otherwise you will get following error when trying to add this script to game object.

    Can't add script behaviour MouseController. The scripts file name does not match the name of the class defined in the script!

    This happens because Unity creates following class inside the script, and renaming the script doesn’t change the class name inside.

    public class NewBehaviourScript

    It is time to write some code. Open the MouseController script by double clicking it either in the Project browser or in the Inspector. This will open the MouseController.cs file in MonoDevelop.

    Add the following jetpackForce public variable just inside the class definition:

    public float jetpackForce = 75.0f;

    This will be the force applied to the mouse when the jetpack is on.

    Note: It is a good idea to make it a public variable and set a default value. This way you can adjust the jetpack force in the Inspector, but will have a default value in case you forget or don’t want to adjust it.

    rocket_mouse_unity_21

    Next, add the following method inside the class:

        void FixedUpdate () 
        {
            bool jetpackActive = Input.GetButton("Fire1");
     
            if (jetpackActive)
            {
                rigidbody2D.AddForce(new Vector2(0, jetpackForce));
            }
        }

    The FixedUpdate method is called by Unity at a fixed time step. All physics related code is written in this method.

    Note: The difference between the Update and the FixedUpdate methods is that the FixedUpdate method is called at a fixed rate, while the Update method is simply called for every frame.

    Since frame rate can vary, the time between the Update method calls can also vary and physics engines do not work well with variable time step. This is why the FixedUpdate method exists and should be used to write the code related to the physics simulation (e.g. applying force, setting velocity and so on).

    In FixedUpdate you check if the Fire1 button is currently pressed. Fire1 is defined by default in Unity as a left mouse button click, the left Control key on a keyboard, or a simple screen tap in the case of an iOS app. For this game, you want the jetpack to engage when the user touches the screen. Therefore, if Fire1 is currently pressed, the code will add a force to the mouse.

    The rigidbody2D property simply returns the Rigidbody 2D component attached to the current game object or null if there is no Rigidbody 2D component attached. The AddForce method simply applies the force to the rigidbody. It takes a Vector2 that defines the direction and the magnitude of the force to apply. You will move the mouse forward later, so right now you only apply the force to move the mouse up with the magnitude of jetpackForce.

    Run the scene and hold your left mouse button to enable the jetpack and make the mouse move up.

    rocket_mouse_unity_22

    Adjusting the Gravity

    The jetpack works, but you can see several problems straight away. First, depending on your perspective, the jetpack force is too strong; or the gravity is too weak. It’s far too easy to send the mouse flying off the top of the screen, never to be seen again. Rather than change the jetpack force, you can change the gravity setting of the entire project. By changing the gravity setting globally, you set a smarter default for the smaller iPhone screen. And besides, who doesn’t like the idea of controlling gravity? :]

    To change the gravity force globally chose Edit\Project Settings\Physics 2D. This will open the Physics 2D Settings of the project in the Inspector. Find the Gravity field and set its Y value to -15.

    rocket_mouse_unity_23

    Run the scene again. It should be much easier to keep the mouse within the game screen.

    rocket_mouse_unity_24

    Don’t worry if you’re still having difficulties keeping the mouse within the game screen. Try making your Game view bigger or adjust the jetpackForce or Gravity settings. The values recommended here work well when you run the game on the iPhone. Of course, adding a floor and a ceiling will help keep the mouse in sight, so you’ll add those next.

    Adding the Floor and the Ceiling

    As you might have guessed, adding a floor and a ceiling is a relatively simple exercise; all you really need is an object for the mouse to collide with at the top and bottom of the scene. When you created the Mouse object earlier, it was created with an image so the user can visually track where the object is throughout the game. The floor and ceiling, however, can be represented by empty GameObjects as they never move and their location is relatively obvious to the user.

    Choose GameObject\Create Empty to create empty object. You won’t see it on the screen right now… What do you expect? It’s empty! Select GameObject in the Hierarchy and make following changes in the Inspector:

    1. Rename it to floor.
    2. Set its Position to (0, -3.5, 0).
    3. Set its Scale to (14.4, 1, 1).
    4. Click Add Component and add a Box Collider 2D component by selecting Physics 2D\Box Collider 2D.

    Now you should see a green collider at the bottom of the scene:

    Unity Floor Collider

    Don’t worry too much about the magic numbers in the Position and Scale properties; they will make more sense later as more elements get added to the Scene.

    Note: You didn’t add a Rigidbody 2D component to the floor game object. This is because the floor should only limit the mouse movement, but it shouldn’t be affected by gravity, nor will you apply a force to it, and so on.

    Run the scene. Now the mouse falls on the floor and stays there.

    Mouse at Bottom of Screen

    However if you activate the jetpack, the mouse still leaves the room since there is no ceiling.

    I’m sure you can add a ceiling yourself. Set its Position to (0, 3.7, 0), and don’t forget to rename it to ceiling.

    Solution Inside: Need help adding a ceiling? SelectShow>

    Now there is both a floor and a ceiling present in the scene.

    Ceiling and Floor Colliders

    And if you run the game you will see that the mouse will never fly off the top or fall off the bottom of the scene.

    Colliding Mouse

    Using a Particle System to Create Jetpack Flames

    Now that you’ve got the mouse moving at the user’s every will, it’s time to add some flare. In this section, you’ll make the jetpack shoot flames when the mouse goes up. Why flames? Because everything’s better with flames!

    There are many different ways you can show flames coming out of the jetpack, but my personal favorite is using a Particle System. Particle systems are used to create a lot of small particles and simulate effects like fire, explosions, fog; all based on how you configure the system.

    Creating a Particle System

    To add a Particle System to the scene choose GameObject\Create Other\Particle System. You’ll notice a change to the scene immediately; the Particle System will show it’s default behavior in the the Scene when the object is selected.

    rocket_mouse_unity_25

    This is a good start, but right away you should notice some problems. First, the particle system always stays in the middle of the screen, regardless of where the rocket mouse flies. To make the particles always emit from the jetpack, you’ll need to add the Particle System as a child of the mouse. In the Hierarchy, drag Particle System over mouse to add it as a child. It should look like the following screenshot:

    rocket_mouse_unity_26

    Now that the Particle System moves correctly, configure it to resemble flames by selecting Particle System in the Hierarchy and changing the following in the Inspector:

    1. Rename the Particle System to jetpackFlames.
    2. Set its Position to (-0.62, -0.33, 0) to move it to the jetpack nozzle.
    3. Set its Rotation to (65, 270, 270) to set the direction at which the particles will be emitted.
    4. Still in the Inspector, find the Particle System component and set following properties:
      • Set Start Lifetime to 0.5
      • Set Start Size to 0.3
      • Click on Start Color and set Red to 255, Green to 135, Blue to 40 and leave Alpha at 255. You should get a nice orange color.
      • Expand Emission section and set Rate to 300. Make sure you don’t uncheck the checkbox to the left of Emission section and disable it.
      • Expand Shape section. Make sure Shape is set to Cone and set Angle to 12 and Radius to 0.1. Finally enable the Random Direction checkbox.

    Here is how the particle system should look right now:

    rocket_mouse_unity_28

    If your jetpack flames are different make sure you’ve set all the settings shown on this screenshot:

    rocket_mouse_unity_29

    Improving the Flames

    The flames are looking okay, but you’ll notice that the flame particles stop suddenly, as if they hit an invisible wall at the end of the particle emitter. You can fix this by changing the color of the particles as they fall further from the jet pack.

    Select jetpackFlames in the Hierarchy and search for a section called Color over Lifetime in the Particle System component. Enable it by clicking the white circled checkbox to the left of the section name. Expand the section.

    rocket_mouse_unity_31

    Note: Right now it is just plain white. This might seem strange, since you clearly see the orange color of the flames. However, Color over Lifetime works in a bit different way. Instead of setting the color it multiplies itself with the Start Color value.

    Since multiplying any color with white just gives the original color, you always see the orange color. But you can change the Color over Lifetime to a gradient and set the end color to have 0 alpha. This way particles will disappear gradually.

    Click the white color box within Color over Lifetime to open the Gradient Editor. It should look like this:

    rocket_mouse_unity_32

    Select the top slider on the right and change the Alpha value to 0. Then close the Gradient Editor.

    rocket_mouse_unity_33

    Run the scene. Now jetpack flames look much more realistic.

    rocket_mouse_unity_34

    Creating a Level Section

    Remember that the mouse should glide through an endless room, avoiding lasers and collecting coins. However, it will take an eternity to create an endless room by adding everything by hand :] You got the joke, right?

    rocket_mouse_unity_p2_1

    Anyway, you’re going to create a few different level sections and will add a script that will randomly add them ahead of the player. As you might imagine, this can’t all be done at once! You’ll start by simply adding a few background elements in this tutorial.
    In Part 2, you’ll start creating additional rooms for the mouse to fly through.

    This is how one level section might look like:

    rocket_mouse_unity_p2_2

    The process of creating level section (let’s call one section a room) consists of 3 steps:

    • Adding the background
    • Adding the floor and the ceiling
    • Adding decorative objects (book shelf, mouse hole, and so on)

    Adding Room Background

    Make sure the Scene view and the Project browser are visible. In the Project browser open the Sprites folder and drag the bg_window sprite to the scene. You don’t need to place it in a precise location; you’ll take care of that in a minute.

    rocket_mouse_unity_p2_3

    Right now your background will be on top of the mouse object. We will sort this out later, so for now just ignore the mouse. Select bg_window in the Hierarchy and set its Position to (0, 0, 0).

    rocket_mouse_unity_p2_4

    After placing the central section of the room, you need to add few more sections. One to the left and one to the right of the window.

    This time use the bg sprite. Find bg in the Project browser and drag it to the scene two times. First time to the left, and second time to the right of bg_window. Don’t try to place it precisely. Right now you only need to add it to the scene.

    You should get something like this:

    rocket_mouse_unity_p2_5

    Looks like a room by Salvador Dali, doesn’t it? :]

    Note: You might wonder why you have to build the room around the (0, 0, 0) point. This is required because you will add all rooms to an Empty game object, so you need to make sure that you know where the room center point is. It will get clearer when you’ll start generating the level.

    Using Vertex Snapping

    You could just position every background element on the screen based each elements size, but moving objects by calculating these values all the time is not very convenient.

    Instead you’re going to use Unity’s Vertex Snapping feature, which easily allows you to position elements next to each other. Just look how easy it is:

    rocket_mouse_unity_p2_6

    To use vertex snapping, you simply need to hold the V key after selecting, but before moving the GameObject.

    Solution Inside: Need more detailed instruction on using Vertex Snapping SelectShow>

    If you run the game scene, or simply switch to the Game view you will see the mouse (the player character, not your actual input device) is behind the background. It’s as if your mouse is locked outside, trying to get back in. Don’t let your hero freeze outside!

    rocket_mouse_unity_p2_7

    Using Sorting Layers

    To make the mouse appear on top of the room background, you’re going to use a feature called Sorting Layer. It will take only a moment to set everything up.

    Select the mouse in the Hierarchy and search for the Sprite Renderer component in the Inspector. There you will see a drop down called Sorting Layer, which value is currently set to Default, as it is shown below.

    rocket_mouse_unity_p2_8

    Open the drop down and you’ll see a list of all the sorting layers that you currently have in your project. Right now there should be only Default.

    Click on the Add Sorting Layer… option to add more sorting layers. This will immediately open the Tags & Layers editor.

    Add following sorting layers, by clicking the + button.

    1. Background
    2. Decorations
    3. Objects
    4. Player

    Note: The order is important, since the order of sorting layers defines the order of the objects in the game.

    When you’re done the Tags & Layers editor should look like this:

    rocket_mouse_unity_p2_9

    Right now you’re going to need only Background and Player sorting layers. Other sorting layers will be used later.

    Select mouse in the Hierarchy and set its Sorting Layer to Player. The mouse should immediately move to the front of the scene.

    rocket_mouse_unity_p2_10

    You should also move the background elements (two different bg objects and bg_window) to the background layer by selecting each object in the Hierarchy and changing it’s layer. This isn’t immediately necessary, but will help keep our house in order as you add more elements.

    rocket_mouse_unity_p2_11

    If you run the game, you will still notice one problem – your jetpack flames are behind the background.

    rocket_mouse_unity_p2_12

    You can try to find the Sorting Layer property in the Inspector for the jetpackFlames particle system, but you won’t find it. This is just a small reminder that Unity is initially a 3D game engine and some of its objects are still not fully updated to work in 2D world.

    Fixing the Particle System Sorting Order

    Even though there’s no Sorting Layer property for the jetpackFlames in the Inspector, that doesn’t mean you can’t access and change it with a script.

    First, create a new C# script and call it Creating ParticleSortingLayerFix. Attach it to the jetpackFlames object. Stuck on how to do it? Check out the spoiler below:

    Solution Inside: Creating ParticleSortingLayerFix script SelectShow>

    Open the ParticleSortingLayerFix script by double clicking it in the Project browser or in the Inspector.

    Add the following code in Start:

    particleSystem.renderer.sortingLayerName = "Player";
    particleSystem.renderer.sortingOrder = -1;

    The first line sets the sorting layer for the particle system, and the second line sets the order of the object within the sorting layer. If you don’t set it to -1 then jetpack flames will be displayed on top of the jetpack, as is shown below:
    rocket_mouse_unity_p2_21

    Run the scene and you should see that jetpack flames are now displayed above the background.

    rocket_mouse_unity_p2_22

    However, if you stop the game and switch to the Scene view, the particle system will still be displayed behind the background. Unfortunately, you have to live with this. The flames’ Sorting Layer is set in code, so it won’t show up in the Scene UI. Maybe this will be fixed in some of the next Unity updates.

    Decorating the Room

    To decorate the room you can use any amount of bookcases and mouse holes from Sprites folder in the Project browser. You can position them anyway you want. Just don’t forget to set their Sorting Layer to Decorations.

    Solution Inside: Need the help of interior designer? SelectShow>

    This is how I decorated the room:rocket_mouse_unity_p2_18

    Note: You don’t have to do it exactly like me, after all maybe your mouse is a rich mouse and can afford 2 book cases :]

    Just don’t cover the window with book cases. You will add something to look at later in the tutorial.

    Now, this is starting to look like a real game!

    Where To Go From Here?

    I hope you liked the tutorial so far. Now that you have your hero the mouse flying up and down on a basic background, head over to Part 2 where your mouse will start to move forward through randomly generated rooms. You’ll even add a few animations to keep the game fun and engaging.

    You can download the final project for this part here: RocketMouse_Final_Part1

    I would love to hear your comments and questions below. See you in Part 2!

    How to Make a Game Like Jetpack Joyride in Unity 2D – Part 1 is a post from: Ray Wenderlich

    The post How to Make a Game Like Jetpack Joyride in Unity 2D – Part 1 appeared first on Ray Wenderlich.

    How to Make a Game Like Jetpack Joyride in Unity 2D – Part 2

    $
    0
    0

    This is the second part of tutorial about How to create a game like Jetpack Joyride in Unity 2D. If you’ve missed the first part, you can find it here.

    In the first part of this tutorial series you created a game with a mouse flying up and down in a room. Oh, and don’t forget the flames, they look nice ;] Although the fire is fun to look at, simply adding jetpack flames doesn’t make a good game.

    In this part of the tutorial series you’re going to move the mouse forward through randomly generated rooms simulating an endless level. In addition, you’ll add a fun animation to make the mouse run when it is grounded.

    Getting started

    If you completed the first part of this tutorial series you can continue working with your own project. Alternatively you can download the starter project for this part of tutorial here: RocketMouse_Final_Part1

    Find your project or download and unpack the final project for the first part. Then find and open the RocketMouse.unity scene.

    rocket_mouse_unity_p2_0

    Making the Mouse Fly Forward

    It is time to move forward, literally. To make the mouse fly forward you will need to do two things.

    • Make the mouse actually move.
    • Make the camera follow the mouse.

    Adding a bit of code solves both tasks.

    Setting the Mouse Velocity

    That’s easy. Open the MouseController script and add following public variable:

    public float forwardMovementSpeed = 3.0f;

    It will define how fast the mouse moves forward.

    Note: Once again, by making it a public variable you’re giving yourself an opportunity to adjust the speed from Unity without opening the script in MonoDevelop.

    rocket_mouse_unity_p2_26

    After that add the following code at the end of FixedUpdate:

    Vector2 newVelocity = rigidbody2D.velocity;
    newVelocity.x = forwardMovementSpeed;
    rigidbody2D.velocity = newVelocity;

    This code simply sets the velocity x-component without making any changes to y-component. It is important to update only the x-component, since the y-component is controlled by the jetpack force.

    Run the scene. The mouse moves forward, but there is a problem. At some point the mouse just leaves the screen.

    rocket_mouse_unity_p2_23

    To fix this you need to make the camera follow the mouse.

    Making the Camera Follow the Player

    Create a new C# Script named CameraFollow. Drag it over the Main Camera in the Hierarchy to add it as a component.

    Open CameraFollow script in MonoDevelop and add the following public variable:

    public GameObject targetObject;

    You will assign it to the mouse GameObject in a moment, so that the camera knows which object to follow.

    Add the following code to Update:

    float targetObjectX = targetObject.transform.position.x;
     
    Vector3 newCameraPosition = transform.position;
    newCameraPosition.x = targetObjectX;
    transform.position = newCameraPosition;

    This code simply takes the x-coordinate of the target object and moves the camera to that position.

    Note: You only change the x-coordinate of the camera, since you don’t want it to move up or down following the mouse.

    Switch back from MonoDevelop to Unity and select Main Camera in the Hierarchy. There is a new property in the CameraFollow script component called Target Object. You will noticed that it is not set to anything.

    rocket_mouse_unity_p2_24

    To set the Target Object, click on mouse in the Hierarchy and without releasing drag mouse to the Target Object field in the Inspector as shown below:

    Note: It is important not to release the mouse button, since if you click on mouse and release the mouse button you will select it and the Inspector will show the mouse properties instead of Main Camera.

    Alternatively you can lock the Inspector to the Main Camera by clicking the lock button in the Inspector.

    rocket_mouse_unity_p2_25

    rocket_mouse_unity_p2_27

    Run the scene. This time the camera follows the mouse.

    rocket_mouse_unity_p2_28

    This is a good news / bad news kinda thing – the good news is the camera follows the mouse! The bad news is that, well, nothing else does! You’ll address this in a moment, but first, you need to give the mouse a little space. He’s a shy sort of fella. :]

    Keeping the Camera at the Distance

    In most endless run games the player character is not placed at the center of the screen. Instead it is placed somewhere in the middle of the left side of the screen. This is to give the player more time to react and avoid obstacles, collect coins, etc.

    To do this, select the mouse in the Hierarchy and set its Position to (-3.5, 0, 0) and run the scene.

    rocket_mouse_unity_p2_29

    Wait, the mouse is still centered on the screen, but this has nothing to do with the mouse position. This happens because the camera script centers the camera at the target object. This is also why you see the blue background on the left, which you didn’t see before.

    To fix this, open the CameraFollow script and add distanceToTarget private variable:

    private float distanceToTarget;

    Then add the following code to Start:

    distanceToTarget = transform.position.x - targetObject.transform.position.x;

    This will calculate the initial distance between the camera and the target.

    Finally, modify the code in Update to take this distance into account:

    void Update () {
    	float targetObjectX = targetObject.transform.position.x;
     
    	Vector3 newCameraPosition = transform.position;
    	newCameraPosition.x = targetObjectX + distanceToTarget;
    	transform.position = newCameraPosition;
    }

    The camera script will now keep the initial distance between the target object and the actual camera. It will also maintain this gap throughout the entire game.

    Run the scene. The mouse now remains offset to the left.

    rocket_mouse_unity_p2_30

    Generating Endless Level

    Right now playing the game more then a few seconds doesn’t make much sense. The mouse simply flies out of the room into a blue space.

    You could write a script that adds backgrounds, places the floor and the ceiling and finally adds some decorations. However, it is much easier to save the complete room as a Prefab and then instantce the whole room at once.

    Note: In a game like Jetpack Joyride, you’ll often see different areas (aquarium, caves, etc.) that are each their own different Prefab. For the purposes of this game, you’ll stick with one.

    rocket_mouse_unity_p2_31

    Here is an excerpt from Unity documentation regarding Prefabs.

    A Prefab is a type of asset — a reusable GameObject stored in Project View. Prefabs can be inserted into any number of scenes, multiple times per scene. When you add a Prefab to a scene, you create an instance of it. All Prefab instances are linked to the original Prefab and are essentially clones of it. No matter how many instances exist in your project, when you make any changes to the Prefab you will see the change applied to all instances.

    In other words you add objects to your scene, set their properties, add components like scripts, colliders, rigidbodies and so on. Then you save your object as a Prefab and you can instantiate it as many times as you like with all the properties and components in place.

    Creating Room Prefab

    You’re going to want your Prefab to contain all the different room elements: the book case, the window, the ceiling, etc. To include all these elements as part of the same Prefab, you’ll first need to add them to a parent object.

    To do this, create empty GameObject by choosing GameObject\Create Empty. Then select GameObject in the Hierarchy, and make following changes in the Inspector:

    • Rename it to room1
    • Set its Position to (0, 0, 0)

    This is what you should see in the Inspector:
    rocket_mouse_unity_p2_32

    Note: At this moment it is important to understand that Empty is placed right in the center of the room and at the (0,0,0) point. This is not a coincidence and is done intentionally.

    When you add all the room parts into Empty to group them, their positions will become relative to that Empty. Later when you will want to move the whole room by moving Empty it will be much easier to position it knowing that setting the position of the Empty will move the room center at this point.

    In other words when you add objects to Empty its current position becomes the pivot point. So it is much easier if the pivot point is at the center of the group rather then somewhere else.

    Move all the room parts (bg, bg, bg_window, ceiling, floor, object_bookcase_short1, object_mousehole) into room1, just as you did when added the jetpack flames particle system to the mouse object.

    rocket_mouse_unity_p2_33

    Note: If you decorated your room with more bookcases or mouse holes you should also add them to room1.

    Create a new folder named Prefabs in the Project browser. Open it and drag room1 from the Hierarchy directly into Prefabs folder.

    rocket_mouse_unity_p2_34

    That’s it. Now you can see a Prefab named room1 containing all the room parts. To test it try and drag room1 Prefab to the scene. You will see how easy it is to create room duplicates using a Prefab.

    Note: You can reuse this Prefab not only in this scene but in other scenes too.

    rocket_mouse_unity_p2_35

    The Idea Behind the Room Generation

    The idea behind the generator script is quite simple. The script that has an array of rooms it can generate, a list of rooms currently generated, and two additional methods. One method checks to see if another room needs to be added and the other method actually adds room.

    To check if a room needs to be added, the script will enumerate all existing rooms and see if there is a room ahead, farther then the screen width, to guarantee that the player never sees the end of the level.

    rocket_mouse_unity_p2_37

    As you can see in case #1 you don’t need to add a room yet, since the end of the last room is still far enough from the player. And in case #2 you should already add a room.

    Note: The center of the mouse object doesn’t lie on the left edge of the screen. So although the distance to the end of the level is less then screen width, the player still won’t see the level end in case #2, but he will soon, so it is better to add a room.

    Of course the image above is only a rough example, the real script will detect that it needs to generate new room much earlier, right after the end of the room passes the point at which it is closer to mouse then the screen width.

    Now when the idea is clear it is time to add the script.

    Adding Script to Generate Rooms

    Create new C# Script and name it GeneratorScript. Add this script to the mouse GameObject. Now mouse should have two script components:

    rocket_mouse_unity_p2_36

    Open GeneratorScript in MonoDevelop by double clicking it in the Project view or in the Inspector.

    First, add System.Collections.Generic namespace, since you’re going to use List<T> class:

    using System.Collections.Generic;

    Then add following instance variables:

    public GameObject[] availableRooms;
     
    public List<GameObject> currentRooms;
     
    private float screenWidthInPoints;

    The availableRooms will contain an array of Prefabs, which the script can generate. Currently you have only one Prefab (room1). But you can create many different room types and add them all to this array, so that the script could randomly choose which room type to generate.

    Note: The final project that you can download at the end of Part 3 contains multiple room types as well as other improvements, but right now it is easer to work with only one room Prefab.

    The currentRooms list will store instanced rooms, so that it can check where the last room ends and if it needs to add more rooms. Once the room is behind the player character, it will remove it as well.

    The screenWidthInPoints variable is just required to cache screen size in points.

    Now, add the following code in Start:

    float height = 2.0f * Camera.main.orthographicSize;
    screenWidthInPoints = height * Camera.main.aspect;

    Here you calculate the size of the screen in points. The screen size will be used to determine if you need to generate new room, as it is described above.

    The Method to Add New Room

    Add the following AddRoom method to GeneratorScript:

    void AddRoom(float farhtestRoomEndX)
    {
        //1
        int randomRoomIndex = Random.Range(0, availableRooms.Length);
     
        //2
        GameObject room = (GameObject)Instantiate(availableRooms[randomRoomIndex]);
     
        //3
        float roomWidth = room.transform.FindChild("floor").localScale.x;
     
        //4
        float roomCenter = farhtestRoomEndX + roomWidth * 0.5f;
     
        //5
        room.transform.position = new Vector3(roomCenter, 0, 0);
     
        //6
        currentRooms.Add(room);         
    }

    This method adds new room using the farhtestRoomEndX point, which is rightmost point of the level so far. Here is description of every line of this method:

    1. Picks a random index of the room type (Prefab) to generate.
    2. Creates a room object from the array of available rooms using the random index above.
    3. Since the room is just an Empty containing all the room parts, you cannot simply take its size. Instead you get the size of the floor inside the room, which is equal to the room’s width.
    4. When you set the room position, you set the position of its center so you add the half room width to the position where the level ends. This way gets the point at which you should add the room, so that it started straight after the last room.
    5. This sets the position of the room. You need to change only the x-coordinate since all rooms have the same y and z coordinates equal to zero.
    6. Finally you add the room to the list of current rooms. It will be cleared in the next method which is why you need to maintain this list.
    7. Now take a short break, the next method is going to be a bit bigger.

    rocket_mouse_unity_p2_38

    The Method to Check If New Room Is Required

    Ready? Add the GenerateRoomIfRequired method:

    void GenerateRoomIfRequired()
    {
        //1
        List<GameObject> roomsToRemove = new List<GameObject>();
     
        //2
        bool addRooms = true;        
     
        //3
        float playerX = transform.position.x;
     
        //4
        float removeRoomX = playerX - screenWidthInPoints;        
     
        //5
        float addRoomX = playerX + screenWidthInPoints;
     
        //6
        float farthestRoomEndX = 0;
     
        foreach(var room in currentRooms)
        {
            //7
            float roomWidth = room.transform.FindChild("floor").localScale.x;
            float roomStartX = room.transform.position.x - (roomWidth * 0.5f);    
            float roomEndX = roomStartX + roomWidth;                            
     
            //8
            if (roomStartX > addRoomX)
                addRooms = false;
     
            //9
            if (roomEndX < removeRoomX)
                roomsToRemove.Add(room);
     
            //10
            farthestRoomEndX = Mathf.Max(farthestRoomEndX, roomEndX);
        }
     
        //11
        foreach(var room in roomsToRemove)
        {
            currentRooms.Remove(room);
            Destroy(room);            
        }
     
        //12
        if (addRooms)
            AddRoom(farthestRoomEndX);
    }

    It only might looks scary, but in fact it is quite simple. Especially if you keep in mind the idea previously described.

    1. Creates a new list to store rooms that needs to be removed. Separate lists are required since you cannot remove items from the list while you iterating through it.
    2. This is a flag that shows if you need to add more rooms. By default it is set to true, but most of the time it will be set to false inside the foreach.
    3. Saves player position.

      Note: I always say position, but most of the time you only work with x coordinate.

    4. This is the point after which the room should be removed. If room position is behind this point (to the left), it needs to be removed.

      Note: You need to remove rooms, since you cannot endlessly generate rooms without removing them after they are already not needed. Otherwise you will simply run out of memory.

    5. If there is no room after addRoomX point you need to add a room, since the end of the level is closer then the screen width.
    6. In farthestRoomEndX you store the point where the level currently ends. You will use this variable to add new room if required, since new room should start at that point to make the level seamless.
    7. In foreach you simply enumerate current rooms. You use the floor to get the room width and calculate the roomStartX (a point where room starts, leftmost point of the room) and roomEndX (a point where the room ends, rightmost point of the room).
    8. If there is a room that starts after addRoomX then you don’t need to add rooms right now. However there is no break instruction here, since you still need to check if this room needs to be removed.
    9. If room ends to the left of removeRoomX point, then it is already off the screen and needs to be removed.
    10. Here you simply find the rightmost point of the level. This will be a point where the level currently ends. It is used only if you need to add a room.
    11. This removes rooms that are marked for removal. The mouse GameObject already flew through them and thus, they are far behind, so you need to remove them.
    12. If at this point addRooms is still true then the level end is near. addRooms will be true if it didn’t find a room starting farther then screen width. This indicate that a new room needs to be added.

    Phew, that was hard but you’ve maid it!

    rocket_mouse_unity_p2_39

    Add FixedUpdate to GeneratorScript containing the call to GenerateRoomIfRequred:

    void FixedUpdate () 
    {    
            GenerateRoomIfRequired();
    }

    This insures that GenerateRoomIfRequred is periodically executed.

    Note: You don’t have to call this method each time, and the method itself can be optimized, but for simplicity sake you’ll leave it like this.

    Setting the Script Options and Enjoying

    Now, return to Unity and select the mouse GameObject in the Hierarchy. In the Inspector, find the GeneratorScript component.

    rocket_mouse_unity_p2_40

    Drag the room1 from the Hierarchy to Current Rooms list. Then open Prefabs folder in Project browser and drag room1 from it to Available Rooms.

    As a reminder, the Available Rooms property in the GeneratorScript is used as an array of room types that the script can generate. The Current Rooms property is room instances that are currently added to the scene.

    This means that Available Rooms or Current Rooms can contain unique room types, that are not present in the other list.

    Here is an animated GIF demonstrating the process. Note that I’ve created one more room type called room2, just to demonstrate what would you do in case you had many room Prefabs.

    rocket_mouse_unity_p2_41

    Run the scene. Now the mouse can endlessly fly through the level.

    rocket_mouse_unity_p2_42

    Note that rooms are appearing and disappearing in the Hierarchy while you fly. And for even more fun, run the scene and switch to the Scene view without stopping the game. This way you will see how rooms are added and removed in real time.

    Note: If you run the scene and switch to the Scene view after some time you will only find empty Scene. This is because the mouse already flew far to the right and all the rooms behind, including the first room were removed. You can try to find the mouse or just restart the scene and switch to the Scene view straight away.

    rocket_mouse_unity_p2_43

    Animating the Mouse

    Right now the mouse is very lazy. It doesn’t want to move a muscle and simply let the jetpack drag it on the floor. However, the price for the jetpack fuel is quite expensive, so it is better for the mouse to run while on the ground :]

    To make the mouse run, you’re going to create an animation and modify the MouseController script to switch between animations while on the ground or in the air.

    Slicing mouse_run Animation Spritesheet

    Frames of running animation are contained within the mouse_run spritesheet, so first of all you need to slice it correctly.

    Open Sprites folder in the Project browser and find mouse_run. Select it and set its Sprite Mode to Multiple in the Inspector.

    Then click the Sprite Editor button to open the Sprite Editor.

    rocket_mouse_unity_p3_1

    In the Sprite Editor click Slice button in the left top corner, to open slicing options. Set the Type field to Grid. Set the grid size to 162 x 156 and click Slice button. You will see the grid immediately appear.

    Don’t forget to click Apply button to save changes.

    rocket_mouse_unity_p3_2

    Close the Sprite Editor. Now if you expand mouse_run in the Project browser you will see that it was sliced on four different sprites.

    rocket_mouse_unity_p3_3

    Creating Animations

    Now that you have all frames, you can create the running and flying mouse animation.

    Note: Flying animation consists of one sprite. The sprite that you used to create the mouse, so you already had all the frames for this animation.

    To work with animations you will need to open Animation window, if you don’t have it already opened. Choose Window\Animation and open the Animation view.

    Place it somewhere so that the Project browser so that you can see both the Animation view and the Project view. I prefer placing it on top, next to the Scene and the Game< views, but you can place it anywhere you like.

    rocket_mouse_unity_p3_4

    Before you create your first animation, create a Animations folder in the Project view and select that new folder. Don’t forget that most of new files in Unity are created in the folder that is currently selected in the Project browser.

    rocket_mouse_unity_p3_5

    Next, select the mouse GameObject in the Hierarchy, since new animations will be added to most recently selected object in the Hierarchy.

    rocket_mouse_unity_p3_7

    In the Animation window create two new clips: run and fly, by selecting [Create New Clip] in the dropdown menu at the top left corner, to the left of Samples property.

    rocket_mouse_unity_p3_6

    Note the 3 new files created in the Project view: fly and run animations. You will also notice a mouse animator file. Select the mouse in the Hierarchy. In the inspector, you will see that that an Animator component was automatically added to it.

    rocket_mouse_unity_p3_12

    Adding Run Animation Frames

    First, you’re going to add frames to the run animation. Make sure both the Animation view and the Project view are visible.

    In the Animation view select run animation.

    rocket_mouse_unity_p3_11

    In the Project view, open the Sprites folder and expand mouse_run spritesheet.

    rocket_mouse_unity_p3_8

    Select all animation frames: mouse_run_0, mouse_run_1, mouse_run_2, mouse_run_3. Drag the frames to the Animation view’s timeline as shown below:

    rocket_mouse_unity_p3_10

    Here is how the timeline should look like after you have added the frames.

    rocket_mouse_unity_p3_13

    Adding Fly Animation Frame

    Believe it or not, the Fly animation will consist of only one frame.

    Select fly animation in the Animation view.

    rocket_mouse_unity_p3_9

    In the Project view find mouse_fly sprite and drag it to the timeline, just as you did with run animation. But this time you only need to add one sprite.

    rocket_mouse_unity_p3_14

    Make sure you stop the recoding mode in the Animation view after you add animation frames, or you might accidentally animate mouse position or other properties. Click the red dot in the Animation view’s control bar to stop the recording.

    rocket_mouse_unity_p2_rec

    Why would someone want to create an animation with only one frame? Well, you will see in a moment that it will be much easier to switch between the running and flying mouse states using the Animator transitions.

    Adjusting the Animator and Run Animation Settings

    Run the scene. You will notice two strange things.

    1. The mouse is running like crazy.
    2. The mouse is not falling down even when you don’t touch the screen.

    rocket_mouse_unity_p3_15

    Fortunately both issues are quite easy to fix. The first one occurs because the animation is played too fast. The second one is due to the Animator settings.

    Note: Did you notice that the mouse is now running by default, although earlier it was just single flying mouse sprite?

    This happens because you added run animation first and Animator component set is as the default animation. This way it starts playing as soon as you run the scene.

    To fix the animation speed select run animation in the Animation view and set Samples property to 8 instead of 60.

    rocket_mouse_unity_p3_16

    To fix the second issue, select mouse GameObject in the Hierarchy and search for Animator component in the Inspector.

    Disable Apply Root Motion and enable Animate Physics.

    rocket_mouse_unity_p3_17

    Here is an excerpt from Unity documentation about the Apply Root Motion property:

    Root motion is the effect where an object’s entire mesh moves away from its starting point but that motion is created by the animation itself rather than by changing the Transform position.

    In other words, you need to enable it if your animation changes the object Transform. This is not the case right now, which is why you turned it off.

    Also since the game is using physics, it is a good idea to keep animations in sync with physics. This is why you check the Animate Physics checkbox.

    Run the scene. Now the mouse walks on the floor.

    rocket_mouse_unity_p3_18

    However, the mouse continues to walk even while it is in the air.

    rocket_mouse_unity_p3_19

    To fix this, you need to create some animation transitions.

    Switching Between Animations

    Since there are two animations, you should make the mouse GameObject switch between them. To do this you’re going to use the Animator Transitions mechanism.

    Creating Animation Transitions

    At this point you’re going to need one more Unity window. In top menu choose Window\Animator to add the Animator view.

    Currently you have two animations there: run and fly. The run animation is orange, which means that it is the default animation.

    rocket_mouse_unity_p3_20

    However, now there is no transition between run and fly animations. This means that the mouse is stuck forever in the run animation state. To fix this you need to add two transitions: from run to fly and back from fly to run.

    To add a transition from run to fly, right-click the run animation and select Make Transition, then hover over fly animation and left-click on it.

    To add a transition from fly to run, right-click the fly animation, select Make Transition and this time hover over run animation and left-click.

    Here is the process of creating both transitions:

    rocket_mouse_unity_p3_21

    This has created two unconditional transitions which means that when you run the scene, the mouse will first play its run state, but after playing run animation one time, the mouse will switch to fly state. Once the fly state is completed, it will transition back to the run state and continue ad infinitum.

    It is hard to notice, because the fly animation takes only a fraction of a second to play, since there is only one frame. However, if you switch to the Animator while the scene is running you will see that there is a constant process of transitioning between the animations as follows:

    rocket_mouse_unity_p3_22

    Adding Transition Parameter

    To break the vicious circle, you need to add a condition that controls when the fly animation should transition to the run animation and vice versa.
    Open the Animator view and find the Parameters panel in the left bottom corner. Currently it is empty. Click a + button to add a parameter, in the dropdown select Bool.

    rocket_mouse_unity_p3_23

    Name the new parameter grounded.

    rocket_mouse_unity_p3_24

    Select the transition from run to fly to open transition properties in the Inspector. In Conditions section change the only condition from Exit Time to grounded and set its value to false.

    rocket_mouse_unity_p3_25

    Do the same with the transition from fly to run, but this time set the grounded value to true. This way the mouse state will be changed to fly when grounded is false, and to run when grounded is true.

    While you still have to pass in the parameters, you can test the transitions right now. To do this, run the scene, then make sure the Animator view is visible and check or uncheck grounded parameter while the game is running.

    rocket_mouse_unity_p3_26

    Adding Object to Check if Mouse Grounded

    There are many ways to check if the game object is grounded. I like following method because it provides visual representation of the point where the ground is checked, and this is quite useful when you have many different checks (e.g. ground check, ceiling check, and so on).

    What makes this method visual is an Empty GameObject added as a child of the player character, like it is shown below.

    rocket_mouse_unity_p3_27

    Go ahead and create an Empty GameObject, then drag it over mouse GameObject in the Hierarchy, to add it as a child object. Select this GameObject in the Hierarchy and rename it to groundCheck. Set its Position to (0, -0.7, 0).

    To make it more visual, click on the icon selection button in the Inspector and set its icon to the green oval. You can really choose any color, but green is truly the best. :]

    rocket_mouse_unity_p3_28

    Here is what you should get in the end:

    rocket_mouse_unity_p3_29

    The script will use the position of this Empty to check if it is on the ground.

    Using Layers to Define What is Ground

    Before you can check that the mouse is on the ground you need to define what is ground. If you don’t do this, the mouse will walk on top of lasers, coins and other game objects with colliders.

    You’re going to use the LayerMask class in the script, but to use it, you first must set correct Layer to the floor object.

    Open Prefabs folder in the Project view and expand room1 Prefab. Select floor that is inside the Prefab.

    rocket_mouse_unity_p3_extra_floor_prefab

    In the Inspector click on Layer dropdown and choose Add Layer… option.

    rocket_mouse_unity_p3_30

    This will open the Tags & Layers editor in the Inspector. Find first editable element, which is User Layer 8 and enter Ground in it. All previous layers are reserved by Unity.

    rocket_mouse_unity_p3_31

    Next, select the floor within Prefab folder and once again and set its Layer to Ground.

    rocket_mouse_unity_p3_34

    Checking if Mouse is Grounded

    To make the mouse automatically switch states, you will have to the update MouseController script to check if the mouse is currently grounded, then let the Animator know about it.

    Open MouseController script in MonoDevelop and add following instance variables:

    public Transform groundCheckTransform;
     
    private bool grounded;
     
    public LayerMask groundCheckLayerMask;
     
    Animator animator;

    The groundCheckTransform variable will store a reference to that groundCheck Empty that you created earlier.

    The grounded variable denotes if the mouse is grounded.

    The groundCheckLayerMask stores a LayerMask that defines what is the ground.

    Finally the animator variables contains a reference to the Animator component.

    Note: It is better to cache components you get by GetComponent in some instance variable, since GetComponent is slow.

    To cache Animator component open following line of code to Start:

    animator = GetComponent<Animator>();

    Now add UpdateGroundedStatus method:

    void UpdateGroundedStatus()
    {
        //1
        grounded = Physics2D.OverlapCircle(groundCheckTransform.position, 0.1f, groundCheckLayerMask);
     
        //2
        animator.SetBool("grounded", grounded);
    }

    This methods checks if the mouse is grounded and sets the animator parameter:

    1. To Check if the mouse GameObject is grounded, you create a circle of 0.1 radius at position of the groundCheck object that you added to the scene.

      If this circle overlaps any object that has a Layer specified in groundCheckLayerMask then the mouse is grounded.

    2. This code actually sets grounded parameter of Animator which then triggers the animation.

    Finally, add a call to UpdateGroundedStatus at the end of FixedUpdate:

    UpdateGroundedStatus();

    This calls the method with each fixed update, insuring that the ground status is consistently checked.

    Setting MouseController Script Parameters for Ground Check

    There is only one small step left to make the mouse automatically switch between flying and running. Open Unity and select mouse GameObject in the Hierarchy.

    Search for the Mouse Controller script component. You will see two new parameters of the script:

    rocket_mouse_unity_p3_35

    Click the Ground Check Layer Mask dropdown and select Ground layer. Drag the groundCheck from the Hierarchy to the Ground Check Transform property.

    rocket_mouse_unity_p3_36

    Run the scene.

    rocket_mouse_unity_p3_37

    Enabling and Disabling Jetpack Flames

    Although you cured the mouse from laziness you haven’t cured its wastefulness :] The jetpack is still On even when the mouse is on the ground. You don’t want the mouse to go bankrupt do you? Especially when only few tweaks in the code are needed to fix this.

    Open MouseController script and add jetpack public variable to store a reference to the particle system.

    public ParticleSystem jetpack;

    Then add the following AdjustJetpack method:

    void AdjustJetpack (bool jetpackActive)
    {
        jetpack.enableEmission = !grounded;
        jetpack.emissionRate = jetpackActive ? 300.0f : 75.0f; 
    }

    This method disables the particle system when grounded and in addition to that, it also decreases the emission rate when the mouse is falling down, since jetpack might be still be active, but not at full strength.

    Add a call to this method to the end of FixedUpdate:

    AdjustJetpack(jetpackActive);

    Just a reminder: jetpackActive variable is true when you hold the left mouse button and false when you don’t.

    Now switch back to Unity and drag jetpackFlames from the Hierarchy to jetpack property of the MouseController script.

    rocket_mouse_unity_p3_38

    Run the scene.

    rocket_mouse_unity_p3_39

    Now the jetpack has three different states. It is disabled when the mouse is grounded, full strength when going up, and a small emission rate when the mouse is going down. I find it pretty realistic since it is unlikely that you just turn Off the jetpack at the highest point and fall down.

    Where to Go From Here

    I hope you are enjoying the tutorial so far. You can download the final project for this part using this link: RocketMouse_Final_Part2

    The next part will be the final part and has all the fun. You will add lasers, coins, sound effects, and many more.

    If you want to know more about Prefabs Unity documentation is a good place to start.

    If you have any comments, questions or issues (I hope you don’t have them:]) please post them below.

    How to Make a Game Like Jetpack Joyride in Unity 2D – Part 2 is a post from: Ray Wenderlich

    The post How to Make a Game Like Jetpack Joyride in Unity 2D – Part 2 appeared first on Ray Wenderlich.

    Adaptive UI: The raywenderlich.com Podcast Episode 8

    $
    0
    0
    Episode 8: Adaptive UI

    Episode 8: Adaptive UI

    In this episode, we talk about one of the most exciting new features of iOS 8: Adaptive UI, along with special guest Sam Davies.

    We also talk about the latest news like Xcode 6-beta 3, the new iPod touch devices (an inexpensive developer device option), and famed game designer Rob Pardo leaving Blizzard!

    [Subscribe in iTunes] [RSS Feed]

    Here’s what is covered in this episode:

    • News: Xcode 6-beta 3, new iPod touch devices, Rob Pardo leaving Blizzard
    • What’s new in raywenderlich.com: Tutorial highlights from past month
    • Tech Talk: Adaptive UI in iOS 8 and Xcode 6

    Links and References

    Our Sponsor

    • Syncano: Making it easier to synchronize and scale data across devices and servers – in real time.

    News

    What’s New on raywenderlich.com

    Tech Talk: Adaptive UI

    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!

    Adaptive UI: The raywenderlich.com Podcast Episode 8 is a post from: Ray Wenderlich

    The post Adaptive UI: The raywenderlich.com Podcast Episode 8 appeared first on Ray Wenderlich.

    How to Make a Game Like Jetpack Joyride in Unity 2D – Part 3

    $
    0
    0

    This is the final part of How to create a game like Jetpack Joyride in Unity 2D tutorial series. If you’ve missed previous parts you better to complete them first. Here they are: Part 1 and Part 2.

    As I’ve mentioned at the end of the previous part this part has all the fun. This will be the reward for going this far :]

    In this part you will add lasers, coins, sound effects, music and even parallax scrolling. So enough talking lets start the fun!

    Getting Started

    You can continue using the project you created in the second part or alternatively you can download the starter project for this part. They should be almost identical.

    If you want to download the starter project use this link: RocketMouse_Final_Part2

    When you’re ready open the RocketMouse.unity scene and let’s begin!

    Adding Lasers

    The mouse flying through the room is great, but what is the challenge of this game? It is time to add some obstacles, and what can be cooler than lasers> ;]

    Lasers will be generated randomly, in a similar manner as you generate rooms, so you need to create a Prefab. Also you will need to create a small script controlling the laser.

    Creating Laser

    Here are the steps required to create a laser object:

    1. In the Project view find laser_on sprite and drag it to the scene.

      Note: Since laser Prefab will consist of only the laser itself you don’t have to position it at the origin or something like this.

    2. Select it in the Hierarchy and rename it to laser.
    3. Set its Sorting Layer to Objects.
    4. Add Box Collider 2D component.
    5. Enable Is Trigger property in Box Collider 2D component.

      Note: When the Is Trigger property is enabled collider will trigger collision events, but will be ignored by the physics engine. In other words if the mouse touches the laser you will get notified. However, the laser will not block the mouse movement.

      It is very convenient for many reasons. For example if the mouse dies on top of the laser it wont hang in the air lying on the laser. Also the mouse can still move a little forward after hitting the laser due to inertia, instead of bouncing back from the laser

      Besides that, real lasers are not some hard objects, so by enabling this property you’re just simulating the real laser.

    6. Set Size of collider, X to 0.18 and Y to 3.1.

      Note: This creates a collider only where the laser is, leaving emitters on both ends absolutely safe.

    7. Create new C# Script named LaserScript and attach it to laser.

    Here is the full list of steps displayed:

    rocket_mouse_unity_p3_40

    Turning laser On and Off from the Script

    Open LaserScript in MonoDevelop and add following instance variables:

    //1
    public Sprite laserOnSprite;    
    public Sprite laserOffSprite;
     
    //2    
    public float interval = 0.5f;    
    public float rotationSpeed = 0.0f;
     
    //3
    private bool isLaserOn = true;    
    private float timeUntilNextToggle;

    It might seem like a lot of variables, but in fact everything is quite trivial.

    1. The Laser is going to be in two states: On and Off. There is a separate image for each state. You will specify each state image in just a moment.
    2. These properties allow you to add a bit of random fluctuation. You can set a different interval so that all lasers on the level didn’t work exactly the same. By setting a low interval you will create a laser that will turn On and Off real quick, and by setting a high interval you will create a laser that will stay in its state for quite long, and who knows, maybe the mouse can even fly trough the laser when it is Off.

      The rotationSpeed variable serves similar purpose. It specifies the speed of the laser rotation.

    3. Finally there are two private variables that are used to toggle the laser state.

    Here is an example of lasers. Each has a different interval and rotationSpeed.

    rocket_mouse_unity_p3_41

    Add the following code in Start:

    timeUntilNextToggle = interval;

    This will set the time until the laser should toggle its state for the first time.

    To toggle and rotate the laser add FixedUpdate with following:

    void FixedUpdate () {
        //1
        timeUntilNextToggle -= Time.fixedDeltaTime;
     
        //2
        if (timeUntilNextToggle <= 0) {
     
            //3
            isLaserOn = !isLaserOn;
     
            //4
            collider2D.enabled = isLaserOn;
     
            //5
            SpriteRenderer spriteRenderer = ((SpriteRenderer)this.renderer);
            if (isLaserOn)
                spriteRenderer.sprite = laserOnSprite;
            else
                spriteRenderer.sprite = laserOffSprite;
     
            //6
            timeUntilNextToggle = interval;
        }
     
        //7
        transform.RotateAround(transform.position, Vector3.forward, rotationSpeed * Time. fixedDeltaTime);
    }

    Here is what that code does:

    1. Decreases the time left until next toggle.
    2. If timeUntilNextToggle is zero or even less then zero, it is time to toggle the laser state.
    3. Sets the correct state of the laser in the private variable.
    4. The laser collider is enabled only when the laser is On. This means that mouse can fly through the laser freely if it is Off.
    5. Casts the more general Renderer class to a SpriteRenderer since you know the laser is a Sprite. It also sets the correct laser sprite. This will display laser_on sprite when the laser is On and laser_off sprite when the laser is Off.
    6. Resets timeUntilNextToggle variable since the laser has just been toggled.
    7. Rotates the laser around the z-axis using its rotationSpeed.

    Note: To disable rotation you can just set rotationSpeed to zero.

    Setting the Laser Script Parameters

    Switch back to Unity and select laser in the Hierarchy. Make sure the Laser Script component is visible.

    Drag laser_on sprite from the Project view to the Laser On Sprite property of the Laser Script component in the Inspector.

    Then drag laser_off sprite to Laser Off Sprite property.

    Set Rotation Speed to 30.

    rocket_mouse_unity_p3_42

    Now set the laser Position at (2, 0.25, 0). This is to test that everything works correctly.

    Run the scene. You should see a laser nicely rotating.

    rocket_mouse_unity_p3_43

    Now, turn the laser into a prefab.

    Solution Inside: Need help creating a laser prefab? SelectShow>

    Killing the Mouse

    Right now the mouse can easily pass through the enabled laser without so much as a whisker getting bent. This is not a good example for kids. Kids should see the consequences of playing with lasers. :]

    Better get to fixing that.

    Open MouseController script and add dead instance variable.

    private bool dead = false;

    This instance variable indicates a dead mouse. Once this variable is true, you will not be able to activate the jetpack, move forward, and so on.

    Now add the following two methods somewhere within the MouseController class:

    void OnTriggerEnter2D(Collider2D collider)
    {
        HitByLaser(collider);
    }
     
    void HitByLaser(Collider2D laserCollider)
    {
        dead = true;
    }

    The OnTriggerEnter2D method is called when mouse collides with any laser. Currently, it just makes marks the mouse dead.

    Note: It might seem strange why you need to create a separate method for one line of code, but you will add more code to both OnTriggerEnter2D and HitByLaser so it is just a way to make future changes more convenient.

    Now, when the mouse is dead it shouldn’t move forward or fly using the jetpack. You’re not filming The Flying Dead, aren’t you? :]

    Make the following changes in FixedUpdate to make sure this doesn’t happen:

    void FixedUpdate () 
    {
        bool jetpackActive = Input.GetButton("Fire1");
     
        jetpackActive = jetpackActive && !dead;
     
        if (jetpackActive)
        {
            rigidbody2D.AddForce(new Vector2(0, jetpackForce));
        }
     
        if (!dead)
        {
            Vector2 newVelocity = rigidbody2D.velocity;
            newVelocity.x = forwardMovementSpeed;
            rigidbody2D.velocity = newVelocity;
        }
     
        UpdateGroundedStatus();
     
        AdjustJetpack(jetpackActive);
    }

    Note that now jetpackActive is always false when the mouse is dead. This means that no upward force will be applied to the mouse and also, since jetpackActive is passed to AdjustJetpack, the particle system will be disabled.

    In addition to that you don’t set the mouse velocity if its dead, which is pretty obvious thing to do.

    Switch back to Unity and run the scene. Make the mouse fly into the laser.

    rocket_mouse_unity_p3_45

    Hm.., it looks like you can no longer use the jetpack and the mouse doesn’t move forward, but why is the mouse running like crazy?

    Guesses? Anyone? Bueller? Bueller?

    The reason for this strange behavior is that you have two states for the mouse: run and fly, and when the mouse falls down on the floor it becomes grounded so run animation is activated.

    Since the game cannot end like this, you need to add few more states to show that the mouse is dead.

    Adding the Fall and Die Mouse Animations

    Select mouse GameObject in the Hierarchy and open the Animation view.

    Create new animation called die. Save new animation to Animations folder.

    rocket_mouse_unity_p3_46

    After that, follow these steps to complete the animation:

    1. Open Sprites folder in the Project view.
    2. Select and drag mouse_die_0 and mouse_die_1 sprites to the Animation view’s timeline.
    3. Set Samples to 8 to make the animation slower.
    4. Note that recording mode is on. The easiest way to notice this is by looking at playback buttons, which has turned red. Click on the recording button to stop the recording. This will make the playback buttons to return to normal color.

    rocket_mouse_unity_p3_47

    That was easy. In fact I think you can create fall animation yourself. This time just use the mouse_fall sprite as a single frame. However, if you get stuck feel free to expand the section below for detailed instructions.

    Solution Inside: Need help creating fall animation? SelectShow>

    Transitioning to Fall and Die Animations

    After creating the animations, you need to make the Animator switch to the corresponding animation at the right time. To do this, you’re going to transition from a special state called Any State, since it doesn’t matter what the current state the mouse is currently in, when it hits the laser.

    Since you created two animations(fall and die), there is a difference if the mouse hits the laser in the air or while running on the ground. In the first case, the mouse should switch to the fall animation state and only after hitting the ground play die animation.

    However, in both cases you need one new parameter. Open the Animator view and create new Bool parameter called dead.

    rocket_mouse_unity_p3_52

    After this Make Transition from Any State to fall.

    rocket_mouse_unity_p3_53

    Select this transition and in Conditions, set dead to true and add grounded as a second parameter by clicking +. Set grounded to false.

    rocket_mouse_unity_p3_54

    Select this transition and in Conditions set both dead and grounded parameters to true.

    rocket_mouse_unity_p3_60

    This way there are two possible combinations:

    1. dead but not grounded
    2. dead and grounded

    rocket_mouse_unity_p3_55

    This way, if the mouse is dead, but still in the air (not grounded) the state is switched to fall. However, if the mouse is dead and grounded, or was dead and becomes grounded after falling to the ground, the state is switched to die.

    The only thing left to do is update the dead parameter from MouseController script.

    Open MouseController script and add the following line to the end of HitByLaser:

    animator.SetBool("dead", true);

    This will set the dead parameter of the Animator component to true.

    Run the scene and fly into the laser.

    rocket_mouse_unity_p3_56

    As you can see, when the mouse hits the laser, the script sets a dead parameter to true and the mouse switches to fall state (since grounded is still false). However, when the mouse reaches the floor, the script sets grounded parameter to true. Now, all conditions are met to switch to die state.

    Using the Trigger to Make the Mouse Die Once

    There is an old saying that you only can die once, however the mouse right now is dying all the time. You can check this yourself by looking at Animator view after the mouse dies.

    rocket_mouse_unity_p3_57

    This happens because the transition from Any State to die happens all the time. The grounded and dead parameters are always true, which triggers the animator to transition from Any State

    To fix this, you use a special parameter type called Trigger. Trigger parameters are very similar to Bool, with the exception that they are automatically reset after used. This is a relatively new featured added in Unity 4.3.

    Open the Animator view and add a new Trigger parameter called dieOnceTrigger. Set its state to On, by checking the checkbox next to it.

    rocket_mouse_unity_p3_58

    Next, select the transition from Any State to die, and add dieOnceTrigger in the Conditions section.

    Run the scene and fly into the laser once again.

    rocket_mouse_unity_p3_61

    I wouldn’t say that this makes things better, but fortunately this is very easy to fix. This happens only because die animation is set to loop by default.

    Open Animations folder in the Project view and select animation. In the Inspector uncheck Loop Time. This disables the animation looping.

    rocket_mouse_unity_p3_62

    Run the scene and collide with a laser.

    rocket_mouse_unity_p3_63

    This time the mouse stays on the floor after dying.

    Adding Coins

    While death dealing lasers are fun to implement, how about adding some coins for mouse to collect.

    Creating Coin Prefab

    Creating a coin Prefab is so easy and similar to creating laser so you should try doing this yourself. Just use the coin sprite and follow these tips:

    • Don’t create any scripts for coin.
    • Use Circle Collider 2D instead of Box Collider 2D.
    • Enable Is Trigger option for the collider, since you don’t want the coin to stop the mouse movement.

    If you have any questions just take a look at the expandable section below.

    Solution Inside: Creating coin prefab SelectShow>

    After creating a coin GameObject, drag it from the Hierarchy and into the Prefabs folder in the Project view to create a coin Prefab.

    rocket_mouse_unity_p3_65

    Now add several coins to the scene by dragging coin Prefabs to the Scene view. Create something like this:

    rocket_mouse_unity_p3_66

    Run the scene.

    rocket_mouse_unity_p3_67

    Wait a second – the mouse died the moment it touched the coins? Are they poisoned?

    rocket_mouse_unity_p3_68

    No, the coins are okay. The mouse dies because of the code in MouseController script, which handles any collision as a collision with a laser.

    Using Tags to Distinguish Coins from Lasers

    To distinguish coins from lasers you will use Tags, which are made exactly for that purpose.

    Select coin Prefab right in Prefabs folder in the Project view. This will open the Prefab properties in the Inspector. Find the Tag dropdown right below the name field, open it, and chose Add Tag…..

    rocket_mouse_unity_p3_69

    This will open the already familiar Tags & Layers editor in the Inspector. In the Tags section add a tag named Coins.

    Note: It will automatically increase the Size to 2 and add Element 1, but that’s ok.

    Now select coin Prefab in the Project view once again and set its Tag to Coins in the Inspector.

    rocket_mouse_unity_p3_71

    Of course just setting the Tag property doesn’t make the script to distinguish coins from lasers, you’ll still need to modify some code.

    Updating MouseController Script to use Tags

    Open the MouseController script and add a coins counter variable:

    private uint coins = 0;

    This is where you’ll store the coin count.

    Then add CollectCoin method:

    void CollectCoin(Collider2D coinCollider)
    {
        coins++;
     
        Destroy(coinCollider.gameObject);
    }

    This method increases the coin count and removes the coin from the scene so that you don’t collide with it a second time.

    Finally, make following changes in OnTriggerEnter2D:

    void OnTriggerEnter2D(Collider2D collider)
    {
        if (collider.gameObject.CompareTag("Coins"))
            CollectCoin(collider);
        else
            HitByLaser(collider);
    }

    With this change, you call CollectCoin in case of a coin and HitByLaser in all other cases.

    Note: In this game there are only two types of objects so it is okay to use else case for lasers. In a real game, you should assign tags for all object types and check them implicitly.

    Run the scene.

    rocket_mouse_unity_p3_72

    Now that’s much better. The mouse collects coins and dies if it hits a laser. It looks like you’re ready to generate lasers and coins using a script.

    Generating Coins and Lasers

    Generating coins and lasers is similar to what you did when you generated rooms. The algorithm is almost identical, but before writing the code, you need to improve the coin generation so as to make it more fun for the player.

    Currently you have a Prefab that consists of only one coin, so if you write generation code you will simply generate only one coin here and there on the level. This is not fun! How about creating different figures from coins and generating a pack of coins at once?

    Creating Pack of Coins Prefab

    Open the Prefabs folder in the Project viewer and create 9 coins on the scene using the coin Prefab. It should look something like this:

    rocket_mouse_unity_p3_73

    Select any coin and set its Position to (0, 0, 0). This will be central coin. You will add all coins into Empty GameObject, so you need to build your figure around the origin.

    After placing the central coin, build a face down triangle shaped figure around the coin. Don’t forget that you can use Vertex Snapping by holding V key.

    rocket_mouse_unity_p3_74

    Now create an Empty GameObject by choosing GameObject\Create Empty. Select it in the Hierarchy and rename it to coins_v.

    Set its Position to (0, 0, 0) so that it has the same position as the central coin. After that select all coins in the Hierarchy and add them to coins_v. You should get something like this in the Hierarchy:

    rocket_mouse_unity_p3_75

    Select coins_v in the Hierarchy and drag it to Prefabs folder in the Project view to create Prefab.

    rocket_mouse_unity_p3_76

    Note: You can create as many different coins combinations as you want, just as with rooms, the generator script will provide a property where you will specify all the possible objects to generate.

    You’re done. Now remove all the coins and lasers from the scene since they will be generated by the script.

    Adding New Parameters to GeneratorScript

    Open GeneratorScript and add following instance variables:

    public GameObject[] availableObjects;    
    public List<GameObject> objects;
     
    public float objectsMinDistance = 5.0f;    
    public float objectsMaxDistance = 10.0f;
     
    public float objectsMinY = -1.4f;
    public float objectsMaxY = 1.4f;
     
    public float objectsMinRotation = -45.0f;
    public float objectsMaxRotation = 45.0f;

    The availableObjects array, will hold all objects that the script can generate (i.e. different coins packs and laser). The objects list will store the created objects, so that you could check if you need to add more ahead of the player or remove them when they have left the screen.

    Note: Just as with rooms, you can create several lasers or coins at the beginning of the level where you don’t want to rely on random generation code. Just don’t forget to add them to objects list.

    The variables objectsMinDistance and objectsMaxDistance are used to pick a random distance between the last object and the currently added object, so that the objects don’t appear at fixed interval.

    By using objectsMinY and objectsMaxY you can configure the maximum and minimum height at which objects are placed, and by using objectsMinRotation and objectsMaxRotation you can configure the rotation range.

    Adding The Method to Add New Object

    New objects are added in AddObject in a similar way to how rooms are added.

    Add the following:

    void AddObject(float lastObjectX)
    {
        //1
        int randomIndex = Random.Range(0, availableObjects.Length);
     
        //2
        GameObject obj = (GameObject)Instantiate(availableObjects[randomIndex]);
     
        //3
        float objectPositionX = lastObjectX + Random.Range(objectsMinDistance, objectsMaxDistance);
        float randomY = Random.Range(objectsMinY, objectsMaxY);
        obj.transform.position = new Vector3(objectPositionX,randomY,0); 
     
        //4
        float rotation = Random.Range(objectsMinRotation, objectsMaxRotation);
        obj.transform.rotation = Quaternion.Euler(Vector3.forward * rotation);
     
        //5
        objects.Add(obj);            
    }

    This method takes the position of the last (rightmost) object and creates a new object at the random position (within given interval) after it. By calling this method, each time the last object is about to show on the screen – you create a new object off screen and keep an endless flow of new coins and lasers.

    Here is the description of each code block:

    1. Generates a random index for the object to generate. This can be a laser or one of the coin packs.
    2. Creates an instance of the object that was just randomly selected.
    3. Sets the object’s position, using a random interval and a random height. This is controlled by script parameters.
    4. Adds a random rotation to the newly placed objects.
    5. Adds the newly created object to the objects list for tracking and ultimately, removal (when it leaves the screen).

    With the code in place, the only thing left to do is actually use it.

    Generating and Removing Objects When Required

    Add the following in the GeneratorScript:

    void GenerateObjectsIfRequired()
    {
        //1
        float playerX = transform.position.x;        
        float removeObjectsX = playerX - screenWidthInPoints;
        float addObjectX = playerX + screenWidthInPoints;
        float farthestObjectX = 0;
     
        //2
        List<GameObject> objectsToRemove = new List<GameObject>();
     
        foreach (var obj in objects)
        {
            //3
            float objX = obj.transform.position.x;
     
            //4
            farthestObjectX = Mathf.Max(farthestObjectX, objX);
     
            //5
            if (objX < removeObjectsX)            
                objectsToRemove.Add(obj);
        }
     
        //6
        foreach (var obj in objectsToRemove)
        {
            objects.Remove(obj);
            Destroy(obj);
        }
     
        //7
        if (farthestObjectX < addObjectX)
            AddObject(farthestObjectX);
    }

    This method checks if an object should be added or removed somewhere within GeneratorScript class. Here’s the breakdown:

    1. Calculates key points ahead and behind the player.

      If the laser or coin pack is to the left of removeObjectsX, then it has already left the screen and is far behind. You will have to remove it.

      If there is no object after addObjectX point, then you need to add more objects since the last of the generated objects is about to enter the screen.

      The farthestObjectX variable is used to find the position of the last (rightmost) object to compare it with addObjectX.

    2. Since you cannot remove objects from the currently iterating, you place objects that you need to remove in a separate array to be removed after the loop.
    3. This is the position of the object (coin pack or laser).
    4. By executing this code for each objX you get a maximum objX value in farthestObjectX at the end of the cycle (or the initial value of 0, if all objects are to the left of origin, but not in our case)
    5. If current object is far behind, it is marked removal to free some resources.
    6. Removes objects marked for removal.
    7. If the player is about to see the last object and there are no more objects ahead, the scripts adds more.

    To make this method work, add a call to GenerateObjectsIfRequired at the end of FixedUpdate:

    GenerateObjectsIfRequired();

    This method is called with each fixed update, insuring that there will always be objects ahead of the player.

    Setting up Script Parameters

    To make the GeneratorScript work, you need to set few of its parameters. Switch back to Unity and select the mouse GameObject in the Hierarchy.

    Find the Generator Script component in the Inspector and make sure that the Prefabs folder is opened in the Project view.

    Drag the coins_v Prefab from the Project view to the Available Objects list in the GeneratorScript component. After that, drag the laser Prefab from the Project view also to the Available Objects list in the GeneratorScript.

    rocket_mouse_unity_p3_78

    That’s it! Run the scene.

    Note: In the animated GIF, the lasers are not rotating because I set rotationSpeed parameter of LaserScript to 0. With rotating lasers, it is quite hard to record a good gameplay video :]

    rocket_mouse_unity_p3_79

    Now this looks like an almost complete game!

    Adding GUI Elements

    What’s the point of collecting coins if you can’t see how many coins that you have collected? Also, There’s no way for the player to restart the game once they have died. It’s time to fix these issues by adding a couple of GUI elements.

    Displaying Coins Count

    Open the MouseController script in MonoDevelop and add the following instance variable:

    public Texture2D coinIconTexture;

    Then add DisplayCoinsCount method:

    void DisplayCoinsCount()
    {
        Rect coinIconRect = new Rect(10, 10, 32, 32);
        GUI.DrawTexture(coinIconRect, coinIconTexture);                         
     
        GUIStyle style = new GUIStyle();
        style.fontSize = 30;
        style.fontStyle = FontStyle.Bold;
        style.normal.textColor = Color.yellow;
     
        Rect labelRect = new Rect(coinIconRect.xMax, coinIconRect.y, 60, 32);
        GUI.Label(labelRect, coins.ToString(), style);
    }

    This method uses GUI.DrawTexture to draw a coin icon at the top left corner of the screen. Then it creates a GUIStyle for the label to change its size, bolds the text, then changes the text color to yellow. Or should I say gold? :]

    Finally, it uses GUI.Label to display the amount of coins to the right of the coins icon.

    All code to display GUI elements should be called from OnGUI method that is called by Unity. So go ahead and add the OnGUI method that simply calls DisplayCoinsCount.

    void OnGUI()
    {
        DisplayCoinsCount();
    }

    Switch back to Unity and select mouse GameObject in the Hierarchy. Open Sprites folder in the Project view and drag coin sprite to Coin Icon Texture field of Mouse Controller component in the Inspector.

    rocket_mouse_unity_p3_80

    Run the scene. You should see coins count displayed in top left corner.

    rocket_mouse_unity_p3_81

    Raising the Dead

    Open MouseController script again, but this time add DisplayRestartButton method:

    void DisplayRestartButton()
    {
        if (dead && grounded)
        {
            Rect buttonRect = new Rect(Screen.width * 0.35f, Screen.height * 0.45f, Screen.width * 0.30f, Screen.height * 0.1f);
            if (GUI.Button(buttonRect, "Tap to restart!"))
            {
                Application.LoadLevel (Application.loadedLevelName);
            };
        }
    }

    Most of this method is only executed when mouse GameObject is dead and grounded. You don’t want the player to miss the mouse dripping dead, especially after you put so much efforts creating those animations :]

    When the mouse is dead and grounded, you display a button right in the center of the screen with a Tap to restart! label on it. If the player taps this button, you simply reload the currently loaded scene.

    Now add a call to DisplayRestartButton at the end of OnGUI and you’re done:

    DisplayRestartButton();

    Switch to Unity and Run the scene. Fly into some laser to kill the mouse. When the button appears, tap it, and you will restart the game.

    rocket_mouse_unity_p3_82

    Note: You can customize how button looks by creating a public instance variable like this:

    public GUIStyle restartButtonStyle;

    Then you can use customize the button’s look and feel.

    rocket_mouse_unity_p3_83

    You can find out more information about customizing the button’s look and feel by checking out the Unity documentation. Keep in mind that with the arrival of Unity 4.6, the New GUI will be released which provides an entirely new way of working with Unity’s GUI tools. You can find out more about it over here

    Adding Sound and Music

    The game is deadly quiet. You will be amazed how much better it play once add some sounds and music to it.

    Hitting Laser Sound

    Open Prefabs folder in the Project view and select laser Prefab.

    rocket_mouse_unity_p3_86

    In the Inspector, add an Audio Source component by clicking Add Component and selecting Audio\Audio Source. Then open the Audio folder in the Project view and drag laser_zap sound to Audio Clip field.

    Don’t forget to uncheck Play On Awake. Otherwise the laser zap sound will be played right at the start of the game, just as if the mouse started on the laser.

    Oh, that would be a cruel game if the player character died right at the start and you couldn’t do anything about it. Maybe it will even break all the records in the AppStore since it is that much harder then Flappy Bird :]

    This is what you should get:

    rocket_mouse_unity_p3_85

    Note: Make sure you select the laser Prefab not the laser instance in the Hierarchy. Otherwise you will need additionally to click Apply button to save changes made in instance to Prefab.

    Now open MouseController script in MonoDevelop and add following code to the beginning of HitByLaser:

    if (!dead)
       laserCollider.gameObject.audio.Play();

    Note: It is important to add it to the beginning of the method, before you set dead to true or otherwise it won’t be played even once.

    When the mouse touches the laser, you get a reference to the laser’s collider in OnTriggerEnter2D. By accessing gameObject property of laserCollider you then get the laser object itself. Then, you can access the Audio Source component, and make it play.

    Run the scene. You will now hear a zap sound when the mouse hits any laser.

    rocket_mouse_unity_p3_87

    However, the zap sound is too quiet and if you have a stereo speakers or headphones, the sound is played a bit t to the left. This happens because Audio Listener component is placed on Main Camera by default, and the Audio Source component is placed on laser. Fortunately, this is an easy issue to fix.

    Note: Don’t forget that Unity was originally developed to create 3D games. In 3D games, you need 3D sounds (e.g. to hear that someone shoots at you from the side or even from the back).

    However, although you still might want to use 3D sound in some 2D games, most of the time you just want the sound played at the same volume independently from where the audio source is placed.

    Disabling 3D Sound Mode

    Open Audio folder in the Project view and select laser_zap file. This will open the Import Settings editor in the Inspector. Uncheck the 3D Sound option and click Apply.

    rocket_mouse_unity_p3_88

    Do the same for the rest audio files:

    • coin_collect
    • footsteps
    • jetpack_sound
    • music

    Collecting Coin Sound

    While you could apply the same approach with coins, you’ll be something a little bit different.

    Open the MouseController script in MonoDevelop and add following instance variable:

    public AudioClip coinCollectSound;

    Scroll down to the CollectCoin method and add following line of code at the end of the method:

    AudioSource.PlayClipAtPoint(coinCollectSound, transform.position);

    This way you use a static method of the AudioSource class to play the coin collect sound at the position where the mouse is currently located.

    Switch back to Unity and select the mouse GameObject in the Hierarchy. Drag the coin_collect from the Project view to the Coin Collect Sound field in MouseController script.

    rocket_mouse_unity_p3_89

    Run the scene. You should hear a nice sound when collecting a coin :]

    rocket_mouse_unity_p3_90

    Jetpack and Footsteps Sound

    Next, you need to add the sound of the jetpack and the mouse’s footsteps when it is running on the floor. This will be just a little bit different since mouse will have to have two Audio Source components at once.

    Adding Audio Sources

    Select the mouse GameObject in the Hierarchy and add two Audio Source components. Drag footsteps from the Project view to the Audio Clip of the first Audio Source component. Then drag jetpack_sound to Audio Clip field of the second Audio Source component.

    Enable Play On Awake and Loop for both Audio Sources.

    rocket_mouse_unity_p3_91

    If you run the scene, you will hear that both sounds are playing all the time, independently of whether the mouse is flying or running on the floor. You’ll fix this in code.

    Switching Between Footsteps and Jetpack Sounds

    Open MouseController script in MonoDevelop and add two following instance variables:

    public AudioSource jetpackAudio;
     
    public AudioSource footstepsAudio;

    These reference your newly created Audio Sources.

    Now add AdjustFootstepsAndJetpackSound method:

    void AdjustFootstepsAndJetpackSound(bool jetpackActive)    
    {
        footstepsAudio.enabled = !dead && grounded;
     
        jetpackAudio.enabled =  !dead && !grounded;
        jetpackAudio.volume = jetpackActive ? 1.0f : 0.5f;        
    }

    This method enables and disables the footsteps and the jetpack Audio Source component. The footsteps sound is enabled when the mouse is not dead and on the ground, the jetpack sound only when the mouse is not dead and not on the ground.

    In addition, this method also adjusts the jetpack volume, so that it corresponds with the particle system

    Finally add a call to AdjustFootstepsAndJetpackSound at the end of FixedUpdate:

    AdjustFootstepsAndJetpackSound(jetpackActive);

    Now you need to assign references to Audio Source components within mouse GameObject to footstepsAudio and jetpackAudio variables.

    Setting Footstep and Jetpack Script Variables

    Switch back to Unity and select mouse GameObject in the Hierachy. You’re going to work only within the Inspector window. Collapse all components except Mouse Controller.

    rocket_mouse_unity_p3_92

    Now drag the top Audio Source component to Footsteps Audio in the Mouse Controller script component.

    Note: In my case I know that the first Audio Source in the Inspector is the footsteps sound clip, but you might want temporarily expand the Audio Source component to check this.

    rocket_mouse_unity_p3_93

    After that drag the second Audio Source component to the Jetpack Audio in the Mouse Controller script component.

    rocket_mouse_unity_p3_94

    Run the scene. Now you should hear the footsteps when the mouse is running on the floor and jetpack engine when it’s flying. Also the jetpack sound should become stronger when you enable jetpack by holding the left mouse button.

    rocket_mouse_unity_p3_95

    Adding Music

    To add music just follow these simple steps:

    1. Select Main Camera in the Hierarchy.
    2. Add an Audio Source component in the Inspector.
    3. Drag music asset from the Project browser to Audio Clip property.
    4. Make sure Play On Awake and Loop are enabled.
    5. Decrease the Volume to 0.3, since the music is quite loud compared to other sounds.

    rocket_mouse_unity_p3_96

    That’s it. Run the scene and enjoy some music!

    Adding Parallax Background

    Currently this room with a view is pretty boring.

    rocket_mouse_unity_p3_97

    However, there are two ways to solve it:

    1. Create something to show behind the window.
    2. Don’t use windows :]

    Of course you’ll go with the first one, but instead of adding a motionless background image you will add a parallax background.

    Note: To implement parallax scrolling for this game I used the technique described in one of the Mike Geig’s videos that you can watch over here. The link is provided in the end of this part.

    Here is how things will work. You will add two Quads, one for background and one for foreground parallax layer.

    Note: You can read more about Quads in Unity documentation. To simplify things a little, you can think of them as just a rectangles with a texture stretched on it. Well, at least in this case.

    You might wonder why do you need to use Quad instead of a typical Sprite? The reason for this is that you can’t change the Sprite’s image wrapping mode. Well, at least at the moment of writing this tutorial. And we need to change the wrapping mode to make sure the texture is endlessly repeated while we moving it to the left. It will get clearer in a moment.

    You will set a texture for each quad, and instead of moving quads to simulate movement you will simply move the textures within the quad at a different speed for the background and the foreground layer.

    Preparing Background Images

    To use background images with quads you need to adjust how they are imported to Unity.

    Open Sprites folder in the Project view and select window_background. In the Inspector change its Texture Type to Texture instead of Sprite. This will change how the look of the Inspector.

    After that change Wrap Mode that just appeared to Repeat. Click Apply.

    rocket_mouse_unity_p3_98

    Do the same for window_foreground image.

    rocket_mouse_unity_p3_99

    Creating Another Camera

    Wait, what, another camera? The Main Camera is reserved for following the mouse through the level. This new camera will render the parallax background and won’t move.

    Create new camera by selecting GameObject\Create Other\Camera. Select it in the Hierarchy and make following changes in the Inspector:

    1. Rename to ParallaxCamera.
    2. Set Position to (0, 10, 0)
    3. Set Projection to Orthographic
    4. Set Size to 3.2, the same size as Main Camera has.

    rocket_mouse_unity_p3_100

    Since you have two cameras you also have 2 audio listeners in the scene. Disable Audio Listener in ParallaxCamera or you will get following warning.

    There are 2 audio listeners in the scene. Please ensure there is always exactly one audio listener in the scene.

    rocket_mouse_unity_p3_115

    Creating Quads

    Create two Quad objects by choosing GameObject\Create Other\Quad. Name the first quad parallaxBackground and the second name parallaxForeground. Drag both quads to ParallaxCamera to add them as children.

    rocket_mouse_unity_p3_101

    Select parallaxBackground and change its Position to (0, 10, 10) and Scale to (11.36, 4.92, 0).

    Note: You get this scale because background images have size of 1136 × 492 px.

    rocket_mouse_unity_p3_102

    Select parallaxForeground and set its Position to (0, 10, 9) and Scale to (11.36, 4.92, 0).

    rocket_mouse_unity_p3_103

    Note: This time you set the z-coordinate position. Since you cannot use Sorting Layers for quads you need to set the background quad behind the foreground quad.

    Setting Quad Textures

    Open Sprites folder in the Project view. Drag the window_background over to the parallaxBackground and window_foreground over parallaxForeground in the Hierarchy.

    rocket_mouse_unity_p3_104

    Then select parallaxForeground in the Hierarchy. You will see that a Mesh Renderer component was added. Click on the Shader drop down and select Unlit\Transparent.

    rocket_mouse_unity_p3_105

    Do the same for parallaxBackground.

    rocket_mouse_unity_p3_106

    This is what you should see in the Scene view right now.

    rocket_mouse_unity_p3_107

    If you disable 2D mode and rotate a scene a little, this is how things are in the scene right now.

    rocket_mouse_unity_p3_108

    Run the scene. You will see that the background is in front of the main level. This is useful so you can see how the textures move with ParallaxScrolling. Once you have the textures moving, you will move it back to the background.

    rocket_mouse_unity_p3_109

    Making Textures Move

    You will not move the Quads. Instead, you’re going to move the textures of the quads by changing the texture offset. Since you set the Wrap Mode to Repeat the texture will repeat itself.

    Note: This doesn’t won’t work with any image. They background images designed to be repeated. In other words if you repeat the background horizontally many times and each left side of the picture will perfectly fit the ride side.

    Create a new C# Script called ParallaxScroll and attach it to ParallaxCamera.

    rocket_mouse_unity_p3_110

    Open ParallaxScript in MonoDevelop and add the following instance variables:

    public Renderer background;
    public Renderer foreground;
     
    public float backgroundSpeed = 0.02f;
    public float foregroundSpeed = 0.06f;

    The Renderer variables will hold a reference to the Mesh Renderer component of each of the quads so that you could adjust their texture properties. The backgroundSpeed and foregroundSpeed just define speed for each background.

    Add following code to Update:

    float backgroundOffset = Time.timeSinceLevelLoad * backgroundSpeed;
    float foregroundOffset = Time.timeSinceLevelLoad * foregroundSpeed;
     
    background.material.mainTextureOffset = new Vector2(backgroundOffset, 0);
    foreground.material.mainTextureOffset = new Vector2(foregroundOffset, 0);

    This code increases the texture offset of each the quad’s texture with time, thus moving it. The speed is different since the script uses backgroundSpeed and foregroundSpeed coefficients.

    Switch back to Unity and select ParallaxCamera in the Hierarchy. Drag the parallaxBackground quad to Background field of the ParallaxScroll script and parallaxForeground to Foreground.

    rocket_mouse_unity_p3_111

    Run the scene. You will now see some nice parallax scrolling.

    rocket_mouse_unity_p3_112

    But what about the level itself? You can’t see it!

    Fixing the Order of Cameras

    Select ParallaxCamera in the Hierarchy. In the Inspector, find Camera component and look for a Depth field, set it to -2.

    Note: The Depth of the ParallaxCamera should be lower then Depth of Main Camera, so check your Main Camera Depth if required and adjust Depth of ParallaxCamera to be lower.

    rocket_mouse_unity_p3_113

    However, if you run the game right now you won’t see parallax background through the window.

    rocket_mouse_unity_p3_114

    To fix this, select Main Camera in the Hierarchy and set its Clear Flags to Depth Only. This way it won’t clear out the picture drawn by parallax camera.

    rocket_mouse_unity_p3_116

    Run the scene. Now you will see parallax background through the window.

    rocket_mouse_unity_p3_117

    Few Improvements

    Although you can see the treetops and clouds throughout the window, it is better to move background quads a bit higher, so that you can see the hills.

    Another thing to improve is that the background keeps moving even after the mouse dies.

    Stoping Parallax Scrolling After the Mouse Dies

    Open ParallaxScroll script in MonoDevelop and add public offset variable.

    public float offset = 0;

    You will use it instead of Time.timeSinceLevelLoad, so in Update replace the code where you calculate offsets with this:

    float backgroundOffset = offset * backgroundSpeed;
    float foregroundOffset = offset * foregroundSpeed;

    Now open MouseController script and add following public variable:

    public ParallaxScroll parallax;

    Then add following code to the end of FixedUpdate:

    parallax.offset = transform.position.x;

    This way you will use the mouse position as offset instead of time.

    Switch back to Unity and select mouse GameObject in the Hierarchy. Make sure MouseController script is visible in the Inspector.

    Drag ParallaxCamera from the Hierarchy to Parallax field in the Inspector.

    rocket_mouse_unity_p3_119

    This will allow MouseController script to change the offset variable of ParallaxScroll script.

    Moving Background a bit higher

    Set Y component of Position of both parallaxBackground and parallaxForeground quads to 0.7. This will make them a little higher so that you can see the hills.

    rocket_mouse_unity_p3_120

    Run the scene. Now the background stops moving as soon as the mouse dies, and you can see more background through the window.

    Note: In fact, the mouse doesn’t have to die in order to stop the background. As soon as the mouse stops, the background will also stop. If for some reason the mouse flies backward, the background will also move in the opposite direction.

    However, this is too sad an image to end this tutorial, so I decided to add a kitty :]

    rocket_mouse_unity_p3_121

    Where To Go from Here?

    I hope you liked this tutorial as much as I liked creating it.

    You can download the final project here: Download Final Project

    If you want to know more about the making of the actual Jetpack Joyride game check out this video.

    Creating a parallax background is heavily inspired by this video by Mike Geig (he has a lot of really cool videos on Unity).

    The cat image is made by Nicolas Suzor and you can find it on Flickr.

    Please post your questions and comments below. Thank you for completing this tutorial :]

    How to Make a Game Like Jetpack Joyride in Unity 2D – Part 3 is a post from: Ray Wenderlich

    The post How to Make a Game Like Jetpack Joyride in Unity 2D – Part 3 appeared first on Ray Wenderlich.

    Viewing all 4373 articles
    Browse latest View live


    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>