Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[Applause]
>> DAVE ADDEY: Hello.
Welcome to Deeper into
GameplayKit with DemoBots.
We created a new game sample
this year called DemoBots.
If you would like to
play along at home,
you can download
the sample today
from
developer.apple.com/spritekit.
This game takes advantage of
lots of the things you learned
about in the What's New
in SpriteKit session
and Introducing GameplayKit.
Check it out if you couldn't.
We couldn't make a
game called DemoBots
without giving you a demo.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
without giving you a demo.
Let's take a look and
see how the game plays.
We start a new game, you're
PlayerBot, this guy here.
Your job is to find and
fix all of the bad TaskBots
on each level before
the clock runs out.
Here we have two good TaskBots,
you can tell they're good,
because they've got green faces.
If I run around the corner here,
we find the first bad TaskBot.
He's a GroundBot in this case.
When he spots me, he will
charge toward me and attack me.
I lose some charge.
That's okay.
I have a beam to zap him with.
After I zapped him for a few
seconds he's fixed and green.
Let's see where he's going.
Here we have another
bad TaskBots,
I have to fix him as well.
There are two more on
this level that I need
to fix before the
timer runs out.
Unfortunately they
have a tendency
to attack one another as well.
They can turn each other bad.
That's what happened there.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
That's what happened there.
Once all of them are fixed
the level is complete.
[Applause]
There's a level 2.
Level 2 introduces a new
character, FlyingBot,
here is a FlyingBot on the side
hanging out, doing his thing.
If you find a bad FlyingBot,
they have a different
attack.When we bump into him,
he does a blast attack
affecting everybody
within a certain radius.
The good news, if you fix
one, they do a good attack
that will cure any other
FlyingBot within range.
Follow him over here.
He's almost certainly going
to bump into another bad one.
He avoided him.
Good. I can cure
this one as well.
He's almost certainly going to
do a really, really silly thing,
to go all the way around
here to a great big nest
of the things around the corner.
This is almost a certainty.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We can use his benevolent
blast after he's been made bad,
he can cure one, two,
all of them in one go.
[Laughter] One more to go.
Oh no! [Laughter] This
is not going well.
[Laughter] One more.
Then the level is complete.
[Applause]
So that's DemoBots.
I would like to take
a look at tools
and technologies we have used
to make this game a reality.
You saw our TaskBots had lots
of animation states
they went through.
A thing we tried to do is keep
the textures, images we needed
for those to a minimum to
keep the app size realistic.
We decided to keep the new
action editor in SpriteKit
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to create the actions as --
animations as actions
rather than textures.
The zap animation you
see, the zap beam,
that's a reference action
we have made and applied
to both the GroundBot
and FlyingBot.
If we click into that action
we can see it is made of lots
of small move actions
one after another.
Because it is a reference
action we can create it once
and apply it to all
TaskBots we have regardless
of the orientation.
Because it is a reference
action, we can create it once
and then go and change
it, the source,
seeing that change going
everywhere, we don't have
to change it in multiple places.
We use the action editor to
create the beam animation.
It is only fired for so many
seconds, we want that beam
to decay over time and we
create that actions editor too
so that we can see it visually
and use it in the game.
All of the assets in the
game have been created
in asset catalogs and out
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in asset catalogs and out
of Xcode 7 these asset
catalogs are converted
to texture atlases for us.
That means that we get the
best possible draw performance
when used in a game.
We have a lot of these images.
It also gives us a way to
specify the right images to use
for different devices.
This helps us to
optimize the size further.
When we were designing our
levels we used a reference
height of 768 points, the yellow
box there, that's our reference
for how much level we wanted
to be visible onscreen.
At that size our PlayerBot is
120 points, and now if we use
that level on an iPad, the
iPad is also 768 points,
so we'll know the size of
the PlayerBot on screen,
and he's going to be 120
points and we can work
out the image size we need
to get a sharp image
of him onscreen.
If we scale that level down
to an iPhone with, say,
a 320 point height, he
actually is a bit smaller,
he'll be 50 points high.
We don't need such a
high resolution texture
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We don't need such a
high resolution texture
as on the iPad and
we can save space.
If we look at all of the devices
that we support with DemoBots,
iPad, iPhone and Mac, as that
Scene height scales we can work
out the corresponding
player height.
The iPhone 6 is slightly bigger
than the 4S through the 5S,
we used the same assets, it
is so small, you don't notice.
This then means you work out
the 1X, 2X, 3X assets we need
to make them look sharp
without using any more pixels
than we needed to.
We rounded the iPhone 6
Plus down to 180 pixels just
to keep things simple.
We can set up the assets
we want for each device.
We have a lot of
these in the game.
I really, really do
mean a lot of them.
There are thousands.
All of these character
assets are actually generated
from a 3D rendering output.
Which we can also connect.
We automated that creation
of the asset catalogs too.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We automated that creation
of the asset catalogs too.
The JSON file here,
the one frame
of the PlayerBot's
walk animation,
and we can create
these automatically,
this is what Xcode creates
with the asset catalogs
and we can automate the process
of picking the right images
for the right device and we'll
release the Asset Catalog format
reference in a future seed
to make it easier for you
to create these yourself
as well.
Another SpriteKit
feature that we used
in the game was SKCameraNode,
before that, if we wanted
to move the view around
the level, we actually had
to move the world, we had
to move the level
itself beneath the view.
with SKCameraNode
things are much simpler,
the camera is a node
in the scene.
It has a position.
Because it has a position,
we can move it around just
by changing that position.
Same as any other node.
It is easier to change where
the view is currently looking.
Because it is a node we can
do node-like things with it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Because it is a node we can
do node-like things with it.
We can apply constraints
to that node for example.
We can use this to constrain
the node's Camera position
to a PlayerBot position,
when he runs to the corner
of the screen, we have a lot
of black space around him,
not as much level as we
would like on the screen.
We can make it better to
use a second restraint
to make sure we're never too
near the edge of the level.
That's how that looks.
We follow the PlayerBot
generally speaking
but if he's near we
stop following him
and keep more level onscreen.
This is easier to see when
the enemy bots attack you.
How do we do that?
We start by working out
the banding rectangle
of the entire level.
We then make a smaller
rectangle in set from that based
on the screens width and height
that they're viewing
the game on.
We make sure that the Camera can
never move outside of that box.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We make sure that the Camera can
never move outside of that box.
We constrain it to
that rectangle.
Then when the Camera moves
around following the player
it never gets too near
to the edge of the level.
Because the Camera is a node we
can also add child nodes to it,
which is good for
heads-up displays
like our timer label
we have in the game.
We don't want this
label to move,
we want it to stay
top and center.
So we add it as a child of the
Camera rather than had a child
of the Scene, that way,
as the Camera moves
around the label moves
with it and it is easier
to keep the permanent features
onscreen in the right place.
Those are some of the
SpriteKit features
that we have used in the game.
Let's look at some if of the
GameplayKit ones we used.
the first is GKStateMachine,
you would have seen
from the GameplayKit talk how
you can use state machines
to track what's going on
with characters and levels
and other elements of your game.
We use this amongst other
things for the PlayerBot,
this is the states he has.
He starts in his Appear state
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
He starts in his Appear state
where he teleports
into the game.
The nice thing about using
custom states for this is
that they can make sure that
the right thing happens.
They start and it starts a
timer, after the right amount
of time passes, it
moves straight
to the player control state
and turns on the input use
so you can control it.
If he's hit we move into the
Hit state, when we have this,
we enter a different
animation, the jump animation
and the player can't
be controlled when hit
and it turns off
input, tracks the state,
when the time has
passed, move him back
to the player control state.
If we get hit enough
times we have to recharge
and instead the Hit state
moves to the Recharging state.
That tracks how long we
have been recharging for.
The state actually does the job
of adding more charge
back on to the player.
Eventually when recharged,
it goes back
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Eventually when recharged,
it goes back
to the PlayerControlled state.
That's a reasonably
complex set of behaviors
that the player can have.
By using a state machine to
not only codify the states,
make them happen, define the
right things to happen moving
from state to state it
is easier to make sure
that only the right thing
could happen in game.
Our player Bot can't do
something we don't want him to.
We use this also
for the game itself.
We have an active state
when you play a level,
if we hit pause we go
into a pause state.
This handles overlay,
by removing that overlay
when you move out
of the pause state.
If I complete the level,
we have a success state.
Again, this handles all of the
tasks of showing the buttons
and overlay for when
we succeeded.
The state knows the right things
to do in these situations.
Another aspect of GameplayKit
we have used is entities
and components.
Now components are a
really good way to package
up self-contained
pieces of functionality
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
up self-contained
pieces of functionality
that are shared amongst
different entities in the game.
We have three entities,
we have PlayerBot,
GroundBot and FlyingBot.
They have some things in common.
They all need to be
rendered into the Scene
and they all need a shadow.
So we have a render component
and a shadow component
to do these things.
They also all have animation,
physics and intelligence,
which is the name we've given
to the state machine tracking .
At this point you may think
they look very similar.
Why not have a base
Bot class providing all
of the functionality
to all three?
They're not actually
that similar after all.
The PlayerBot needs
input and we can control
that from a game controller or
keyboard or from touch control.
The GroundBot and
FlyingBot don't need that,
you never control
those characters.
They're driven instead
by an agent.
You will see later
how we use agents
to drive the characters
around the level.
Player Bot doesn't
need an agent,
he's powered by your input.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
he's powered by your input.
Likewise, GroundBot
and FlyingBot have rules
telling the what they should do
in a given situation and we'll
look at those later on as well.
Player Bot doesn't, you
tell him what to do.
PlayerBot and GroundBot do
have one thing in common,
they both have a
movement component.
This component's job is to
say if I'm here and I need
to move this distance
at a certain angle make
that happen within the Scene.
Player Bot uses that
input to render it
in the Scene, move him around.
GroundBot uses it for a
charge forward attack,
he moves from here
to charge forward.
FlyingBot doesn't
need that capability.
He doesn't move so he
doesn't have that component.
These components, it is a
good way to break up bits
of functionality
and assign them only
to the entities that need them.
So these are some of the
ways we have used GameplayKit
functionality in the game.
I would like to invite Dave
on stage to tell you more
about how we have
used GameplayKit
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
about how we have
used GameplayKit
to create our games
Logic and Gameplay.
[Applause]
>> DAVE SCHAEFGEN:
Thank you, Dave.
So when we think about
Logic in Gameplay,
one of the most immediate
things that jump to my mind
when I think about logic
in gameplay is the actual
intelligence of my adversaries.
a big piece of that
intelligence is going
to be what I can
actually do in that space?
As we saw earlier in the demo,
if I'm a good Bot I'm moving
around the level,
patrolling circuits,
keepings things moving.
If I'm bad, I may want
to attack the PlayerBot.
Got him that time.
I may also wasn't to turn other
TaskBots bad so I have help
when it comes to
getting the player Bot.
Got that one too.
Finally, I may move around the
level opposite direction messing
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Finally, I may move around the
level opposite direction messing
with induction, currents in
the circuits, causing problems.
Not as interesting, but now
that we know what we can do
the question becomes how do we
decide what we are going to do.
In DemoBots we chose
to implement a fuzzy logic
system with GKRuleSystem.
The advantages of this were
we could still take in a lot
of information about the
current state of the level,
what was going on, and
have our characters react
to that information.
We could do that without
having if else trees that went
on for hundreds of
lines of code.
We have simple rules and rely
on the simple rules to interact
with each other and
allow complex
and interesting behaviors
to emerge.
So if you're unfamiliar with
fuzzy Logic, I'll give you a bit
of an idea of what we're
talking about here.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The fuzzy that we're referring
to is actually the fact
that everything is not black
and white, true and false.
Our rules aren't
mutually exclusive.
We modeled our rules on natural
concepts and tried to think
of them like sentences that
you would say to a colleague.
The PlayerBot is nearby.
. These are implemented in
the fuzzy TaskBots rule class.
It is a subclass of GKRule.
We have actually tied the fact
of the rule to the grade here.
Only we assert the fact if the
grade is greater than zero.
This is interesting because
we made grade a function
of the actual state information
available in the level rather
than it being something that
you set up at the moment
that you create the GKRule.
Let's look at what those -- what
that proximity rule looks like.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's look at what those -- what
that proximity rule looks like.
Thinking about being
near or far,
and let's actually use a
graph to take a look at this
and see how these
functions work.
Up here near the origin you can
see that we have a PlayerBot
and out about as far away
as we can get them
you have the TaskBot,
if I bring in a graphical
representation
of the far rule it is pretty
simple to see that the grade
for the far rule is going
to be one in this case.
If I pull in the
medium and near rules,
it is clear that they're going
to have a zero grade
in this situation.
Now that doesn't lineup
seeming all that interesting,
it does kindof seem black and
white and a bit true/false.
If we move our task Bot closer
you can see what I'm talking
about clearly.
Here the grade for
the far rule is.
75 and for the medium
rule, about.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
75 and for the medium
rule, about.
25. If we move him still
closer, things switch
and we have membership in both
the near and medium rules.
>> Now that you have
seen how the functions
that we have calculate
our grades,
let's look at how we actually
make decisions once we have
calculated those grades
for each of our rules.
So here are the rules that
we actually have in DemoBots.
Our first step, like I
said, is to go ahead,
evaluate them, calculate
the values.
The next thing we're going to
do is combine a few of them
to decide these are contributing
factors that we would want
to pay attention to when
hunting the PlayerBot.
Read like a sentence, you
can tell how this is working
like a telling of a story.
If a percentage of
bad TaskBots is high
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If a percentage of
bad TaskBots is high
and the PlayerBot is
a medium distance away
and the good TaskBot is
also a medium distance away,
I want to hunt the PlayerBot.
The reason I would want
to do that in this case,
the way I think about it,
is there already are a lot
of bad TaskBots on this level,
I don't need to turn them,
I may as well hunt the player
at this point.The trouble I have
is I have 3 different grades
to represent this idea of
hunting the PlayerBot now.
So we're going to use
GKRuleSystems minimum grade
for facts to grab the minimum
grade for each of the facts
that we want to combine.
Now, the reason why we pick
the minimum is our mandate
for hunting the PlayerBot based
on that information is only
as strong as the weakest
fact contributing to it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as strong as the weakest
fact contributing to it.
We could combine those
rules in any number of ways
and get several different
indicators saying
to hunt the PlayerBot
or to hunt TaskBots.
This is the stage
where we get to needing
to actually defuzz-ify the
rules to get one simple number
to really represent the idea
of hunting the PlayerBot.
In this case we just
take our facts,
we use the reduce
function in Swift
and the max function
to combine things.
In this case we actually --
we want hunting the PlayerBot
or hunting the TaskBots
to be represented
by the strongest grade
available of the ones we have.
Looking at these numbers,
it is pretty obvious
we'll hunt the PlayerBot.
Now that we know we want to hunt
the PlayerBot the first problem
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now that we know we want to hunt
the PlayerBot the first problem
we've got is how do I
actually get to the PlayerBot.
Often that's pretty
straightforward,
just move in a straight line
and you'll eventually get there.
Obstacles present a challenge,
here you see the FlyingBot
getting hung up there.
You remember from a sample from
a couple years ago, Adventure,
the goblins in that sample, were
really fond of this behavior.
We decided to do
something about that,
and in GameplayKit we have
made it really easy for you
to take advantage of pathfinding
in a world and get your Bots
or your enemies moving
easily like this.
It has great conveniences
for working with things
when you're using SpriteKit for
a game and it is really easy
to get things up and running.
Only a few lines of code.
Let's take a look at what
those lines look like.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's take a look at what
those lines look like.
So the first thing you're going
to do is actually get the
polygon obstacles making
up the level.
In our case we'll first search
for our nodes within our Scene.
They have all been
named obstacles.
When we have an array of
those nodes we'll pass it
to a convenience function
that SKNodes has in iOS 9,
obstacles for node
physics bodies
to take the actual physics
body you defined to use
that to define the obstacle.
When those obstacles, when we
have the obstacles we're going
to construct an obstacle
graph using them,
and also a buffer
radius parameter.
This parameter is a
little bit of extra spacing
around the actual obstacle.
A good way for you to
think about it might be
as you're walking towards a
doorway and you're walking
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as you're walking towards a
doorway and you're walking
through the doorway you're
not actually going to walk
at one corner or the
other of the door frame.
You're going to aim for a point
in the middle of the doorway
where no part of your
body is actually going
to contact the door when
you walk through it.
This radius helps set that
spacing around the obstacles.
Next we'll take the TaskBot
and PlayerBot positions
and connect them to the graph.
Finally we will ask the
graph to give us a path
from the start node
to the end node.
We'll get back in array
of the individual nodes
that are necessary to get
from point A to point B
and it really is that simple
to turn things from walking
into memory chips and along them
to actually walking around them.
We have a path, we have points,
but how am I actually
going to get there?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
How do I actually get there
and make it look nice?
Well, GameplayKit has
an answer for us again.
This time it is GKAgent 2D.
This is one of my favorite
classes in GameplayKit.
This is a GKComponent,
so it works very nicely
with the Entity/Component system
that Dave talked about earlier.
It is very easy to
get things set up.
You have a GKBehavior
that describes what it is
that you want this agent to do,
it is kind of a container
for GKGoals.
GKGoal, lucky for us in this
case actually has a couple
of different constructions that
allow us to work with paths.
Because the agent
world and the world
of GameplayKit is
different from the world
of SpriteKit the delegate
here makes it very simple
to integrate the two of them.
Let's take a look at
what it actually --
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's take a look at
what it actually --
you know, what the
code looks like.
We'll take that array of path
nodes that we had from before.
We're going to initialize them.
We're going to pass them to an
initializer to create our path.
There is another parameter
here and that is the radius.
What I want you to think
about here is how do
you want this path
to be defined for your agent.
Very small values will result
in your agent treating your
path like a tight rope.
Larger values, you know,
could have them treating it
like an 8-lane highway,
moving all over the place.
You'll want to fine tune
that for what works
best for your game.
Next we'll create a behavior.
Here it is just an
empty behavior,
nothing really going on yet.
Then we'll add goals to it.
These are the two
path-related goals I was talking
about from before.
The first one on the screen,
our goal to follow path is going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The first one on the screen,
our goal to follow path is going
to establish the direction in
which we'll travel our array.
In this case we'll move
forward in a forward direction
from the start position, our
TaskBots to the end position,
which was our PlayerBot.
The stay on path goal is
there to motivate our agent,
to actually remain within
the bounds of the path
that we defined earlier.
Now that we have a working
behavior we assign it
to our agent to get him moving.
the agent works like many
of the other components
within GameplayKit.
It is updated on a cycle.
When you've added it into your
update loop it will notify you
before and after changes,
and this delegate
notification before
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and this delegate
notification before
and after changes
is the ideal place
to actually get things working
and hooked up with SpriteKit.
In WillUpdate, this is
a position that you want
to take the information
from your SpriteKit node
that represents your
agent in Scene and pull
that information back
over and update the agent
because we're doing
this at the front
of the SpriteKit
update loop and the end
of the last SpriteKit update
loop would have involved
simulating physics, applying
constraints to your node
which may have caused
its position to move
from where the agent thought it
was at the last time through.
In AgentDidUpdate, we'll take
the information from the agent,
its position, its rotation,
things of that nature
and pull those back over into
the SpriteKit world updating our
node before it goes
into physics simulation
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
node before it goes
into physics simulation
and constraint application
this time around.
Really, that is what got us
to this point with DemoBots
and an intelligence that
works nicely, moving readily
around the screen, smoothly
interacting with you.
This is something extra I
guess, we left debug drawing
in the sample that
you can download.
You can enable it by
hitting the slash key.
The nice thing about this,
it helps you visualize some
of the parameters we
talked about earlier.
The orange boxes are buffer
radius around the obstacles
and the thick lines that you can
see emanating from the player,
the task Bots are a
representation of that path
that we talked about, the
path radius that was there.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that we talked about, the
path radius that was there.
Now that we have a game, I would
like to invite my colleague
Michael up on stage.
He's going to discuss
with you how
to get your users playing
your game that much faster.
Michael.
[Applause]
>> MICHAEL DEWITT:
Thank you very much.
We'll talk about
taking a fun game
and making a great overall
experience for your users.
I'll boil it down to
one phrase and that is,
of course, time to fun.
How long does it
take for your users
to start really enjoying
your game?
The first barrier to entry there
is really the initial download.
If you're shipping an app that's
too big some users won't be able
to download it over
cellular connection
and it can take a
while even on Wi-Fi.
This is the biggest latency
between your user finding
the app on the App Store
and deciding to get into it.
But we know this 100 mega
download limit is hard
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But we know this 100 mega
download limit is hard
for games to stay under.
We'll go back to DemoBots
and see how we handle this.
If you focus on the PlayerBot
you notice we're not looking
strictly top-down
on the character.
It is not a 2D game but
an isometric feel here.
We pulled that off by
giving this character
multiple orientations.
As you move the character
around the map,
we'll swap out the texture
representing the character
to get this perspective.
When you add all of the extra
frames in there, it takes space.
It is only 6 megabytes,
that's small for a game.
When you consider it is 6
megabytes times the three bots
we have times the different
actions they can perform,
we need the orientation frames
for the Player Bot when idling,
hit, walking around, so
this starts to add up.
Traditionally, that means that
all of the assets plus the 1X,
2X, 3X versions, they're
bundled in the app.
We have a better
story for this now.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We have a better
story for this now.
You may have heard about
it earlier this week.
If you use asset catalogs
you will take advantage
of a new feature
called app slicing.
What app slicing will do
is breakdown the 1X, 2X,
3X for the actual device
that they're used on.
This is going to save us a ton
of space right off the bat just
by using the asset catalogs
and letting the App
Store slice it for us.
It is not only that we
now save a bunch of space,
it is what can we do with
the extra room on our app.
I'm looking at the
graph and it is empty.
In DemoBots, it has
practical implications.
We started with 8 orientations
and I'll show you a video here.
Watch closely.
The PlayerBot, it looks like
it is facing directly forward,
you will notice, watch
the movement closely,
see if you notice something.
So especially when the character
is moving back towards the top
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So especially when the character
is moving back towards the top
of the map there, we
term that skating.
While we only have
the eight orientations
to represent the
characters movement,
the player is supplying 360°
of input, we can be slightly off
of that orientation while
not triggering the next
and you get the strafing
behavior.
More frustrating than that
though was using the eight
orientations means you could
end up in a situation like this
where the PlayerBot is
facing directly forward,
looks like it should be easily
hitting that TaskBot but isn't
because of the debug drawing,
you see the player is aiming
to the side and the user
has no way to see this.
With that extra space we got
from app slicing we
bumped it up a notch.
We went to 16 orientations,
that makes movement feel a lot
smoother throughout the game
so you can see we have many
more animation frames here
to represent that.
Then when it comes to aiming,
now it is much more granular.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Then when it comes to aiming,
now it is much more granular.
The direction where the
character faces almost
corresponds exactly with
where the beam's going to hit.
You click through there,
it is easier for the user
to know what's going
on with the game.
That's app slicing, we use
that in DemoBots simply
by putting our texture
atlases within Asset Catalogs,
it helps us to greatly decrease
the size of the app overall,
but it's more than that,
we can actually improve
the gameplay now
because we have the extra space.
This feature works super
well for assets that you need
in your game all the time.
DemoBots is not much of
a game unless we keep the
PlayerBot around.
But there are other assets
we don't necessarily need all
the time.
For that we have
another technology
that you may have heard
about earlier this week,
on demand resources.
The mild overview of this, it
lets you tag resources just
with a simple string
for download later on.
I'll talk about how we
use this in DemoBots.
The first place,
maybe it is obvious,
we have multiple levels.We
can tag those level 1,
level 2, and level 3 .
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
level 2, and level 3 .
The advantage here, it is
now that we have tagged these
with ODR we can actually say
if the user initially downloads
the game, we know they'll go
to level 1, there is no reason
to include the other
two levels there.
It gets even more interesting
as the game progresses,
because we can predict
with this linear flow
that the user will
proceed on to level 3
so we'll start downloading that
level early and it is unlikely
that they'll replay level 1
to we can recycle some
of those resources.
Let's take it a step further.
If we look closely at
level 2 and you will notice
from the demo and from
this small picture
that only the FlyingBot
shows up in this level
and shows up again in level 3.
Contrast that with a GroundBot
who is in levels 1 and 3.
When we tag the characters
individually it allows us
to breakdown our
resources even further.
If we know the user
is downloading the app
for the first time we only ship
with the GroundBot in level 1
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for the first time we only ship
with the GroundBot in level 1
and we can do the
FlyingBot later on.
And if you're on a device
that's tight for storage,
maybe when the user's playing
level 2 you can purge the
GroundBot and then bring
them both back for level 3.
You can see how we
laid out the tags
in the Project Navigator
under resource tags.
You will see that the assets
for level 1 are tagged
as prefetched, those
are coming down shortly
after the app has been
installed, but not included
in the bundle size, whereas the
other resources can be tagged
for download when
we request them.
That's on-demand resources.
We used it in DemoBots to tag
the resources for later download
which gives us a faster
initial download time.
We can cut a bunch of stuff out
that we don't need immediately.
Overall it helps us keep the
storage footprint small too.
That's the bigger message here.
You can make a more rich game
and have many more assets
because you are still keeping
the same footprint on device
but accessing everything
else on demand.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but accessing everything
else on demand.
We recognize this
does add complication.
When you talk about presenting
the next Scene traditionally you
know it is in local storage,
you can prepare those resources,
and when the user requests
it, you can present.
Now we're adding an
extra complication.
Maybe you need to download
those resources, and of course
with network connection
that download can fail.
If you want to get the
space savings, you will have
to purge the resources
at some point and rinse
and repeat that whole cycle.
This can get complicated
and I want to focus
on how we solved it in DemoBots.
Specifically going back to a
technology that Dave mentioned
at the top of the talk,
the GKStateMachine.
If we use that to model this,
we call it our SceneLoader
and it has six associated
states.
You will notice that
only two of the states,
the downloading resources
and download failed state,
they're actually
associated with ODR
because this is a full pipeline
to model bringing your resources
into memory whether they're
from local storage or need
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
into memory whether they're
from local storage or need
to be downloaded first.
the real advantage we're getting
from using the state machine
is how we model the transition
from states.
If we look closely
at the preparing resources state
we can enforce what are the
valid next states by
overriding IsValidNextState
in our GKState subclass
and we can say the state
machine can only move
on to the ready state if
the Scene is indeed loaded,
or we can move back
to the available state
if the user's canceled
this request.
We're not going to go back
to downloading while trying
to prepare our resources
because we're able to enforce it
with this IsValidNextState
and so it leads
to much more determininistic
behavior.
Alright, so to wrap things up,
I'll share a final few tips we
learned developing this game.
the first, if you're
using on-demand resources,
put that download
request in early.
If you have a predictable
progression in the game,
you can start downloading
level 2 as soon
as the player begins level 1.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as the player begins level 1.
Don't forget about the tools
for ODR that are within Xcode.
You can look at the disk
report tool and specifically
under the on-demand
resources see
if your tags have been
downloaded or are currently
in use or have already
been purged.
This is really useful.
Additionally, if the player
is coming up to a junction
when they'll need
additional resources
and you haven't downloaded
to prepare them yet.
You can modify the
priority of your request.
This means that you can
bump up the loading priority
on the bundle resource request,
it is a scale between 0 and 1
and there is even a constant for
urgent if the user is blocked
and you're trying to download.
We modeled preparing with an
NSOperation queue you can bump
up quality of service there.
Overall in DemoBots we
wanted to do a ton of things,
we really wanted to ship a
sample that showcased a bunch
of different aspects
of developing games
that we thought you would
all be interested in.
So Dave spoke to you initially
about fine tuning your assets
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So Dave spoke to you initially
about fine tuning your assets
for every device, including
special assets for the Mac.
We talked about elegant
character navigation,
without having to write a
ton of movement code yourself
and finally adding additional
assets to improve gameplay
because now we're
slimming down our app.
I'm excited to say GamePlayKit
has ton of great features
and iOS 9 in general to
help you do these things.
To check out how they're used,
you can download the sample
right now from this link,
I encourage you to do so.
Here are additional
links for documentation,
you can contact our
evangelist Allan.
The related sessions we
mentioned throughout this talk,
they have already happened,
you can catch them online.
Thank you all so much.
[Applause]