Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2009-305
$eventId
ID of event: wwdc2009
$eventContentId
ID of session without event part: 305
$eventShortId
Shortened ID of event: wwdc09
$year
Year of session: 2009
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2009] [Session 305] Game Develo...

WWDC09 • Session 305

Game Development for iPhone, Part 2

iPhone • 57:02

The iPhone SDK provides a phenomenal platform for mobile game development. Explore the technologies available to iPhone game developers and learn essential best practices for your titles. Understand how the best games harness the rich capabilities of iPhone OS and receive expert guidance for creating a compelling and entertaining experience of your own. This is the second of two sessions covering iPhone game development techniques.

Speaker: Allan Schaffer

Unlisted on Apple Developer site

Downloads from Apple

SD Video (126.8 MB)

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Well, good afternoon everybody and welcome back. My name is Allan Schaffer, I'm Apple's Graphics Evangelist. I'm going to continue on with Part 2 of our discussion of Game Development for iPhone. How many of you were here for the morning sessions? Just show of hands. Wow! OK, so just about everybody. I can just kind of recap. In the morning session I talked about OpenGL for graphics and we went through several of the I/O APIs, OpenAL, iPod Library Access, and a brief mention of the Audio Session API.

So here for Part 2, I'm actually just going to dive straight in. We're covering 3 topics here. The first is peer to peer connectivity then I'm going to go into some material that is very important for you to get right if you're using the accelerometer, so we'll take about 20 minutes for that, and then finally in-app purchase. So let's just dive straight in. Welcome back.

So first with peer-to-peer connectivity. So the Game Kit framework is a high-level API to help you support network gaming in your application. And there are 3 components to this API. The first is the Session part of the API, and this handles data communication over Bluetooth with another device, with no pairing needed, which is great because if you think about having to pair with another device just to play a game, that would really complicate some of the initiation of the game play experience. And the second part is a Peer Picker. So we provide you with some built-in user interface to help you discover other players on the Bluetooth network and look or-- on the online network as well.

And so, and help you to connect with them. Pick a player who you're going to play with and begin the game And then the third component is In-Game Voice for voice chat over the online network, so over the Internet. And you will use your own connection to set this up and then I'll show you some of the details about how this works.

So for the beginning part of this talk, I'm going to focus on using the Session API along with the Peer Picker for a fairly specific idea that we want to initiate a head-to-head communication with one other device using these APIs. So let's dive in and just first a brief mention about the session API. So the GKSession object is what provides you with the connection and handles the data communication between the 2 instances of your app running on these 2 devices.

It handles sending and receiving the data. It can be set up in either a topology of client/server or peer-to-peer. The session object is going to be tracking the state of the connection during the lifetime of the game. And the entire implementation is actually an Objective-C encapsulation of the networking functionality provided by Bonjour over Bluetooth.

And so, some really robust functionality, and this is how you're able to very easily discover other players because of the Bluetooth functional-- or excuse me, the Bonjour functionality built in. So, we're going to use the Session API in this instance along with the PeerPickerController to initiate the game play. And so these are just a few of the screens that you'll see as you're going through that process. But let me walk through just an example scenario here.

So we have, say, a dungeon-themed game and you have your Single Player Mode with the play, you also have a Challenge Mode where you can connect with another player and play head-to-head as you go through this dungeon campaign. Now, there are a couple of different options that the Peer Picker can support for you. The first is whether you want to have this game happen online, over the Internet, or locally, nearby over Bluetooth. If you select online, then actually you take over the connection and the networking code from that point.

You'll dismiss this Picker API and, excuse me, this user interface and handle all the progression from there. But let's say that you picked the Nearby Option for Bluetooth functionality. OK, so the first thing that might happen is that if you don't have Bluetooth turned on in your phone, it will actually present to you a dialogue here which will allow you to enable Bluetooth. So, this is great.

Just think about the fact that you're able to stay in your game, you don't have to pop back out of your game, go to the Settings, go through that and enable Bluetooth to-- and then come back into the game to restart again. You can handle all of that directly through the Peer Picker UI. So then once we have or assuming we have Bluetooth turned on or after you've turned it on with that interface, now the device is going to use the Bonjour stack to start discovering other devices that are nearby you.

And if we have another player who is also running a copy of your application, they might-- they'll be doing the same thing. And now these 2 devices can discover each other and you'll be presented with a control here that lets you choose a player to play with. So on the left that person John is going to see Jane's iPhone. Over here on the right, Jane is going to see John's iPhone.

And let's say that John over there selects Jane. OK. So he will be waiting now for Jane to respond and Jane is going-- Jane's instance of the application is going to show this dialogue saying that John's iPhone would like to connect and she can either accept or decline that connection.

If she accepts then great, you're connected, and you can now start the head-to-head game play within your application. So now, let's walk through a bit of the actual code, the steps that you'll use and the code that you'll write to actually make this happen. There are 5 major steps that you need to go through to set up this connection.

And the first is to just configure and present that UI control that I just showed you. Then secondly, the Peer Picker actually depends on you to provide to it a Session Object which it will use then to discover the other players and initiate the game play. And the method that you'll implement there is peerPickerController:sessionForConnectionType:. The third part here, all right, so now we have to handle the cases where the user actually wants to either connect or when they cancel out. And so there are 2 delegate methods here that you'll implement.

peerPickerController:didConnectPeer:toSession: or peerPickerController:didCancel:. OK. So now we're connected and we have to actually exchange data. So we'll implement the methods on the Session Object to send and receive data. So that's, you'll write a method to send data and then you'll also have a delegate method that will be invoked when you receive data from the network.

And then finally, you'll have to handle GKSession state changes for when, for example, the other player disconnects from you, and so you'll implement session:peer:didChangeState:. Now, I'm going to walk through some code here, but before I do and just like I mentioned in the first session, all of this code is available to you in samples that are online either on the main iPhone Dev Center or associated with sessions here at-- on the attendee website for WWC, so don't worry about trying to write all these code down, you can just go and download it later.

But watch the progression that we walked through here. So OK, so first we're going to present that PeerPickerController. So it's a simple alloc init and we set a delegate that is going to then receive those other delegate methods like to ask for a session object and so on. Now in this instance, I'm requesting to only support nearby connections. So in this particular scenario that I'm showing you is just for that case again of a head-to-head game being played over Bluetooth.

And then we show the picker to the user. All right. The second part now, as I mentioned, the picker actually requires for you to provide it with a session to handle all of the discovery, and it'll use that later also for all of the different state for the connection. So here we implement peerPickerController:sessionForConnectionType: and we'll create a Session Object.

So creating the Session Object, there are a few different parts of this. The first is the SessionID and this can be an arbitrary string that's specific to your application or what we actually recommend is that that be a registered Bonjour service type; and if you want to set that up, you can just visit this website here down below to register your protocol. The second element here, display name.

So by default, the Session API would just use the name of your device: Allan's iPhone, Jane's iPhone, John's iPhone, and so on. But if you have some kind of in-game handle, you can actually have that be what's presented to the other user by specifying it here. And then third, the Session Mode. So the PeerPickerController only supports the peer-to-peer connections, like if you were using the Session API directly, there would be other options for you here as well.

Then we release the session and now it's-- we're ready to go. So at that point in that-- remember the earlier sequence, at that point now the session is going to be used to begin discovering other players. So you'll see looking for other players in the net dialog come up showing Jane's iPhone and John's iPhone, and so on. Now when those 2 people connect, this method is going to be-- is going to fire. So this is peerPickerController:didConnectPeer:toSession. Now there are a few different steps that we're going to go through here.

First is just some memory bookkeeping, we're going to keep-- we're going to retain a reference to our session for later instead of delegate method which is going to receive the next series of notifications.

We also set a delegate now that is going to actually be the method that receives data.

So when we receive data from the session over the Bluetooth connection, this method will be invoked or a data receive handler will be invoked, and that will be where you actually extract the data and use it in your game. And finally, since now in this method we have now connected, and so we can dismiss the picker, release the delegate-- or set the delegate to NULL and release the picker memory.

So at this point, the connection is made and you can begin your game, OK? So that was step 3, and remember, there are 2 more. We need to now cover actually transferring some data back and forth. So to do that, we-- oh I apologize, I forgot one case here, that was if the session was-- or excuse me the picker did connect, you also should cover the case if you-- the user canceled out. So the basic steps here are just tear down the picker, tear down the session that was created, and probably return back to the main menu in your game, back to left and go, and go back into Challenge Mode if they want.

But all right, so now back to where I was. We need to exchange data between these 2 participants and you'll use the Session API directly to do this. So this first method is just one that you might write, mySendData:usingDataMode:. And within that method, to send data now, you'll call method on to Session Object which is sendDataToAllPeers:withDataMode and you can get an error value back.

So the data that you'll send over the connection is just an NSData object which makes it very easy for you to wrap up a number of different objects and have them transfer over-- over the connection. The Mode is actually very interesting. We support-- there are 2 different Modes that you might send data, either reliable or unreliable.

With reliable, what that means is that we'll actually attempt to retransmit any data that doesn't-- that isn't delivered to the other side. With unreliable, we won't worry about it, we'll just keep on-- keep on going. So unreliable might be appropriate, for example, if you are just continually sending say your position to the other player. If that data doesn't make it, you just don't worry about it.

You can just send them another position update rather than sending them an old one. Well, OK, so that's how you send the data. Now the companion to that of course is receiving data. So to receive data, this is a session delegate method that will be-- will fire when data comes to your application from the network.

It's receiveData:fromPeer:inSession with context. And so within this routine then, you will unpack that data which is your application-specific data set and use that to update your game state, do whatever application-specific coding you need to do there. But OK. So now we've initiated the connection, we're sending data back and forth and the final thing that we need to handle there are Session state changes. So in this case, it's, well so all of the Session state changes will come in through this delegate method, session:peer:didChangeState:. And there are a number of different states that a session can go through in its lifetime. But one that I really want to highlight here is PeerStateDisconnected:.

So this is where you would have to write some code to handle the case where the remote player dropped off, they either quit the game or maybe they got a phone call or something else happened, and so they're no longer playing with you, and you would have some application-specific behavior to handle that case.

All right, so that was a quick walkthrough using the Session API along with the PeerPickerController. These are the 5 steps, so it's just to recite them again. We're going to configure and present the controller. We provide it with a session. We handle connecting. We handle data transfer back and forth.

And then we handle basically disconnecting. OK. So then where I want to go next is that third element of the Game Kit framework, which is In-Game Voice. So the GKVoiceChatService lets you add voice chat within your application between devices. And so this is-- this API, what it's going to do is sample your microphone and send your voice to the remote participant, and meanwhile, the remote participant might be talking, and so it will play on your device the audio that it received from them.

And what's great about this API is that it actually supports all of that going on while mixing with audio that's being generated from your game. We're going to be covering the low-level functionality that handles that in the audio session that's happening at 5 o'clock today. And of course, we'll be covering voice chat in a lot of detail in the 9 a.m. Game Kit Session tomorrow.

But let's dive into this just a little bit. So, with this API, your application actually provides the code to handle the initial handshake between the 2 instances of your application running over the network. So, there's a configuration-- some configuration data that we need for you to send to the remote participant and then for them to make a couple of calls to set up the connection. I'll show you that in just a second.

And then also while this is running or once the connection is made, you have a number of controls over things like audio monitoring and making adjustments to what you hear. So you can mute your own microphone. You can change how you-- the volume that you're hearing, the remote participant, and you can enable metering which might allow you to do something, for example, where you present some interface that lets you see when another-- when the other participant is talking, for example. But so, I want to run you through the sequence of events that happen when you actually are initiating this API.

So both sides, to start, have to define a unique participant ID. And this can be just an arbitrary string that you define. It's how other people will identify you and their IDs how you will identify them. Now there are some things that are going to happen on your side and some things that are going to happen on the remote side, so I've broken those out here and on the next line. So on the local side, you initiate the chat by evoking an object or the GKVoiceChatService object method called startVoiceChatWithParticipantID, and you pass the participantID of that remote participant, OK? Now on your side, a delegate method for the GKVoiceChatClient object is going to fire, and that's called voiceChatService:sendData:toParticipantID.

So this is the configuration data packet that I was talking about. And it's your responsibility now over the network connection that you may have already set up with some remote participant to send this control data over to them so they can make some calls and finish setting up the connection.

So let's look at that. So now on the remote side, the remote instance of your application is going to receive that data packet from the network. And it's going to unpack that data, realize, "Ah, this isn't just some game state information being passed back and forth or something else, this is actually voice chat configuration data", and so I need to invoke this routine here on my-- on their side, they will invoke their VoiceChatClient receivedData:fromParticipantID, that's your participantID and the data that you sent to them, OK? Now, on their side, the GKVoiceChatService delegate method will fire a voiceChatService:didReceiveInvitationFromParticipantID with the callID.

And at this point now, you're at a stage where they know that you have-- that you have invited them into a voice chat, and so on their side they might pop up some user interface controls to allow them to either, allow, to accept or decline that chat invitation.

And assuming that they do accept it then we call-- on their side they will call their VoiceChatService object acceptCallID, OK. So now everything has been accepted. The configuration data has been exchanged, and the connection gets negotiated under the hood by the VoiceChatService object. Both sides now can receive a notification that the voice chat started. It's an optional method.

You don't have to implement this, but it could be helpful so now you can know that you're in voice chat mode, and so both sides can receive voiceChatService:didStartWithParticipantID and that will be the participant ID of the guy on the other side. Well, all right. So that is just the quick look through the peer-to-peer connectivity that's provided now in iPhone OS 3.0. Now I just want to remind you, obviously this was a very fast run through of that functionality.

We'll have a session that goes into this in depth tomorrow morning in this room at 9 a.m. Where I want to go next is with the accelerometer. So, the accelerometer has obviously captured people's imagination on this platform. People use-- we see all kinds of games in the App Store that are using the accelerometer to make their phone into a game controller. And so, the accelerometer itself actually reports for simplex.

So, it's constantly reporting the forces that are felt on the device and most frequently the force that it's feeling is the force of gravity. And by knowing the force or by seeing which direction the vector of the force of gravity is in, you're able to calculate the orientation of the device as you're holding on to it. And the accelerometer gives you obviously instantaneous knowledge as this is going on. You can be receiving accelerometer data at a rate of up to 100 updates per second, so it's very fine grain. And we see this being used in all sorts of games.

So, perhaps some games are using, are rolling the phones to steer as you move around. Others are doing-- changing the pitch to, or looking at the pitch to control the throttle or brake as in, say, a driving simulation. And still other games using the accelerometer in different gestures or interpreting it differently to swing or punch or throw or flip or reload, right.

So, there's all kinds of different game play functionality that you can create using the accelerometer and so you know go out, use your imagination. But so the reason why I actually want to present this section is to make sure that everybody gets this exactly right. So, I'm going to show you a little bit about the interface itself which is just that simple, and how you receive accelerometer data. But then the really interesting part is actually about smoothing the data that you receive from the accelerometer and I'll give you some tips for your user experience in your game. One note before I start, of course there's no accelerometer support in your Mac.

I've said before, don't shake your Mac, it's not going to work in your game. Something actually though that some game developers had done that's very clever here just to be able to test is that sometimes they will play the game live on the device and have a special code path that is actually recording the values coming off the accelerometer.

They bring that back over onto their Mac and when they run in the simulator, they just use that-- those prerecorded accelerometer values as, you know, input into their routines. It's a very effective way of managing to use the accelerometer and still be able to get the fast turnaround you get with using the simulator. But, all right, so let's just dive into this.

So accelerometer, part of the UIKit framework, it's reporting values back to you on 3 axes within a 3-dimensional space in X, Y, Z. If you're holding the phone in portrait mode out towards you guys, then the X-axis is going to run horizontally from left to right. The Y-axis runs vertically from bottom to top, and the Z-axis runs through the device from backstage out towards you. OK, the code for this actually really couldn't be much simpler You just get a handle to the accelerometer instance which is, you know, there's one accelerometer on the device and so you just get a handle to it.

You don't need to initialize, you know, anything along those lines. You set an updateInterval which can be anything up to a maximum of 100 updates per second. And then you set a delegate, and that delegate is going to immediately start sending updates of the accelerometer values to your method accelerometer:didAccelerate.

And I want to note something here. So this will, as it says, it will start sending the updates immediately and it will just keep sending and keep sending until you set the delegate back to nil. So, if you want to go, for example, in your game and go into pause mode or something else is happening in your game, you don't need to be currently reading the accelerometer, set that back to nil so that you don't keep getting accelerometer updates.

'Cause there's probably other things that you're doing when you receive those updates that would just be wasting CPU time. All right. So, now to extract the data out of the information that comes to that delegate, OK, it couldn't really be too much simpler. You get an acceleration object here, UIAcceleration object, and these values are really just floats.

You can get the direct X, Y, and Z values out of them. So then, what you'll do now is process the data itself so-- and that's where it gets really interesting, right? And the thing to understand here is that actually the accelerometer is extraordinarily sensitive. I've taken the AccelerometerGraph example that's up on the iPhone Dev Center here.

And for the X values coming in, I multiplied them by 50X to see you could really see the variance in those values that are coming in. And I took this snapshot as I was trying to hold perfectly still, and yet you know you can see that there are some slight ticks up and down in that, in those values that are reported back.

And if you're using the accelerometer at a sensitivity level where it's controlling the camera location or the rotation of some object in your scene, then actually you'll want to smooth out this data so that the object appears to be presented smoothly to you. So, the activity of smoothing out that data, you know, comes from signal analysis. And so, we're basically taking this problem moving the-- you know, moving it from the time domain over here to the frequency domain and just thinking about different ways that we can isolate different frequencies of data that are coming off of the accelerometer.

And now to simplify this, of course, a low-pass filter is something that you might apply then to isolate the gravity component, the low frequencies, because those are going to be constant. Whereas a high-pass filter is something that you might apply to the incoming data to isolate the so-called "shake" of the device if you wanted to get at that data but didn't care of the original orientation of the device as it's in space. So, let's look at a couple of filters.

This one is actually a trivial low-pass example that we provide in our programming guide. And the thing to know about this though is that actually it's too simple for most game controllers. So, this is going to be fine if all that you need to do and when you're using the accelerometer is kind of know recently where it is.

You know, and so for user interface type gestures where you're going from landscape into portrait, that sort of thing, this would be fine. But, if you're actually going to be using the accelerometer as a real-time game controller in your game, then certainly this trivial example here is not going to be good enough, single-pole filter not working very well.

And if we look at the reason why, let me just factor it out. What we're doing each time through this calculation and these are happening at whatever update rate we set, say 30 times per second. We're going to be taking 10 percent of the original values and adding that to 90 percent of the value from the previous iteration.

And that's just not going to be a big enough contribution of these incoming values to really have the, you know, have the data be-- appear to be responsive. And you think, well you could just bump up those values, couldn't you? And you know like you can make them 50/50 or something like that.

And the issue with that then is that you sort of, you don't ever reach a point of perfection with a simple filter like this. It will always either have too much lag or it will be too much jitter. And so, you need to do something that's just a little bit more intricate than this. So, let's look at the values that you would get from that sort of a filter here.

You know, look at the details here. Over here we have, we reached the 90 percent point at about 22 iterations in. And the given here is that we started perfectly flat for example and instantaneously moved to perfectly upright, and we want to see how long does it take for those values to kind of catch up with reality. So, 22 frames or 22 iterations in, we're at 90 percent.

Even after 30 iterations, we're at 96 percent of the actual value there. And so that's a lot of latency because a lot of games are going to be running at 30 frames per second. If it takes that long, then you've got, you know, kind of a second of lag in your game.

It's going to make it really hard to steer your guy around. So, instead we recommend that you really fine tune this, and this is an example, I realized, is kind of a cloud of code up here, but it's an example of just doing-- taking a few more steps along the way as you are, as you're calculating those incoming values. And I want to point out, so this code now, we are making a new version of the AccelerometerGraph example available to all of you guys. It just didn't make the drop for today's session.

It should be on the website tomorrow, so go take a look at that.

But OK, so there's a couple of different things that we're doing here. We start with kind of the same calculation that we had in the previous trivial filter, but then there is two more things that we're adding to this. First is a step function.

So we observe that values that are kind of within a really tight band, even though there might be a lot variants within that band, those values can essentially be smoothed out to all the constant. And so with this-- by adding in some calculations to calculate a step function, we're able to achieve that.

And then the second observation that we make here is just about calculating a so-called proportional factor to all of these. And so, here's the observation that we want to take the accelerometer and consider how much change there was in this current frame. So if there was a lot of change, then proportionally speaking we want to take more of the new value compared to what was there before. We assume that this lots of change mean an intentional gesture on the part of the user.

But if the amount of change is really low, then we take kind of a smaller amount of that proportionally and work our way through this, and that way, those small changes will kind of end up getting smoothed out. So, I want to show you the results of this, the red line here. So you can see, obviously it converges up to 1.0 much faster than before. That after 11 iterations through this loop, you get to 90 percent and certainly after you're at 30 iterations here, you're all the way up to the actual value of 1.0.

The step function helps you try to kind of snap to that final version of 1.0, and the proportional change helps us to get this sudden rise in the beginning of this. And of course, on that code that I just showed you, you're able to adjust some of the parameters for the step function or other things there to really fine tune this if you want to push that red line even further over to the left. So, go wild with that. It's going to be in the AccelerometerGraph example that you can see, get tomorrow.

Now I want to show you a quick demo of that now.

[ Pause ]

So here I am, I am running the GLTeapot, GLGravity example here. And what you see, you see it's shaking actually quite a bit. This is with absolutely no smoothing coming into the incoming values. And we're using those values to just set the rotation on the Teapot.

And so it's setting here flat on the surface and there are even some variants then. But if I move around, you can see it's actually pretty shaky. And now, as I do this, I observe of course the response is instantaneous. But it's too jittery, I think, to be able to use productively as a game controller.

So you do need to smooth this out. Now if I tap once, now I'm in the mode with the trivial low-pass filter example that came out of our programming guide. And so now you can see, OK, the shake is gone and it's very smooth as we move around. But, and I don't know if you guys can tell so much, but there is some lag.

And as I move around the device here, you can see that there's about, you know, half second to a second of lag depending on how sensitive I really think about. One more tap and now I'm using the code that I just showed you with that improved low-pass filter.

And so now the response-- well, again we've gotten rid of the shake but the response now, it's very responsive and yet it's still very smooth as I move around. And so this would be just fine to go driving around and using as a game controller. So all right, and back to slides.

[ Pause ]

So there's a couple now of expert tips that I want to talk about with relation to the accelerometer. So, the first thing to realize if you're doing the smoothing calculations is that the calculations and the callback are actually occurring on your main thread. And there might be other work happening on your main thread at the same time, in particular OpenGL might be rendering its frame on your main thread as well.

And something to-- a subtlety to be aware of there is that, you know, OpenGL rendering can be variable. The amount of time it takes can be variable from one frame to the next as the load of what you need to render increases and decreases. You know, different objects come into view. It might take longer to render a frame.

You end up looking out into space, maybe it takes shorter. And so, the processing frequency of receiving these callbacks can be irregular and you just, you need to take action to correct that. And in particular, if you're using the timestamp values coming in off the accelerometer to figure out like what the value was at a particular time, you're going to get misled because the events are going to stack up and only be received by you after you're out of that GL rendering part of your processing. So what we recommend here is actually rather than performing the smoothing every time you get an update from the accelerometer, to instead perform the smoothing in a rendering callback.

So-- or in some other callback that more closely relates to the frame rate of your game experience. So, do that there instead of in your accelerometer callback. And in the example that we show you, this is a, this is how we did that. Another thing about using the accelerometer, now this is more on the user interface or user experience side. You need to choose some home position for the accelerometer, something that kind of is the origin or means I'm at zero right now.

And you can either do this by just making a choice. Some people notice that, oh, at around 30-degree incline, it kind of feels natural for a lot of users, so I could just choose that. Something that's even better though would be to calibrate when your game starts up. So, just be aware that the people who are playing your game, sometimes they might be sitting at their desk or in a meeting and or listening to a conference.

So, but other times, you know they might be lying on their couch. The device might have a different orientation when they started, and so having them, making them go to a 30-degree incline wouldn't be natural for their game or for them. So, a couple of different things that you could do here, you could calibrate this when the game starts, and then also recalibrate each time the game is resumed after a pause.

And that would just simply take that value, make that be the new origin so to speak, and all of your subsequent updates would be relative to that origin. Something else that we did in one of the demos that we showed you last year was that if we get out in-- was that actually we would dynamically calibrate.

And what we would do is if we noticed that the accelerometer values coming in were way off, say, in the corner for more than a couple of seconds, we would assume the user has changed their, you know, they're not sitting down anymore or, you know, they've decide to play at some other angle, and we would recalibrate with that new position being the origin. There is no universal answer for this. This is just something that you'll have to think about and implement in your own code. And the last tip that I want to give you with regards to the accelerometer is just to talk about screen dimming behavior.

So, something to remember, or you've probably been playing some games where it's fully accelerometer based, you're playing the game and then suddenly what happened? The screen kind of dimmed, and it's because the timer elapsed that no input had been taken on the screen for some number of seconds. And so, the operating system dimmed the display for you to save the battery.

So, well that's good behavior normally, but if your game is one where you're not normally tapping the screen, then that's just not what you want to set up. And you can instead disable that behavior. You do that by changing this property on that UIApplication object called idleTimerDisabled. You set that to Yes, and now you're disabling the screen from dimming. But it's very important to note this, that you have to get this exactly right as developers.

You should only do this while your game is being played. It's not something for you to just set when you start up your app and just leave it set. It shouldn't be used while you're browsing menus. It shouldn't be bro-- used while you're looking at info screens or other activities in your application. It should just only be during game play, and it's something that really only applies to games that are using the accelerometer for all of their input. All right, so folks, that takes me through what I wanted to tell you about the accelerometer.

And the final portion here now is In-App Purchase. So you saw this yesterday of course, and the Store Kit framework provides you with the capability of having a payment collection engine that runs directly from inside of your application. This is something that is specific to iPhone OS 3.0. And as you heard yesterday it's for paid apps only, free apps remain free. So the API itself is going-- has a number of different scenarios that can be set up, things like subscription model or downloading content into your game or just unlocking functionality that is already compiled in.

So, that last one is the case that we're going to look at today. And it uses the App Store to collect the payment. So it connects back up with the App Store, presents the user with some familiar-- with a familiar experience that they would already be accustomed to if they have, for example, purchased apps on their phone, or downloaded music onto their phone.

From the developer point of view, also it provides to use a very familiar experience. If you have paid apps, you'll just get some updated information to your iTunes Connect reports. So, here is our scenario, we're back in our dungeon game. The user has decided, you know what I'm done with this campaign, I want to see what other campaigns are available in this game. And so, in this particular case, we have a couple of campaigns that are already compiled into the app. They're just not unlocked.

So we are going to provide the user with the option to look at those, maybe select one and purchase it and if they purchase it, then we'll unlock it. So you handle the first stage of this where you might want to show your user what different dungeon campaigns are available for them to purchase or something more specific of course to the genre of your own application. You also present to them now kind of a products catalog and product-specific information.

I've kept it really simple on this slide. Just, this would be here a dialog that you would pop-up maybe with some more information about the level that they are going to purchase. You would present that to them, so that they can make the decision about whether or not to buy it. Now if the user clicks Buy here, this is now when it connects up with the App Store.

And so, the user will be presented with a standard iTunes connection dialog, they'll enter their account credentials here, and if-- if the purchase is successful, well, then now you go through, and now that level is unlocked in your game. OK? So, let's walk through the steps here that are involved with actually setting this up. And again, I'm focusing on this specific instance for unlocking levels that are already provided in the application they've just not enabled it first.

There is other examples, you can have-- subscriptions that can be enabled through in-app purchase, you can be downloading content that's-- or get access to downloading content through the in-app purchase and so on. But many of those other things are going to be covered in more detail in the in-app purchase session that's happening later this week. But OK. So, up front, you defined some metadata. These are the products that you're offering for sale in your app.

And you enter that metadata into iTunes Connect. Oops, I didn't mean to go forward there, go back. Then the second stage is you will build in your application the purchase workflow. So this is going to be all of the code that you write to present the items for sale, let the user choose one, then connect up to the store to actually confirm the purchase, get a transaction back to-- look into that transaction, deliver the goods to the user, and also support the case where perhaps the user has a new device and they're loading your app onto this new device, you have to be able to restore the purchases that the user has made.

So, let's walk through starting up with the metadata. So the metadata, this is a data that you're going to enter into iTunes Connect and the first thing you're going to identify is-- are things like a SKU for each of the products that you wish to offer, items you wish to offer for sale.

You'll give each one a title, you'll give it a description and both of these things can be localized in-- for the different locals that your application supports, and you'll choose a price here. Now, you might actually enter a whole bunch of different items into iTunes Connect and have some of them be available for purchase now, maybe some of them you're going to get to later, and so on.

But, you go log into iTunes Connect, enter in all of these data and now a copy of that is living in the cloud. OK. So then we go to your application and there is a number of steps that you have-- that you need to implement within your application to get through the flow to where the users actually purchase an item.

So, before the purchase there is a few steps there. So this is the case where or this is the part of the process where the user is just browsing your catalog, what dungeons are available for me to go and purchase? And you actually need to connect up to iTunes or excuse me, connect to the App Store, give it your product catalog and it will return to you the localized details about those products localized to that particular user.

The user then, or your application then can present those items to be selected by the user, and you're responsible to present how they're displayed. So it can be something that is kept within the theme of other user interface elements in your application. This part, you don't have any particular built-in UI that you are forced to use.

Then, once the user has selected something that they want to purchase, you'll connect again, again back up to the App Store to request the payment for that. The user will be presented with that dialog to enter their account credentials and the payment or the transaction will either go through or not. Then after that happens now, you have to look at the result of that transaction.

You'll unlock-- if it was successful you will unlock those, the feature or the built-in items that you're trying to enable here. And then only at that point actually do you remove the transaction from a persistent queue that we keep of the different transactions that are happening. And then finally, we go through the process of restoring items.

So, let me now walk you through this step by step. All right, so that first step was to load the catalog. Now, in many cases, your product catalog might be something that you have already compiled into your app. You know what dungeon levels are going to be available to your users.

So you may have hardcoded it or you may have that sitting in a plist somewhere in your document bundle. The other option of course is that that data could be living in the cloud and is something that you could retrieve dynamically to find out, OK, have I updated anything, are there new products that I could enable for this user? And so that would be how you would handle the dynamic case. But, again in this, in today's scenario I'm focusing on unlocking levels that are already compiled into your app.

So, what we're doing now is just finding out the localized information that we should present to the user. So, the first thing to do is that we construct something called a products request and we provide to it all of these of the SKUs that were available for our product.

Create the request, initialize it with that set of SKUs, set a delegate, and then finally when we call, invoke start, that information is going to be send out to the App Store. And the App Store will respond now with a products response. OK. So this delegate method will fire productsRequest:didReceiveResponse:.

And what you will receive there is two things. You'll get a response and it will contain the products that were successfully-- that there was information available for up on-- in iTunes Connect and then it's possible you might have made a mistake or something could have not been finished on our side and so you also get a list of the product IDs that were not valid. OK, so that was the response back from the App Store and now you take, you start unpacking the data here that's in that response looking for the localized title, localized description, and the price of these items that are for sale.

And you will now, within your own user interface, present those to the user so they can make a choice of what to purchase. The next or here is now-- Here is where we are so far. All that we've done now is present the items for sale to the user. The user hasn't made a choice yet.

So, let's take a look at that. So the next stage here is to actually request the payment once the user taps on and makes their selection. So the code stages that are involved with this, pretty simple. We're creating first a payment object and we initialize that with the product that the user selected or we can also initialize it with just the idea of that product that the user selected.

We take that now or so now we have a payment object, we're going to set up something called a payment queue. This queue is going to be managing, excuse me, is going to be managing the payments that you're sending up to the App Store and then when the App Store responds, you'll be taking items off of this queue to handle the transactions.

So here we create the payment queue, a default queue. We add something called the transaction observer, that's where you're going to get notified of the result of this payment.

And then the third step here, there's a couple of little magic pieces that happened. We add the payment into the queue and the API constructs the transaction and puts that payment inside that transaction and sends it up to the App Store. OK. So that's how we get a payment, a request for a payment over to the App Store. The user now will be presented with that iTunes password dialog. They enter their account credentials. And then there are two possibilities.

Either it all goes through or it doesn't, right? And we need to be able to cover both of those cases, obviously Your transaction observer will then get a notification back and this delegate method will fire that the payment queue updated some transactions and you'll get an NSArray of the different transactions because it's actually possible for you to send more than one request for payment at a time and then for it to send back multiple transactions for you to handle.

All right, so now we've finished requesting payment and we move now to the things that you need to do to actually deliver the goods. So the first thing we'll do is inspect the payment transaction within that delegate method that just fired. There's a number of different states the payment transactions can be in. The first is that OK, this item is in the state of being purchased, that's fine.

The one that I real-- that ones I want to focus on are these latter 3. So, TransactionStateFailed, this means that for whatever reason-- the user canceled out, they didn't enter the right password, just whatever happened. This transaction did not go through and so you would probably just return back in your user interface maybe to let them go and look at other products that you have for sale or, you know, act accordingly The other 2, OK, so if the payment state is that it was purchased then this is now for real, they bought it and you need to unlock whatever functionality was previously locked up and deliver that product item to the user. OK, so if this is code within your app that you need to enable, then, you know, flip that bit so now this code pack is active.

Final one here is that we also help you to cover the case where the user may need, maybe restoring purchases from-- onto a new device or onto a device that they've done a clean and reload on. So, that's this last item here, TransactionStateRestored. This is just so you'll know, oh, OK, the user already purchased this and I'm going to go and unlock it. And this is just as real as a real-- as a TransactionStatePurchased.

OK. The final part here, after you've unlocked whatever functionality it is that the user is purchasing is now you need to finish the transaction. The reason why this is a separate step is actually because the user might quit out of your application at any time. It could be that they entered their password and boom, phone call and you know they don't know necessarily oh, what state was this thing that I just purchased. Well, we help you with this quite a bit.

So, the payment queue is persistent, the transactions are persistent. They'll stay with your app. I mean you can go back into them and get back to them until you've actually removed that transaction from the queue, OK. And so you obviously need to make sure that you have delivered the item to the user before you remove that transaction from the queue, but this is to help you with that case where you're dealing with mobility.

All right. So, at this point now, the user is playing your game, they have their new levels. And there's only this one more case that we need to cover and that's restoration. And we provide you with some great functionality here but the policy issues to remember here are that just like applications, purchased items, the user only needs to pay for them once and then they can, if they decide to restore their phone, they get to reload your app and they also get to reload whatever in-app purchases they have made. Also remember that a particular user may have both an iPhone and an iPod Touch.

And it's perfectly valid for both of those devices to have your purchase or the result of your in-app purchase from one purchase. OK. And of course this covers the case where to make purchases available to the user when-- if your, if they delete your app from their phone, why would they do that, of course, but-- and then reinstall it later, OK. So, there's code to handle this. Just a reminder, we're back in our case statement here.

This is where this-- you'll receive notice of this, but there's an issue like, how do you get there? You know if I-- if the user has just restored their device from scratch, they loaded my app, how did they know or how do I know to even check the queue? Well, you should probably add some interface elements in your application that allow the user to go and restore all purchases that they have made. So, if you do that then what you would do when they, after they select that option would be to reconstruct the payment queue and ask the payment queue, so ask the App Store to restore all of the completed transactions that this user has made.

And once they do that now, that same delegate method is going to be firing. You will back running through this case statement for each of the transactions that that user had previously made on their account. All right. So, that's how you can restore transactions to users and it's critically important that you support that functionality in your app. But, all right, so that's the whole process.

There are 8 steps that I listed here. There are, as I've been mentioning, other scenarios that you might be interested in and we'll be covering these steps here and those other scenarios in the in-app purchase session which is happening later in the week. But that is StoreKit Integration. This my contact information--