Using Gesture Recognizers with Cocos2D and CCNode + SFGestureRecognizer

One of the most interesting and useful features that iOS includes is the gesture recogniser. Gesture recognisers are objects that are attached to views, and look for specific patterns of touches. When a gesture recogniser notices that the user has interacted in the way that it’s looking for, it notifies a delegate.

Prior to gesture recognisers, handling complex gestures like pinching or rotation was a lot harder than it had to be. Time was, developers had to manually track the touches involved in a gesture, and measure how they were moving over time; nowadays, we just do this:

And then have a method that gets run when the user interacts with the view with a rotation gesture:

Gesture recognisers are one of those APIs that are completely obvious once you think about them, and solve a potentially tricky problem very cleanly. However, gesture recognisers have to work within the bounds of how views in iOS work, which can have some interesting consequences for using them in games.

To put it briefly: how can we use gesture recognisers in OpenGL-based games?

Here’s the problem: gesture recognisers work by being attached to views; when a touch lands on the screen, UIKit determines which view the finger belongs to, and this information is used for tracking gestures.

However, all OpenGL games do their main work using a single view - the OpenGL view in which all rendering takes place. This is true regardless of whether you’re drawing complex 3D graphics or simple 2D sprites. And this can mean that gesture recognisers are trickier to do, because when the finger lands on the screen, UIKit will say, “hey, the view that was touched was the OpenGL view! Job done, you’re welcome, see you later!”

So, if we want gesture recognisers, and we’re drawing using a single OpenGL view, what needs to happen is this that gesture recognisers need to be added to the OpenGL view, but are limit the areas in which they’ll look for touches to areas that depend on what’s happening in the game.

This is possible through the use of the gestureRecognizer:shouldReceiveTouch: method in the UIGestureRecognizerDelegate protocol. If a delegate implements this method, it’s possible to make a recogniser only track touches in certain areas.

This is the approach taken by Krzysztof Zabłocki’sCCNode+SFGestureRecognizers, which is a zlib-licensed extension to Cocos2D. CCNode+SFGestureRecognizers performs some very clever hacks, including dynamically creating classes that operate as delegates and using the Objective-C runtime’s new associated object support, that allow you to add gesture recognisers directly to CCNodes.

We’re using CCNode+SFGestureRecognizers in Leonardo’s Moon Ship, an adventure game that we’re looking forward to talking about further in the coming weeks and months, to support dragging items from the player’s inventory onto items in the game world.

leo-dragging.jpg
December Developer Training in Melbourne

This event has now passed! Our next training is in Sydney, February 2013!

We're exceptionally pleased to announce another training course! Join us for three days of intense iOS training in Melbourne, where you'll learn Objective-C and iOS development from the ground up. We'll be running the course from December 14 to 16, in the heart of Melbourne's CBD.

For more information, look no further than this here internet web page site link!

Captain: Add Hooks to Your Code
captain-hook-halloween-costume

So, here’s another something that we’ve been working on.

Captain is a lightweight library that lets you add hooks to your code, using JavaScript. We designed it for two reasons:

  • If you’ve got a bunch of objects, each needing a small amount of custom objects, creating a subclass for each variation is cumbersome, and devising a sophisticated data-driven architecture is often overkill and sometimes impossible for your use case.
  • Extremely rapid iteration, skipping the compile/install phase, is awesome.

Many of the games we make involve a lot of special-cased behaviour. Like all halfway decent developers, we try to minimise this, but sometimes you really just need a sprite that walks two feet left, three right, and does a twirl, and there’s nothing else in the game that you can generalise into a nice, reusable system.

We’ve already found some great results in allowing field-testers and non-programmers to make modifications to iOS applications by exposing certain resources to iTunes File Sharing. For example, if you’re writing an app that has a lot of sound effects, it makes your sound designer’s life a heck of a lot easier if they can open up iTunes and drop in a replacement sound file, and see how it sounds in your app without having to get another build.

What we ended up doing was using JavaScriptCore, which is the built-in JavaScript runtime on iOS, to create a very loose binding to Objective-C, and then allow Objective-C objects to delegate parts of their behaviour out to JavaScript.

Captain isn’t a scripting bridge. This means that it’s a slightly different thing to libraries like the excellent jscocoa. Most scripting bridges allow you to write entire apps in other languages, which is super cool. However, we really like Objective-C, and find that using the native language for the iOS platform is the best way to approach most things.

Captain, therefore, lets you expose very specific parts of your application’s behaviour to scripts, effectively creating a domain-specific language for your app. Plus, it’s all designed to be super lightweight.

An important note: Apple’s rules say your app isn’t allowed to get scripts from the outside world and run them. This means that you can’t use Captain to build a plugin architecture for your app, but you can use it during development to make your edit-build-compile cycles faster and more flexible.

How to use it

The most basic use case for Captain is when you want to run a JavaScript function, and get back whatever the function returned.

When you work with Captain, you first create a JSContext object, which serves as the execution environment for all of your scripts. You can create as many contexts as you like, but each one is kept separate from each other, and variables won’t be shared between them.

Creating a JSContext looks like this:

Once a context has been set up, you can give it JavaScript code to execute. You do this using the evaluateScript:error: method:

This method returns whatever the result of the script you passed in was. If the JavaScript code threw an exception for any reason, the error variable will contain information about it.

Any values returned by the evaluateScript: method are returned as Objective-C objects, and are automatically converted from their JavaScript type into the most appropriate Objective-C type. Strings are turned into NSStrings, numbers become NSNumbers, and JavaScript objects get turned into NSDictionary objects.

Calling native functions from JavaScript

Basic stuff, right? However, the fun stuff happens when you give the JavaScript code some native functions that it can call.

You can add register functions in the JavaScript context that scripts can call, by creating a block object and giving it to the JSContext. These block objects take one parameter, an NSArray that contains the parameters that were passed in from JavaScript, and return an id, which will be automatically converted back to a JavaScript value.

Example time!

Once you’ve added a function, you can call it from JavaScript.

Calling JavaScript functions from native code

Things get really interesting when your native code can call JavaScript code. Because you can load the new code at run-time, or even replace code without having to re-launch your app, you get a crazy amount of flexibility.

If you run some JavaScript code that returns a function, it will be returned to as a JSFunction block, which can be called just like a function.

Working with native objects in JavaScript

Working in only numbers and strings is boring. What would be really awesome is if Captain could let JavaScript work with Objective-C objects in a clean, simple way that had zero programmer overhead, and let the developer write clean code in both languages.

Wait, you can? BONUS.

Any property that uses the standard compiler-generated setter and getter methods can be modified by JavaScript code.

In addition to working with data, you can also call methods on Objective-C objects from JavaScript.

But doesn’t Objective-C have crazy weird method names that look ugly in other langauges?

That’s right, it does. That’s part of the reason why Captain only exposes certain methods to JavaScript.

If your Objective-C object has a method named handleFoo:, which takes one parameter, it will be exposed through JavaScript as “foo”. Only methods that meet these requirements are callable from JavaScript.

The reasoning behind this is that Captain is intended to be a simple way to expose limited amounts of domain-specific functionality to a scripting environment, rather than a comprehensive scripting bridge. By only having to support a limited amount of interaction between native code and the script, life is made easier for both worlds.

Example! Here’s an Objective-C object:

And here’s code that interacts with it:

Loading scripts

The final piece to this is in loading a large number of JavaScript functions, which native objects can use.

Let’s say you’ve got a JavaScript file named “UsefulFunctions.js”, and it contains this:

You can register the entire set of functions contained in the script using the loadScriptNamed:error: method.

Doing this causes the JSContext to look for the file “UsefulFunctions.js”, first in the Documents folder, and then in the built-in bundle resources. Because it looks in this order, you can modify the app’s functionality by simply dropping in a replacement file with the same name in iTunes File Sharing, and re-launching the app.

Delegating behaviour to JavaScript

The functions that you load in can also be used by Objective-C objects to delegate some of their behaviour to.

For example, say you have a JavaScript file ‘Foo.js’, containing the following code:

Once Foo.js is loaded in, you can call the function, and additionally provide it with an object to use for the this variable.

With this method, your classes can easily call out to JavaScript when they need some work done.

Conclusion

We hope you enjoy using Captain, and we’d love to see what else you do with it. Captain is on GitHub! We welcome pull requests, and if you’ve got any questions, shoot us an email at lab@secretlab.com.au

Enjoy!

Apple, CodeSecret Labfoss