Categories
Nerd Fun Programming Tech Trends

JavaScript, Swift, and Kotlin Oh My!

This blog post now lives on http://blog.viacom.tech/2017/05/31/the-co-evolution-of-javascript-swift-and-kotlin/ (and it’s much shorter and better!)

 

Categories
Programming

Nothing Changes More Swiftly than Apple’s Swift Syntax

Look at that beautiful Apple Swift code on the right with closures and shorthand argument names! Make’s me smile every time!

I’m enjoying the Stanford University class CS193P: Developing iOS 8 Applications with Swift. It’s free on iTunes U and the instructor Paul Hegarty knows his stuff. He’s a great explainer. I like how first he writes some code in a naive way and then fools around with it, getting it to work and then reworking it like a sculptor with clay. That’s how real programmers work. We write a rough draft of the code, make sure we understand it and then “refactor” it until we’re no longer embarrassed to share it on GitHub.

But even though the class was recorded in 2014 and updated in June of 2015 it’s aging rapidly. We’re already working with Xcode 7 and iOS 9. And all the little minor changes and improvements are adding up.

One problem you might run into is with the second video: More Xcode and Swift, MVC. In this lesson Professor Hegarty wants to show how we can use a form of polymorphism to write clean code. Specifically with Swift supports something that Objective-C did not! In Swift we can have class functions with the same name but different signatures. In the example in the lesson he shows how we can write two versions of performOperation–one that takes two Doubles as arguments and another that takes only a single Double as an argument. Very cool…

Unfortunately this code no longer compiles.

There is an explanation on Stack Overflow (if you can find it). But it’s not really clear as there are a couple of solutions. Perhaps the best solution, like so much on the human side of computer programming, is a matter of taste.

Here is the code and the fix:

    // these functions won't compile with Swift 1.2 and above
    func performOperationWith(operation: (Double, Double) -> Double) {
        if operandStack.count >= 2 {
            displayValue = operation(operandStack.removeLast(), operandStack.removeLast())
            enter()
        }
    }
    
    func performOperationWith(operation: Double -> Double) {
        if operandStack.count >= 1 {
            displayValue = operation(operandStack.removeLast())
            enter()
        }
    }
    // these functions will happily compile
    func performOperationWithTwoOperands(operation: (Double, Double) -> Double) {
        if operandStack.count >= 2 {
            displayValue = operation(operandStack.removeLast(), operandStack.removeLast())
            enter()
        }
    }
    
    func performOperationWithOneOperand(operation: Double -> Double) {
        if operandStack.count >= 1 {
            displayValue = operation(operandStack.removeLast())
            enter()
        }
    }

Because Swift is smart about classes written in Objective-C that you are subclassing from, it forces those subclasses, in this case UIViewController, to obey Objective-C’s limitations. One of those limitation was not allowing polymorphism for methods with the same name but different arguments.

The code is a little uglier, because there is slightly more of it, but it smart of Apple to be as compatible with Objective-C as possible. At least for a few more years 🙂

Categories
Cocos2d-iPhone Programming

Cocos2D-Swift 3.0: A great way to get started developing iOS games!

If you’re new to iOS game development now is a great time to get started. In 2008 a brilliant engineer named Ricardo Quesada rewrote his 2D game engine for Apple’s iOS and released it as open source. It’s no exaggeration to say that hundreds of games, like my own, were developed using Cocos2D—including dozens of hits. Now it’s 2014 and the newbie game developer has several versions of Quesada’s Cocos2D framework to choose from. But for me the branch of Cocos2D devoted to the iPhone and iPad will always have a special place in my heart. Clearly Cocos2D-iPhone (now called Cocos2D-Swift) was the inspiration for Apple’s SpriteKit framework. And once you learn the fundamentals of Cocos2D on iOS you can easily transfer these skills to Android, Windows, and HTML5 versions of the Cocos2d family. Working with Cocos2d-Swift is like “reading Shakespeare in the original Klingon.”

Learning iPhone Game Development with Cocos2d 3.0 by Kirill Muzykov is a book I wish I had when I was learning to develop my first iPhone game. Muzykov patiently covers all the basics (nodes, sprites, actions, text, sound, buttons, menus) and jumps into advanced topics (particles, physics, tile maps, iTunesConnect, Game Center, and in-app purchases) while guiding the reader though creating a game called CocosHunt and using important tools (Particle Designer, Texture Packer, Tiled) and websites (freesound.org, media.io).

Along the way the reader also learns about Objective-C, Xcode, and iOS APIs. You’re still a beginner by the time you finish Muzykov’s book but you’re a well informed beginner and ready to tackle larger and more complex projects—like the next Candy Crush.

One of the best things I like about Muzykov’s book is it’s structure. A typical chapter starts with setting up a project and cycles through segments entitled “time for action” and “what just happened?”. This alternating rhythm becomes a reliable way to digest the material and ensure the author doesn’t wave his hands over new concepts. Almost every chapter includes a pop-quiz. I’m not a big fan of quizzes but if that’s what you need to re-enforce the material Muzykov provides them.

The source code is clean and clearly written with good comments. Muzykov keeps the syntax simple, using “typedef enum” instead of “typedef NS_ENUM” and “#define” instead of “FOUNDATION_EXPORT NSString *const” which is probably for portability. Much of a game is managing state and game developer who follow Muzykov examples in his classes won’t get into trouble.

If you’re new to mobile game development and you want to focus on iOS then Learning iPhone Game Development with Cocos2d 3.0 is a good book for you.

Categories
Programming

Apple’s Snakes and Ladders Swift Example

All the cool kids are learning Swift this week! Not wanting to be left out I downloaded Xcode beta 6 and the iBook introduction and started following long with the code sample in Xcode’s new playground.

Swift is impressive as it fixes a number of issues with Objective-C by replacing it wholesale with a modern language that feels like a well thought-out JavaScript. I love the idea of optional values, implicit types, simple generics, and how let and var are used to create immutable and mutable objects. I’m still digesting the class vs struct vs enum idea. I’m not sure why they are just not all classes but give me time, I’m sure I’ll see the light.

The Snakes and Ladders example is a fine bit of sample code and shows off Swift’s power as a teaching language. But I had a couple of issues with it and tweaked it. See below and check out the comments for why I did what I did. You should be able to copy and paste this code into a playground and watch it run!

// Playground - noun: a place where people can play

import Cocoa

// Snakes and Ladders Game

let finalSquare = 25 // 25 squares in the game (0 to 24)

// init all squares to 0 and included a 26th square (to simplify the code)
// Note: you can use var or let to define the array board (Apple uses var, I'm using let)
// - let makes board a constant but values of a const array are still mutable
// - its the number of elements in the array that are immutable
// - using let allows the compiler to optimize looking up values in the array
let board = Int[] (count: finalSquare + 1, repeatedValue: 0)

// in 4 squares as ladders (+ values) and 4 squares as snakes (- values)
// ladders move the player forward by value in square
// snakes move the player backward by value in square
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

var square = 0   // player position
var diceRoll = 0 // moves are determined by an 8-sided die

println("Game Starting at square \(square)")
// start the game
while square 

Update!

I could not get the sweet while loop with the label "gameLoop" to work in the playground. It's no surprise that Swift has a couple of bugs. So with a bit of fiddling I got it to work with a gameOver flag. And while I was at it I created a swifty function to hide the ugly arc4Rand_uniform() call and refactored the print statements so you can follow along with the game in the console.

func randNum(limit: Int) -> Int {
  return Int(arc4random_uniform(UInt32(limit)))
}

// start the game
println("Game Starting at square \(square)")

var gameOver = false

gameLoop: while square != finalSquare {
  diceRoll = randNum(7)
  switch square + diceRoll {
  case finalSquare:
    // diceRolled moved player to finalSquare
    // game over
    print("starting \(square), ")
    print("diceRoll \(diceRoll), ")
    print("destination \(square + diceRoll), Game Over")
    println()
    
    // Swift didn't recognise the loop's label so I commented it out
    // This teams the break only breaks out of the switch
    // The gameOver flag is used to break out of the while loop
    gameOver = true
    break //gameloop
  case let newSquare where newSquare > finalSquare:
    // diceRoll moved player beyound finalSquare
    // roll again
    print("starting \(square), ")
    print("diceRoll \(diceRoll), ")
    print("destination \(square + diceRoll), Re-roll")
    println()
    
    // Swift didn't recognise the loop's label but in this case
    // it doesn't matter...
    continue //gameloop
  default:
    // valid move
    // update board
    print("starting \(square), ")
    print("diceRoll \(diceRoll), ")
    square += diceRoll
    print("destination \(square), ")
    print("instruction \(board[square]), ")
    square += board[square]
    print("result \(square)")
    println()
  }
  // if flag is set break out of the while loop
  if gameOver {
    break
  }
}
Categories
Programming Sprite Kit

Sprite Kit, Retina, iOS7 and Getting It Right

I spent more time that I care to admit figuring out how to reconcile my old Dungeonators code with Apple’s Sprite Kit, Retina displays, and iOS 7. Along the way I searched the web for help and ran into tons of tutorials and advice for indy game developers (I highly recommend  www.raywenderlich.com for a great set of systematic and well written tutorials). But I learned to be wary of advice on some sites that is confused or actually wrong. For example I came across this macro in one of Ray’s tutorials that originated from a Stack Overflow conversation:

#define IS_WIDESCREEN ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )

This macro was written by a floating point math C macro professional. It’s used to determine if the resolution of an iPhone screen is 4 inches. iOS 7 only runs on iPhone’s with Retina displays but there are still  millions of iPhone 4 and 4s models in use with 3.5 inch screens. The iPhone 5, 5c, and 5s has a widescreen of 4 inches.

But why do I need a fancy macro for that? It’s code that I’m only going to run once. And it’s not a particularly CPU intensive calculation. And really, I want to understand what every line of code in my game does. Copy and paste code is always a bad idea.

It’s not hard to figure out what this macro does: It gets the height of the screen, subtracts the resolution of the 4 inch iPhone from it and makes sure the result is less than a number that is as close to zero as the phone’s floating point system can represent. However, there is no reason that we have to go to this level of complexity!

My feeling about macros is that you almost never need to use them. And if you do, call in a pro C programmer. Macros can optimize your code but if you don’t use the right number of parens you could end up with subtle side effects and bugs that are hard to track down.

As an added bonus this macro is only useful if you have determined that the iOS device your game is running on is an iPhone. Taken out of context this speedy line of code could give your game the wrong idea.

To do it right, as in doing it with code that is safe, maintainable, and as efficient as it needs to be, you have to understand how Apple internally represents the resolution of an iOS device. (I’m assuming you want your game to be universal across the Apple universe.)

So let’s say you want to write a universal iOS game in Objective-C using Sprite Kit. Here are the consequences of your decision:

  • Your game can only run on iOS devices running iOS7. This fact eliminates the original non-Retina iPhones but not non-Retina iPads
  • Your game must support both 3.5 and 4 inch iPhones.
  • Your game must support iPad and iPad Retina devices.
  • Don’t worry about model names! The name of the device (iPad, iPad Air, iPhone 5, iPhone 4s, iPadophone 7xyz) doesn’t tell you anything reliable about the number of pixels you have to play with.

Here’s the non-obvious part: You’re going to create your game with pixels in mind but Apple is going to give coordinates in points. I wish Apple had not done it this way. I guess they were trying to be helpful. At lest let developers turn of the points paradigm if like me, they find it unhelpful. But we can’t turn it off so we have to live with it!

Apple says there are 2 pixels in a point. This table should help:

Device Type    Pixels        Points      Models
iPhone 3.5"    960 x 640     480 x 320   iPhone 4, 4s
iPhone 4"      1136 x 640    568 x 320   iPhone 5, 5c, 5s
iPad           1024 x 768    1024 x 768  iPad, iPad Mini
iPad Retina    2048 x 1536   1024 x 768  iPad with Retina

Apple gives us a macro of it’s own to figure out if we’re running on an iPhone or iPad, UI_USER_INTERFACE_IDIOM() and a way to discover the screen’s dimensions in points,
[UIScreen mainScreen].bounds.size.height

Thus, here is a simple method that you can add to your game projects to figure out what sort of iOS device you’re running on based on it’s screen real estate.

- (NSString *)figureOutScreen {
    // Call once during initialization!
    NSString *result;
    
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        
        // iPhone
        
        if ([UIScreen mainScreen].bounds.size.height==568.0f) {
            
            // iPhone Retina 4-inch
            result = @"iPhone Retina 4-inch";
        } else {
            
            // iPhone filename 3.5-inch            
            result = @"iPhone Retina 3.5-inch";
        }
    } else {
        
        // iPad (Can't tell if it is mini, standard, or Retina 'cause all the dimensions in points are the same
        result = @"iPad";
    }
    return result;
}
Categories
Cocos2d-iPhone Programming Sprite Kit

Re-doing Dungeonators

One thing I think all software developers love to do is to make software for “the heck of it.” Hacking something together without having to worry about users, open source community members, and code reviews, is something devs don’t get to do often in these days of test-driven, agile, minimum viable products. Back in 2011 I created an iPhone game just for the heck of it. I wanted to learn about the indy game development process and there isn’t a better way of learning than by doing. I accomplished my mission and uploaded the first version of Dungeonators to Apple’s iTunes App Store in October of 2011. While the game is not very playable, I learned a lot:

  • How to design and implement a realtime game with Objective-C and the Cocos2d-iPhone framework
  • How to create all the art, fonts, sprites, and a spite atlas
  • How to record (or buy) all the sounds and music and use open source tools to transcode audio for a game
  • How to create the mechanics of a game with objects and callbacks while ref counting to manage memory
  • How to test and deploy an iOS game and get something approved in the App Store

Now it’s 2014 and much has changed in the world of indy game development for iOS. Apple’s iOS 7 introduced Sprite Kit which reduces the need for a framework like Cocos2d. Apple also introduced the iPhone 5 and the iPad with Retina, which mean there are four iOS screen resolutions/aspect ratios in common use. Oh, and let’s not forget Flappy Bird and 2048. While writing a hit indy game is still harder than winning the lottery it looks like a couple of people have done it!

Since I’m taking a break between adventures, I thought it would be fun to update Dungeonators for iOS 7 and Sprite Kit. Plus it would be good to make the game universal (available on all iOS devices) and global (available in more than just the United States edition of the App Store). And I should probably make it playable!

The technical part of this plan is relatively easy. iOS 7 is easier to develop with than previous versions of iOS. I can use ARC (automatic reference counting)in addition to manual ref counting. All the music and sound effects should just work fine. My custom Objective-C classes just need some simplifying (I wrote too much code–that’s my bad coding habit). The original Dungeonator’s artwork is all vector-based so rendering it in different resolutions for each iOS device shouldn’t be too hard. Finally, Sprite Kit is remarkably similar to Cocos2d. Porting the scenes, sprites, transitions, and actions will also be easy.

High resolution Dungeonators under development (1136 x 640 pixel rendering)

The hard part is creating a playable game. If you download original version of Dungeonators from the App Store you’ll find it really confusing. How do you play? Where to you touch? What is the goal? I don’t know what I was thinking!

Actually I do know what I was thinking. I was thinking about the inside not the outside. Inside there are classes for characters, strategies, spells, payloads, and matches. Way too much to fit in a casual game. So while I’m porting everything over I’m going to tear out hundreds of lines of code. I’m going to simplify Dungeonators to the point where it doesn’t require a personal demo from me to learn how to play!

I still might not have a playable game but at least it won’t crash anymore!

Categories
Nerd Fun Programming

Getting Xcode and GitHub to work together like besties

Updated for Xcode Version 7.3.1 

Thanks to Jake for pointing out that his blog post needed a little freshening up!

After watching the WWCD keynote I wanted to fool around in Cocoa and Objective-C again. It’s been a while and my Xcode skills were rusty. One task that always seems tricky is getting Xcode’s local git repository to work well with GitHub’s remote repositories. I clicked around, read some docs, did a little Googling, and experimented until I got it to work and in way that works every time.

There are many other blog and stackoverflow posts on this subject but most are either missing a critical step or making it out to be way more complicated than it really is. That’s the problem with Apple magic: Sometime’s they design away the details to a point that you really don’t know what is going on.

The best tutorial on Git, Xcode, and GitHub for the white belt hacker is here: http://www.raywenderlich.com/13771/how-to-use-git-source-control-with-xcode-in-ios-6

But the part on connecting your local Git repository created and managed by Xcode to your remote GitHub repository is a little brief and lacks detail that might trip you up. Here is all you need to do to get Xcode and GitHub to play nice…

  • Create your Xcode project with a local Git repository and write some code
  • Commit your changes locally (File > Source Control > Commit…)
  • Create your GitHub remote repository on GitHub but do not initialize it and do not add a readme.md file!
    • Note: You can give that remote repository any name you want, I gave it the same name as my local git repository because I’m lazy
  • Go to the Source Control menu and and choose the second item with the name of your repo. This item leads to a popup menu. Choose the last item of that popup menu which has the name of your repo and word “configure”
  • In the configure dialog choose the Remotes item
  • Click the + button and choose “Add Remote” to associate a remote repo
  • In the Add a Remote dialog box you have two fields to fill out
    • In the name field enter the name of the GitHub repository you just created
    • In the location filed paste the “HTTPS” URI from GitHub for your repo
    • It will look like this: https://github.com/you-account-name/your-repo-name.git
  • Now you have a connected local and remote repository 🙂
  • Go back to the Source Code menu and choose Push…
    • A dialog box pops up with a drop down menu
    • Choose the only option you can which will be something like your-repo-name/create
    • Click the push button
    • Enter your user name and password in the dialog box that appears

You Win!!

Remember to check “Push to remote” in the Commit dialog box so you check in your code both locally and remotely.

Extra Credit:

I use Two Factor Authentication and I had to turn it off to test this. That made me a little nervous and I immediately turned Two Factor back on.

However you can generate a Personal Access Token so that that you can push to GitHub from Xcode and keep Two Factor on!

  • Log into you GitHub Account and go to Settings > Personal Access Tokens
  • Click the Generate New Token button
  • Give the button a description such as “Xcode Repo-Name”
  • Check the least amount of scope needed to commit code remotely: “Repo”
  • Click the Generate Token button at the bottom.
  • Copy the token (which is a long string of numbers)
  • Back in Xcode go to Preferences > Accounts and choose your repo under the list of repositories.
    • The Address field will contain the HTTS URI you entered earlier
    • The Description is the name of your repo
    • Authentication should be set to User Name and Password
    • User Name should be your GitHub user name
    • In the Password field paste token

Boom! Now you can commit remotely with Two Factor Auth in full effect!

 

Categories
Cocos2d-iPhone Programming

ARC Memory Management in iOS 5 and Cocos2d-iPhone 2.0

My rewrite of Dungeonators is crawling along at a glacial pace. Mostly because I can only work a couple of hours a week on the project. But the iOS 5 way to manage memory with ARC (automated reference counting) is making the process far less painful than it was the first time I wrote my game. ARC uses annotations and some new key words to manage memory on your behalf. It works very well if you’re doing simple client-side game development and don’t have to worry about backward compatibility with iOS 4.

Here is an example of how ARC changes your life for the better…

In the old days you would declare out your ivars, declare your properties, then synthesize and map them together, and release them in a dealloc method. This little design pattern required 4 repetitive lines of code for every ivar (instance variable) and engendered flamewars about  coding style and where underscore should go (if you thought it was necessary to distinguish an ivar name from a property name).

Pre-ARC Entity.h

#import "cocos2d.h"
@interface Entity : CCSprite {
BOOL isActive_;
NSString * name_;
}
@property (nonatomic) BOOL isActive;
@property (nonatomic, copy) NSString * name;
@end

Pre-ARC Entity.m

#import "Entity.h"
@implementation Entity
@synthesize isActive = isActive_;
@synthesize name = name_;
- (id) init {
  self = [super init];
  if (self) {
    isActive_ = YES;
    name_ = @"Fang";
  }
  return self;
}
- (void) dealloc {
  [name_ release];
  name_ = nil;
  [super dealloc];
}

In this new modern era of automated reference counting I just declare properties, I don’t worry about accessing the ivars directly, and I don’t write my own dealloc. It’s a lot less code in a complex game where characters and objects have dozens of properties.

Post-ARC Entity.h

#import "cocos2d.h"
@interface Entity : CCSprite
@property (nonatomic) BOOL isActive;
@property (nonatomic, strong) NSString * name;
@end

Post-ARC Entity.m

#import "Entity.h"
@implementation Entity
@synthesize isActive;
@synthesize name;
- (id) init {
  self = [super init];
  if (self) {
    isActive = YES;
    name = @"Fang";
  }
  return self;
}

To get Cocos2d-iPhone to work with ARC was really easy: I just replace the NSAutoreleasePool object in main.m with the new ARC friendly @autoreleasepool annotation. (I might not have had to do this but it’s supposedly more efficient.)

int main(int argc, char *argv[]) {
//    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//    int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate");
//    [pool release];
//    return retVal;
  int retVal;
  @autoreleasepool {
    retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate");
  }
  return retVal;
}

The downside, as I stated above, is that your app will only run on iPhones, iPads, and iPods running iOS 5 and you need to use Cocos2d-iPhone 2.0. But I think that’s a fair tradeoff. Backward compatibility is a killer of the artistic soul and should be avoided where possible. To Err is human, but to upgrade is divine 🙂

Check out Ray Wenderlich’s excellent ARC tutorial for all the gritty details!

Categories
Cocos2d-iPhone Programming

Sprite Playground

I wrote a little Cocos2d-iPhone test app and committed the project to GitHub. (Like every dutiful hacker should.) You’re welcome to download the project and fool around with the code.

My goal was to figure out how to create a composite sprite, make sure it could respond to touches, and rotate and move it round the screen. With a little trial and error I got it working. Here’s how I did the hard parts:

I created a sprite from an PNG file and got it’s dimensions:

CCSprite * cocos2dGuy = [CCSprite spriteWithFile:@"avatar_n1_tang.png"];
CGSize spriteSize = cocos2dGuy.textureRect.size;

Then I created a CCNode to be the parent of my sprite sandwich. (I originally tried to use my cocos2dGuy CCSprite as the parent but Cocos2d stacks added on top of partent nodes and I wanted the particle effect under the cocos2dGuy not on top of him.) I set the size of the CCNode to the dimensions of the cocos2dGuy so that when you touch the CCNode it responds as if you’re touching the image. Then I add the CCNode to the layer. It’s also important to set the CCNode’s anchorPoint to the center (0.5, 0.5) as it’s default is bottom-left. CCSprite default to center anchorPoints and I want my CCNode to act like a proper sprite.

CCNode * composite = [CCNode node];
composite.anchorPoint = ccp(0.5,0.5);
composite.contentSize = spriteSize;
composite.position = centerPt;
[self addChild:composite z:0 tag:kCompositeTag];

Next I added the cocos2dGuy sprite to the center of the composite CCNode. These two entities are now both the same size and stacked on top of each other. (It’s probably not necessary for me to set the cocos2dGuy’s anchorPoint and so I’ll remove that line of code down the road.)

cocos2dGuy.anchorPoint = ccp(0.5, 0.5);
CGSize compositeSize = composite.contentSize;
CGPoint compositeCenterPt = ccp(compositeSize.width/2,compositeSize.height/2);
cocos2dGuy.position = compositeCenterPt;
[composite addChild:cocos2dGuy z:1 tag:1];

Finally I created the custom CCParticleSystem from a Particle Designer file and added it to the composite CCNode but at a lower Z value show it shows up under the cocos2dGuy sprite. See how I had to set the position and the anchorPoint to ensure everything lined up.

CCParticleSystem * fx = [ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"fireball_small_fast_green.plist"];
fx.anchorPoint = ccp(0.5, 0.5);
fx.position = compositeCenterPt;
fx.duration = kCCParticleDurationInfinity;
fx.scale = 2.0f;
fx.autoRemoveOnFinish = YES;
[composite addChild:fx z:0 tag:1];

Movement and animation are easy in the world of Cocos2d. I really like the visual effect of CCEaseExponentialInOut. It has a nice punch.

Handling touches are bit harder. I had to remember to set self.isTouchEnabled = YES in my init method and override both registerWithTouchDispatcher and ccTouchBegan. The key bit of code for responding to a touch is isTouchForMe:

bool hit = false;
for(CCNode * node in [self children]) {
    if(node.tag == kCompositeTag) {
         hit = CGRectContainsPoint([node boundingBox], touchLocation);
         if (hit) {
             [self hiliteSprite:(CCSprite *)node];
         } else {
             [self turnSprite:(CCSprite *)node andAttackPoint:touchLocation];
         }
    }
}

The main thing here is to give each touchable node a unique tag and test each node in the layer for that tag. If you touch the sprite he inflates for a moment. If you touch an empty part of the screen the sprite turns and launches himself at your finger. Ouch!

Categories
Cocos2d-iPhone Programming

Cocos2d-iPhone Sprite Rotation to an Arbitrary Point

I had some time during the Thanksgiving weekend to work on Dungeonators. I’m hoping to get an upgrade out to the App Store soon. One thing I needed //TODO: is refactor my rather poor implementation of rotating a sprite to face another sprite. My original code worked ok, in a roundabout way, but was ugly and mysterious.

(My iPhone game is full of ugly mysterious code: As I implement my ideas in code I first focus on getting it to work. Then, if I have time I go back and fix it to work cleanly. Generally cleanly means boiling the code down to the fewest number of lines possible, making it as functional as possible, and using functions from the operating system or open source code libraries as much as possible.)

I won’t show you my original sprite rotation implementation. Some things are too gross even for the Internet. Let’s just say it was written as if I was trying to remember high school trigonometry by trial and error.

Instead below is the final implementation, useful to any Cocos2d-iPhone programmer who wants a sprite to rotate to face an opponent at any point on the screen:

- (void) rotateSourceSprite:(CCSprite *)sourceSprite toFaceTargetSprite:(CCSprite *)targetSprite {

    // Calculation
    CGPoint difference = ccpSub(sourceSprite.position, targetSprite.position);
    CGFloat rotationRadians = ccpToAngle(difference);
    CGFloat rotationDegrees = -CC_RADIANS_TO_DEGREES(rotationRadians);
    rotationDegrees += 90.0f;
    CGFloat rotateByDegrees = rotationDegrees - sourceSprite.rotation;

    // animation
    CCRotateBy * turnBy = [CCRotateBy actionWithDuration:0.5f angle:rotateByDegrees];
    CCEaseIn * ease = [CCEaseIn actionWithAction:turnBy rate:4];
    [sourceSprite runAction:ease];
}

And here’s how it works, just incase you’re curious…

  • The method takes a source sprite and as target sprite as parameters. After it runs it will rotate the source to face the target.
  • The calculation part uses Cocos2d helper functions and macros (ccpSub, ccpToAngle, and CC_RADIANS_DEGREES) to figure the maths. In my original implementation I had written my own versions. Using the Cocos2d API makes my code easier to read and perhaps faster.
  • The maths work like this: Create a vector out of the distance between to points. Convert the vector to an angle. Convert the angle from radians to degrees and negate it*. Add 90 degrees to the angle to get the proper orientation. (The +=90.0f value is game specific: My sprites are rotated 90 degrees to start with.) Subtract the penultimate result of the calculation from the current rotation property (angle) of the source sprite to get the final result (the amount to rotate by).
  • The animation, which could easily be factored out into a method of its own, uses Cocos2d’s CCRotateBy and CCEaseIn to turn the source sprite to face the target in half a second. This part didn’t change from the original code. I didn’t factor it out because in my game I always use these two pieces together. I don’t like to do too much work in the abstract because I usually end up making my code too complex.

* The need to negate the value produced by CC_RADIANS_DEGREES is a bit of a mystery to me. If I don’t do it my sprite ends up facing in the opposite direction.