WWDC2014 Session 611

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
>> Thank you.
Thank you.
Good morning, good morning.
And welcome.
You know, I love games.
I've been playing
them my whole life.
I love making games.
I love teaching about games.
And I'm really excited
to talk to you today
about designing for
game controllers.
My name is JJ Cwik, and
I'm a software engineer
on the Game Play Technology Team
or on the Game Technology
Team at Apple.
Now, let me poll the audience.
How many of you have already
integrated game controllers
into a game before?
All right, a few.
Great. And for how many
of you will this be your first
talk on game controllers?
Good. Large majority.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Good. Large majority.
Great. Wonderful.
So, this is going to be fun.
Let's get started.
When I talk about
game controllers,
what Apple is delivering
is in two parts.
The first part is the
MFi Specification.
Now, this is for third-party
controller developers.
It defines all the hardware
requirements of the controllers.
Things like: the control
layouts on the gamepad itself;
how the buttons feel
when you press them;
and the communication
protocols between the device.
And the goal with this
program is to give confidence
to consumers that when they
purchase a game controller
that has one of these icons
on the box, they're assured
that their game controller
is going to work
with all your games that support
the game controller framework.
So that's the first part,
the MFi Specification.
The second part is the
game controller framework.
This is the software
side of things,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This is the software
side of things,
what you as game
developers will be using
to integrate game
controllers into your games.
This is an iOS and OS X
cross-platform framework.
So code that you write
for game controllers
in one is directly
usable in the other.
And it features a simple
API, which allows you
to find controllers and read the
input off of those controllers.
And the goal with this side of
things with the framework is
to give you one API
so that you can focus
on developing your game,
integrating game controllers
really smoothly and not have
to worry about little
variations in all sorts
of different controllers.
So that's the second part,
the game controller framework.
Now, what kind of
controllers are available?
There are three types.
The first type is a form-fitting
standard controller.
And by form-fitting, we mean
that the controller
encases the device.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Therefore, the touch screen is
easily accessible while they're
playing with the game pad,
and so is motion
control of the device.
And by "standard," we're
referring to the controls layout
on the gamepad, specifically
that there's a D-pad,
that there are four face
buttons (A, B, X and Y,
always in these colors
and in these locations)
and two shoulder
buttons (L and R).
So that's the first
controller type.
The second controller
is the form-fitting
extended controller.
So it too is form-fitting.
And the extended control layout
has all the same controls
of the standard controller,
but it adds to it two
thumbsticks and two triggers.
That's the second
controller type.
The third controller type is the
standalone extended controller.
And by standalone, we mean
the controller does not encase
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And by standalone, we mean
the controller does not encase
the device.
So the touch screen is
not easily accessible.
Motion control is not an option.
You're only getting input
from the controller itself.
And again, this has the exact
same extended control layout
as the previous extended
controller.
So that's an overview.
Now, I'm going to focus
the rest of my talk
on the game controller
framework itself
and how you use the framework
to integrate game
controllers into your games.
Specifically, we're going to
find out inside your game,
how do you know
which controllers are
available for your game to use?
We're going to talk about
finding controllers.
I'm also going to talk about
the various inputs found
on controllers, like the buttons
and the D-pads and how those map
in software, and how
you can read whether
or not a player is pressing
the A button or the D-pad.
I'm also going to
talk about what's new.
What have we been
working on this past year?
What are we introducing at iOS 8
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
What are we introducing at iOS 8
and with respect to
game controllers?
One really cool thing that
I'm excited to tell you
about is controller forwarding.
In a nutshell, this is
a way for a controller
with a snapped-in iPhone to be
used as a wireless controller
to control the game
play experience
on another device, like an iPad.
And throughout the talk,
I'm going to be giving a heavy
emphasis to design guidance.
It's actually fairly
straightforward
to integrate game
controllers into your game.
And it's going to be
really instructive to talk
about design guidance
based on things
that we've seen this past year.
And I'd really like
to focus especially
on an Apple Design Award winning
game this year, Leo's Fortune,
because it integrates game
controllers really, really well.
It's a fantastic integration.
And I think it's going
to be really instructive
for all of us.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to be really instructive
for all of us.
So, let's talk about
finding controllers.
How do I know which controllers
are available to my game?
The first thing you're going
to want know is the main class
that you're going
to be interacting
with in the game
controller framework is
called GCController.
Now, this is the same class
for all controller types.
And it allows you to do a
few things, like finding
which controllers are available,
reading the inputs off
of the controllers and
additional information
about the controller
itself, such as whether
or not it's form-fitting, if
it's standard or extended.
Now, to find which controllers
are currently connected,
call the controllers class
method on GCController.
It returns you an array of
currently connected controllers.
Or, an empty array if no
controllers are connected.
Now, this array starts
off empty.
And it's updated as
controllers are added
and removed from the system.
So whenever you check
this method,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So whenever you check
this method,
it'll return you the
latest information
on what's currently connected.
Now let me explain
visually how we're going
to structure our code.
There are two methods that we're
particularly interested in.
"One is application:
didFinishLaunching WithOptions".
And the second is a method
you're expected to create.
In this case we're calling
it setupControllers.
Now setupControllers
is your central method
where you track controller
state and take action
as appropriate to your game.
In there we're going to
check the controllers array.
Next, we're going to connect
and disconnect controllers.
And we want to find
out specifically
when they connect
and disconnect.
So we're going to
set up notifications
to be notified when
players do this.
And importantly,
we're going to --
the action we're going to take
whenever a controller connects
or disconnects is to call
our setupControllers method.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Lastly, we're going to setup --
we're going to kick off a
wireless discovery process.
And what this does is it
allows those controllers
which communicate
wirelessly to pair
with your phone right
within your game.
So you don't have to exit to --
or your players don't
have to exit
to the settings screen
in order to do that.
And importantly, notice
the cascade effect here.
When we kickoff wireless
discovery,
whenever controllers
happen to be discovered,
connection notifications
are posted,
which in turn will cascade down
and call our setupControllers
method.
So that's the game plan.
Let's look at the code
for how to do that.
We're within application:
didFinishLaunching WithOptions.
And you'll notice the first
thing we do is we call --
or we check whether the
GCController class exists.
And by extension, what we're
doing here is we're checking
whether the game controller
framework exists in the version
of the operating system that
your game is running on.
This is important for those
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This is important for those
of you whose games support
operating systems earlier
than iOS 7 and earlier
than OS X Mavericks.
(The operating systems where
the game controller framework
was introduced.)
Assuming the game
controller framework exists,
then we go ahead and register
for notifications whenever
controllers connect
and disconnect.
We do this using the
notification names
GCControllerDid
ConnectNotification
and GCControllerDid
DisconnectNotification.
And you'll notice that in
both of these situations,
whenever these notifications
are posted,
we're calling setupControllers.
Lastly, we call
startWirelessController
Discovery WithCompletionHandler.
Now, this kicks off
asynchronous scanning
of wireless controllers.
When the completion handler
is called, the scanning
of wireless controllers
has stopped.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of wireless controllers
has stopped.
And any controllers
that are wireless
that have already been
discovered will have already
called, or rather, posted
notifications for connection.
And by association,
setupControllers will
have already been called.
So the important takeaway here
is it's completely unnecessary
to explicitly call
setupControllers
within the handler.
Now let's look at our other
method, setupControllers.
Here we're checking
the controllers array.
And if it's non-empty, we take
action appropriate to our game.
So that's it.
That's how you find
which controllers are
available on your system.
Now, I'd like to offer
some design guidance on how
to gracefully handle
connections and disconnections.
When a player connects
a controller,
they're communicating intent
to use that controller
as their preferred control
scheme for your game.
And your game should
react accordingly.
So, specifically, move to
controller-based input.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, specifically, move to
controller-based input.
Remove any on-screen
visuals that are virtual,
like virtual D-pads and analog
sticks, virtual buttons.
Those are now redundant since
the gamepad already has those.
Each gamepad also
has a pause button.
So even the little
pause button overlay
on the screen can be
offloaded from the screen
because the game
controller has that.
And lastly, you're going to
want to set the playerIndex,
which is a property
on the controller.
And this will light up the
LEDs on the controller.
This gives important
feedback to your player
that this game controller
is recognized
and being used by your game.
And disconnections can happen
for a variety of reasons.
Either the player explicitly
disconnects the controller,
or maybe for whatever reason,
the connection becomes loose
or the batteries run out.
In this case, as a
convenience to the players,
consider pausing the game play
if that's appropriate
for your game.
Give them an opportunity
to reconnect or return
to regular controls if
that's an appropriate action.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to regular controls if
that's an appropriate action.
Now, I'd like to focus
in on the player's intent
when they connect.
Some games that use
tilt-and-touch control,
upon entering gameplay,
they present the player
with this kind of an
option: "Do you want
to use tilt control
or touch control?"
And this is a great option for
games to present to players.
Until they snap in
a game controller.
Now, the player's intent is
to use the game controller.
And this kind of a choice
is actually confusing
to the players.
Does this actually mean
that this game doesn't
support game controllers?
Or does it mean that my
controller isn't connected
properly to my phone?
Or if I select one
of these options,
will that disable
game controller input?
Instead, if you have a
dialog that looks like this
in your game, gate it on whether
or not a controller is
currently connected.
If it is, that is sufficient
intent that the player wishes
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If it is, that is sufficient
intent that the player wishes
to use game controllers.
And this kind of a
dialog is unnecessary.
Now, another kind of dialog
you might be tempted to put
up is one like this:
"Game controller detected.
Would you like to use it?
Yes or No."
And at least we're giving
feedback to the player that,
yes, this controller is
connected and recognized.
But again, the intent
that the player has
when they connect
the controller is
that they actually
want to use it.
And so such a dialog
is redundant.
If you have this
kind of a dialog,
you don't actually need it.
Just use the controller
if it's available.
And as an example of a game
that does this really well,
I'd like to look
at Leo's Fortune.
Here it is in one of
its touchscreen modes
where you're controlling the
main character using virtual
buttons along the
bottom of the screen.
This is great.
We don't have a game
controller attached.
And the game is very
obviously presenting these
control options.
Now watch what happens when
we connect a controller.
The virtual buttons went away.
And importantly, the player
didn't have to take any action.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And importantly, the player
didn't have to take any action.
No dialogs were presented.
No choice was given.
The game just assumes
that the existence
of a connected controller is
intent and permission to use it.
So, we've talked about
finding controllers.
The next step is how do I
actually read the inputs?
How do I find out if the
A button is being pressed?
And before I get to that,
let's go through and talk
about what kind of inputs
you're going to find
on the controllers themselves.
So, first and foremost,
when you're designing
for game controllers, keep in
mind to first design for touch.
You want the native controls
of your operating system
to be usable at all times.
And this makes sense.
If a game player downloads
your game from the App Store,
they're going to have the
expectation -- correctly so --
that they're able to play
it regardless of whether
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that they're able to play
it regardless of whether
or not they have
a game controller.
So, put another way,
game controllers cannot be
required to play your game.
Now, let's talk about profiles.
So profiles are the software
mapping to the hardware.
So we have three
different controller types.
And they are mapped in software
to two different profiles.
One is called gamepad,
and the second is
called extendedGamepad.
And you'll notice all
controllers support the
gamepad profile.
Now, the physical controllers,
which are extended
control layouts,
also support the
extendedGamepad.
So if you support only
the gamepad profile,
you're guaranteed that
your game will be playable
on any controller available.
And if you wish to support
the analog thumbsticks
and the triggers, then you'll
target the extendedGamepad
profile as well.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So these are properties on
the controller instance.
Let's look at what properties
are within the gamepad profile.
We have buttonA, buttonB,
buttonX, and buttonY.
And these correspond to
the four face buttons.
Similarly, the two shoulder
buttons are called leftShoulder
and rightShoulder
in the framework.
And the D-pad is
simply called dpad.
Now, switching over to
the extended profile,
extendedGamepad, it has all the
same properties from gamepad
and in addition, we
add leftThumbstick
and rightThumbstick, and
leftTrigger and rightTrigger,
corresponding to the
additional controls
that extended gamepads give us.
Now I'd like to call your
attention to the fact
that triggers and face buttons,
while physically different
to the Game Controller
framework,
we use the same data type:
GCControllerButtonInput.
Similarly, D-pads
and thumbsticks,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Similarly, D-pads
and thumbsticks,
while being physically,
different share the same class,
called GCControllerDirectionPad.
So let's talk about those
classes, buttons and D-pads.
GCControllerButtonInput
represents all the buttons
available on the controller.
And these can be
read in two ways.
One is with the pressed
property.
And this returns the
classic, digital version
of a button whether or not
it's being pressed: Yes or No.
But, all buttons on game
controllers are also
pressure sensitive.
And if this something
that's useful for your game,
you read that using
the value property.
This returns you a float
normalized between 0
and 1 indicating how hard
the button is being pressed.
Now, the A and B buttons have a
special convention in iOS games
and GameController games.
The A button is intended
to be your primary action.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The A button is intended
to be your primary action.
And the B button is to be
used as the secondary action.
And so you need to think
about, with your game:
"What is my primary action?
Is it jumping?
Is it shooting?
Is it throwing a ball?"
And also, think about
it in the context of UI.
If you're not in actual game
play, what are the primary
and secondary actions?
For UI, it should be
to confirm and accept
for the primary action.
And the B button should be used
as cancelling, the
secondary action.
Let's talk about
GCControllerDirectionPad now.
Again, this is used for
both D-pads and thumbsticks.
And it's treated in
two different ways.
One is that it's treated
as four buttons named Up,
Down, Left and Right.
And the second way we treat it
is as two axes, xAxis and yAxis.
And you'll recall that
I said each button
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And you'll recall that
I said each button
on the control pad is
pressure sensitive?
Well, that's also true of
Up, Down, Left and Right,
since they're the same class.
So D-pads are effectively
pressure sensitive
and digital as well.
Let's look at the axis.
Now axes -- the value that's
returned from an axis --
is normalized between
negative 1 and positive 1.
And a value of 0 you can rely
on being the axis at rest,
meaning the player is
not pressing that axis.
Now, you have a guaranteed
minimum range
of the unit circle.
Maximum range of
the unit square.
And for those of you who may
have dealt with calibration
of joysticks and deadzoning
before, do not do that yourself.
We already take care
of that for you.
So that's all the controls.
And now you can get
on with the business
of assigning game actions
to controller inputs.
And one piece of advice I can
offer is a principle which a lot
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And one piece of advice I can
offer is a principle which a lot
of games use to great effect,
which is to group
logically similar actions.
So in Leo's Fortune,
for instance,
our main character
has four actions.
He can walk left, right and
he can jump and he can stomp.
So the developers have
mapped walking left
and walking right to the D-pad.
Walking left and walking right
are logically similar actions.
It's just walking.
And so it makes sense to
group those on the D-pad.
Also, they're making good
use of the D-pad's ability
to readily switch directions.
Now, jumping is assigned
to the A button.
This is because jumping
is logically dissimilar
from horizontal movement,
and so it's separated.
They could have assigned
jumping to the Up button,
but it's a logically
dissimilar action,
so it might have been a little
bit more confusing for players.
And, the D-pad's ability
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And, the D-pad's ability
to readily switch directions
could actually fight the game
player in this case
and result in a lot
of inadvertent jumps
instead of lateral movement.
Now the last ability
is the stomp ability.
That's been assigned
to the B button.
So again, logically
dissimilar from walking,
so we've separated it.
But logically similar
to jumping,
so it's nearby to the A button.
So now let's actually find
out, now that you know
which controls you want
to assign to which actions
in your game, let's actually
go ahead and read some input.
How do you find out if
the A button is pressed?
Well, it's really just as
simple as reading a series
of properties:
myController.gamepad --
gamepad being the profile
-- .buttonA.pressed.
Recall that pressed gives
us the Boolean state
for whether the button's
pressed.
In this case, we're
going to fire our lasers.
Now also in our weapons code,
we're going to use
the B button's analog
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we're going to use
the B button's analog
and digital representations for
this portion of the game play.
If the B button is
being pressed at (all
in the digital sense) then we go
ahead and start firing missiles
at a rate that increases
the harder
that button is pressed;
B-button .value.
Then we apply thrust
to our spaceship based
on whether the player is
pressing the D-pad up or down.
This is perfect for the
yAxis, so we're going
to use the yAxis
representation of the D-pad.
And note that we're not guarding
this line of code with a query
for whether or not
the axis is pressed.
We're leveraging the
fact that we can rely
on the yAxis's value being 0
when the player's
not pressing it.
And our applyThrust
method is resilient to that
and does nothing when a
value of 0 is pressed.
Lastly, we're taking
special action
if the controller that's
controlling our game happens
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if the controller that's
controlling our game happens
to be an extended controller.
You can check if a
controller is extended merely
by checking whether or not the
extendedGamepad profile is nil.
Notice we weren't doing
that above for gamepad,
because gamepad is guaranteed to
be non-nil since it's supported
across all controller types.
And in this case, we're using
the right thumbstick's position
to move a camera in our
game back and forth.
Now notice: the weapons
and thrust code,
we don't explicitly program for
the extendedGamepad profile.
But, the extended
gamepad will fall back
and use the same behavior
that's specified here
since it's not being
overridden with competing code
for the extendedGamepad profile.
And what this means for
you is that you don't have
to duplicate the same code for
both gamepad and extendedGamepad
if you want it to do the
same thing regardless
of the type of controller.
So that's one way to read
controller inputs, is polling.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So that's one way to read
controller inputs, is polling.
Doing this once every
game frame, typically.
The other way is using events.
Sometimes you just want to
be notified when something
on your control pad changes.
Whether it's a button or
an axis, even collections
such as D-pads and thumbsticks,
or profiles if you want to know
if anything on the
controller has changed.
And you do this by registering
a block as a change handler.
Basically, "here's the
code I want you to run
when this input changes."
So we have valueChangedHandlers.
Note "value" corresponds
to our querying the
pressure-sensitive nature
of the buttons, the
float values.
And new for iOS 8, we have
a pressedChangedHandler.
This is for querying
digital changes.
Let's look at an example of
the pressedChangedHandler.
In our game, we want
to be notified whenever
the Y button changes state
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the digital sense.
So we've set up the
pressedChangedHandler.
And it's called twice, once
when the button is pressed
and another time when
the button is released.
And you can filter that based
on the pressed parameter here.
In this case, we
actually care about both.
So when the button is pressed,
we begin charging a speed boost.
And when the button is released,
we actually do the speed boost.
Now, use pressure
sensitivity with discernment.
There are certain times where
you might be tempted to see
if a trigger, for
instance, is being pulled.
Don't use the analog "value"
if what you really want to find
out is whether or not that
control is being pressed.
If so, use "pressed" instead,
the Boolean representation.
Also, take advantage of
the pressure-sensitivity
of buttons and D-pads.
D-pads, while thought
of traditionally
as a digital-only
input, are a great way
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as a digital-only
input, are a great way
to get 360 degree movement.
So this is really
important for players
who have a standard gamepad
without analog thumbsticks.
The D-pad behaves really nicely.
We spent a lot of work
making it work really nicely
as an analog supplement.
And Face Buttons.
You can add nuance to your game
by using the pressure
sensitive values.
Maybe you have a soccer
game and you gate how hard
or soft your passes and your
shots are based on how hard
or soft the player
taps the button.
And please remember
to tell the player
when you're using
pressure-sensitive nature
of the controls.
Nothing's worse than me
playing a soccer game for a week
and seeing all my shots fly
over the net hard and fast only
to realize sometime
in the future, "Oh,
they're actually wanting
me to use a little nuance
when I'm playing the game, and
I need to use a softer touch."
And that relates to how are
you going to be a good teacher
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And that relates to how are
you going to be a good teacher
of your controls to your player?
What you see on screen
here, a visual overlay
that is a reference for players
to know what game inputs do what
in your game, is a
great starting point.
But, we want to be
not just a teacher,
but we want to be
a good teacher.
So let's look at what
Leo's Fortune does.
This is the opening level.
I've dropped into game play.
And the first thing I see is a
little floating icon over top
that suggests I press left
and right on the D-pad.
This is great for a
number of reasons.
It keeps me in the experience.
The game has not paused.
I don't have any overlays.
And I can move the
character back
and forth while this is
onscreen the entire time.
It's iconic and it
allows the player
to progress at their own pace.
And it's out of the way.
It's down in the terrain where
it's not blocking my view.
After playing with
that for a little bit,
I come to the first portion of
the level where I need to jump.
And again, in a similar fashion,
they kindly and gently suggest
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And again, in a similar fashion,
they kindly and gently suggest
that I press the A button.
Now important to note here also,
is they're not overwhelming me
at the start with all the
controls for the entire game.
They're introducing it
in piecemeal fashion
so that I can understand
it, practice it
and then learn the next step.
All right.
Where are we in the talk?
I've given you an overview
of game controllers.
We know which controllers
are connected to our system.
We know how to read
the controller inputs.
So now it's time to talk
about what's new in iOS 8.
And I'm happy to tell you
about a brand new feature
called Controller Forwarding.
This allows your game controller
with the snapped-in iPhone
to be used as a wireless
controller
to control another device
like an iPad or a MacBook Air.
This works over Bluetooth
and Wi-Fi.
And Bluetooth devices
-- or sorry --
the two devices need
to be signed
into the same iCloud account.
Since this is a new
feature, let me walk you
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Since this is a new
feature, let me walk you
through the flow
of what happens.
So we've installed
the game on the iPad.
It is not installed on our
phone in the controller.
We start playing it.
And then through the
magic of continuity,
a little gamepad
icon is displayed
in the lower corner
of the lock screen.
The player swipes up on that
and a wireless connection
is established.
And all the controller events
are forwarded wirelessly
and automatically to the gamepad
-- or to the iPad, sorry.
Now, which of this do you
have to take responsibility
for and program yourself?
Absolutely nothing.
As long you're calling
startWirelessController
DiscoveryWith CompletionHandler,
all this happens for free.
And the beauty is that
the controller appears
as any other in the framework.
You can read the button
presses off of it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You can read the button
presses off of it.
You can find out when
that controller connects
and disconnects.
You can query whether it's
an extended controller
or a standard controller.
It just works.
Now we figured since phones have
accelerometers and gyroscopes,
why don't we go ahead and send
that information along, too.
So this way, if your game
supports motion control
and game controllers, you don't
need to capture that information
and set up a side communication
channel and serialize
and de-serialize the data
and then synchronize
it on the other end.
You don't have to worry
about any of that.
We take care of that for you.
And all this is found in a new
profile we're calling Motion.
Motion is of the
class type GCMotion.
So let's look at
what that gives us.
Here we have four properties,
gravity, user acceleration,
attitude and rotation rate.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
attitude and rotation rate.
Now these are probably very
familiar to those of you
who have used Core
Motion before.
Let's look at each of these
in a little more detail.
First: gravity.
Gravity is type GCAcceleration.
And it returns a vector
oriented in the direction
in which gravity is
pulling the device.
The units are in G's.
And so if the device is
lying flat on a table
with the screen facing up, this
vector will return you a value
of 0, 0, negative 1
because gravity is pointing
in the exact same direction
as the negative zAxis.
Next we have userAcceleration.
This is used to find out
inertial acceleration
when the player is
shaking the controller.
It excludes gravity.
And the units are also in G's.
So in the same scenario, when
the phone is at rest on a table,
the acceleration
will be 0, 0, 0.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the acceleration
will be 0, 0, 0.
We also have attitude.
This allows you to know the
3D orientation of the device.
And from that, you can determine
the yaw, the pitch and the roll
of the device, if that's
important for your game.
Lastly we have rotation rate.
This tells you how the phone is
being spun and on which axis.
And the units here are
in radians per second.
Now, at a high level,
you're going to want
to organize your code like this.
First you check the
motionProfile on the controller
and whether or not it's nil.
If it's non-nil, that means
our controller supports motion.
And we go ahead and use that.
Otherwise, we use
motion from the device.
Now, I'd like to
draw your attention
to the overall structure
of this if-else statement.
In our example, our iPad
was running the game.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In our example, our iPad
was running the game.
And the iPad has
motion capabilities.
But we don't want to use
those motion capabilities
because that device
is just sitting there
on the table while we
play with our phone.
We want to use the phone's
motion capabilities.
Hence, we prioritize
that if it exists.
A few notes as well.
These axes you see drawn on
the screen with the iPhone,
they move with the
phone as it moves.
So, what in this orientation has
the positive Y vector pointing
straight up towards the sky,
when this phone is snapped
into a controller and held
in landscape orientation,
positive Y is now
pointing to the left.
So this is just something
to be aware
of as you're coding your game.
Also, Motion data
will be jittery.
You're going to want to apply
some sort of a filter to this,
maybe a running average of
the last x number of frames.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
maybe a running average of
the last x number of frames.
Also new for iOS 8 is
Idle Timer behavior.
The idle timer is
what governs whether
or not the screen turns
off to save battery power.
And in iOS 8, this is
handled automatically for you.
Playing a game with the gamepad
will keep your screen alive.
In iOS 7, you're going to
want to handle this yourself.
And if you want to do
that, this is what you use.
You set the IdleTimerDisabled
on the UIApplication instance.
Now be careful here.
You also need to know when
to re-enable the idle timer.
And that's out of the
scope of this talk,
so please read the
documentation on that.
Also of note is that motion
apps need to do this as well.
Moving the phone does not
keep the screen alive.
And so regardless of which OS
you're on, you have to do this
if you're using motion
in your game.
All right.
Let me talk about a
little bit more guidance.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let me talk about a
little bit more guidance.
All controllers have
a pause button.
So if your game supports
game controllers,
you have to implement
a pause button handler.
This is found on
controller instances.
It's called
controllerPausedHandler.
And whenever this is called you
want to treat it as a toggle.
So if you're in the middle
of active game play,
pause the game.
If you're not -- or sorry,
if your game is paused
and this is called,
then un-pause.
If you're in a context where
pausing doesn't make sense,
like a main menu, it's entirely
appropriate to do nothing.
Also: Player Indicator LEDs.
Always set the -- sorry,
always set the playerIndex
on the controller whenever
you find a controller
and are about to use it.
This gives players feedback that
the controller is recognized
by the game and being used.
So in a single player
game, it's as easy
as checking the player index.
If it's unset, as denoted
by this handy flag called
GCControllerPlayerIndexUnset,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
by this handy flag called
GCControllerPlayerIndexUnset,
then go ahead and set
the playerIndex to 0.
This is a zero-based index.
So this will light up the
first LED on the control pad.
Now let's talk about
multiple-controller games.
In all of my examples, I'm
calling self.myController.
Well, when you have a
multi-player split-screen game,
or a game where players
are sharing the same view,
you're going to have multiple
controllers, so keep track
of them with an array --
myController is plural.
And you want to make sure
to set the playerIndex
for all the controllers.
And I'll show you how
to do that momentarily.
Also, consider which controllers
you allow to navigate menus.
It can be very confusing if you
have multiple players all trying
to navigate a menu at the same
time competing with each other.
In that case it may
be appropriate
to let only Player
1 control the menus.
Also, think about what
a disconnection means
in the context of a
multi-player game.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the context of a
multi-player game.
In some games like a
one-on-one tennis match,
game play cannot proceed
without the second player.
And so in that case,
we pause the game.
Other types of games with live
drop-in/drop-out game play,
can proceed with game play as
players connect and disconnect.
So in that case, you
don't necessarily need
to pause the game play.
It can proceed.
So take whatever action is
appropriate for your game.
Now let's look at a code sample.
Here we are back in our
friend setupControllers.
And we're looping over every
controller in our array.
And for each one of those we're
checking if the index is unset.
If we find even one
controllerIndex that's unset,
we display a player
picker to the UI.
This allows them to choose which
character they are in the game
or to know which
quadrant they're in.
And within there, we're
going to set the playerIndex
so that the players know
what they're controlling
on the screen.
Now another piece
of design guidance
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now another piece
of design guidance
that I'd like to give you...
Very important: respond to
game controller inputs early.
When a player snaps in
a controller and taps
and launches your game
from a home screen,
what is the first thing they do?
They start mashing
the buttons, right?
They start pressing
the A button.
They start moving the D-pad.
They want to see if your game
supports game controllers.
And every response you make
or response your game doesn't
make communicates something
to the players.
And responding early
tells players, yes,
this game supports
game controllers.
Now splash screens, introductory
cinematics and the main menu
at the latest are great
opportunities to respond
to controller inputs early.
The longer you wait and the
deeper a player has to get
into your game to discover the
game controller integration,
the more likely they
are to miss-assume
that this game doesn't even
support game controllers
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that this game doesn't even
support game controllers
and unplug their controller
and just use native inputs,
missing all the hard work
you put into your game play.
Let's look at what
Leo's Fortune does.
So the game launches.
It starts off with
the splash screen
and introductory cinematic.
And pressing the A button
allows you to skip the rest
of that cinematic and jump
straight to the main menu.
So right there, within a few
seconds of launching the app,
the game has told
the player, yes,
this game supports
game controllers.
Now, at the main menu,
and this is really slick,
only when a game controller's
attached do the green portions
of the buttons glow,
telling you which button
on the screen is active.
By using the D-pad
and navigating over,
another button begins
glowing instead telling you
that that's the active button.
So once again, the game
is confirming now, yes,
this game supports
game controllers.
And the player is highly
likely to enter game play
and enjoy the experience
with the controller.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and enjoy the experience
with the controller.
Now, another reason, if that's
not enough, to mechanize your UI
for game controller support is
because we have standard
controllers,
which are standalone --
or sorry, extended
standalone controllers.
These controllers, as
I mentioned previously,
don't have the touchscreen
easily accessible.
And so all the inputs are coming
exclusively from the controller.
So support mechanizing your
UI so that game controllers
which are standalone
are supported.
So what did we learn today?
I gave you an overview of
what the game controllers are
from Apple.
Basically it's a two-part
-- it comes in two parts.
The first part being
the MFi specification,
and the second part being
the GameController framework.
We talked about how
we find controllers,
how we read inputs off
of those controllers.
I talked about what's new,
forwarding, motion forwarding
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I talked about what's new,
forwarding, motion forwarding
and the idle timer behavior.
And throughout we talked
about design guidance:
things that you can do
to make the experience
of game controllers
really intuitive
and integrated really
nicely with your game
so the player can just
enjoy your underlying game.
For more information
on game controllers,
please contact Allan or Filip.
For related sessions, if
you want to make a 2D game,
check out the SpriteKit
sessions:
"What's New in SpriteKit"
and "Best Practices
for Building Spritekit Games."
If you're interested
in 3D games,
check out the SceneKit
presentations:
"What's New in SceneKit" and
"Building a Game with SceneKit."
Thank you so much.
I'm really excited to see what
you're going to do with this.
[ Applause ]