Things learned from Objective-C – Cocos2D porting and testing in Swift – Part 2

More things learned from playing around with Swift, Objective-C and Cocos2D

Interoperability between Objective-C and Swift – Initialize patterns

If you have in you Objective-C code an Initialize Pattern to access it in Swift you just use the class name and access the initialize pattern as you would access a static function. Just remember to downcast with the ‘as’ operator returning the class from which you are using the initialization pattern.  The green code below:

var moveToLeftEdge : CCActionMoveTo = CCActionMoveTo(duration: CCTime(birdTime), position: screenLeft);
var turnaround : CCActionFlipX = CCActionFlipX(flipX: true);
var moveBackOffScreen : CCActionMoveTo = CCActionMoveTo(duration: CCTime(birdTime), position: birdStart);
var birdActions = Array<CCAction>();
birdActions.append(moveToLeftEdge);
birdActions.append(turnaround);
birdActions.append(moveBackOffScreen);
birdActions.append(turnaround);

var moveLeftThenBack : CCActionSequence = CCActionSequence.actionWithArray(birdActions) as CCActionSequence;

 

Interoperability between Objective-C and Swift – Objective-C Selectors

To access Objective-C selectors in Swift you define a constant value with the value being the function name to call within your class. See green code below:

https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_38

let mySelectorTurnAround: Selector = “turnAround”;
var turnArround : CCActionCallFunc = CCActionCallFunc.actionWithTarget(bird, selector: mySelectorTurnAround) as CCActionCallFunc;

 

Objective-C Struct definition usage and initialization

If you are get the following error message when using struct definitions in Objective-C then the solution is to initialize the struct before usage. Below is an example of than in the green color.

Error Message: Struct ‘struct name’ must be completely initialized before a member is stored to

Original Objective-C Struct definition:

// Bezier configuration structure.
typedef struct _ccBezierConfig {
// End position of the bezier.
CGPoint endPosition;
// Bezier control point 1.
CGPoint controlPoint_1;
// Bezier control point 2.
CGPoint controlPoint_2;
} ccBezierConfig;

In Swift Code Usage

var curve : ccBezierConfig = ccBezierConfig(endPosition: CGPointMake(0, 0), controlPoint_1: CGPointMake(0, 0), controlPoint_2: CGPointMake(0, 0));

Things learned from Objective-C – Cocos2D porting and testing in Swift – Part 1

Subclass & Initializers

This is a Swift language ralated “issue”, well it is a language feature. Personally I find it annoying. If you find yourself with the following error:

fatal error: use of unimplemented initializer ‘some initializer name of your class in which you operate in’ for class ‘your class name

Then you probably are dealing with an initialize problem. This is by default a Swift language feature. In the following link this is explained very well: http://www.codeproject.com/Articles/783584/Subclassing-Objective-C-classes-in-Swift-and-the-p

Quoted from the above link: “Subclassing Objective-C classes in Swift and the perils of Initializers – CodeProject// //

Unlike subclasses in Objective-C, Swift subclasses do not not inherit their superclass initializers by default. Swift’s approach prevents a situation in which a simple initializer from a superclass is automatically inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.

If you want your custom subclass to present one or more of the same initializers as its superclass—perhaps to perform some customization during initialization—you can provide an overriding implementation of the same initializer within your custom subclass.

This explains it. Basically, in Swift, initialization methods work like virtual functions in fact super virtual functions. If a super class initializer is invoked and that in turns calls another initializer (designated or not) then it forwards the call to the derived class. The upshot of this seems to be that for any derived class in Swift it would need to re-implement all of the super classes initializers or at least any which may be invoked from the the other initializers.

The possible solution are that you call each initializer from the super class or use directly the initializer that you definitely are going to use.

To create Formated strings you can use the following piece of code(The green bold text):

var animFrameNameFormat : String;

switch self.birdType
{
case .BirdTypeBig:
animFrameNameFormat = “bird_big_%d.png”;

case .BirdTypeMedium:
animFrameNameFormat = “bird_middle_%d.png”;

case .BirdTypeSmall:
animFrameNameFormat = “bird_small_%d.png”;

default:
animFrameNameFormat = “bird_small_%d.png”;

}

var animFrames = [CCSpriteFrame]();

for (var i = 0; i < 7 ; i++)
{
var currentFrameName = String(format: animFrameNameFormat, i);

var animationFrame = CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName(currentFrameName);
animFrames.append(animationFrame);
}

In this example basically you call the String class to create a string formatted with an integer number that indicates which image to load. Pay also attention to the text notations where the integer is inserted at( the red bold text). You can use Objective-C formaters in swift: https://developer.apple.com/library/ios/documentation/cocoa/conceptual/Strings/Articles/formatSpecifiers.html

Cocos2D Macros as Extension in Swift:

For some reason(I am not fluent in Objective-C, Swift or XCode) in swift the macros for Cocos2D game framework do not work, even when adding the Objective-C macros and extension header file. I decided to take another approach to tie struct, class and type operations as extension that can be performed on a specific chosen element. In the code below I added to the CGPoint the Cocos2d equivalent ccpSub, ccpMult, ccpDot etc operations. Notice that in order for these function to be available through the CGPoint struct the function must be set to static.

extension CGPoint
{
static func Sub(v1 : CGPoint, v2 : CGPoint) -> CGPoint
{
return(CGPointMake(v1.x – v2.x, v1.y – v2.y));
}

static func Dot(let v1 : CGPoint, let v2 : CGPoint) -> CGFloat
{
return v1.x * v2.x + v1.y * v2.y;
}

static func LengthSQ(let v: CGPoint) -> CGFloat
{
return CGPoint.Dot(v, v2: v);
}

static func Normalize(let v: CGPoint) -> CGPoint
{
return CGPoint.Mult(v, s: 1.0 / CGPoint.Length(v));
}

static func Mult(let v: CGPoint , let s: CGFloat) -> CGPoint
{
return CGPointMake(v.x * s, v.y * s);
}

static func Length(let v: CGPoint) -> CGFloat
{
return CGFloat(sqrtf(Float(CGPoint.LengthSQ(v))));
}

static func AngleSigned(a: CGPoint, b: CGPoint) -> Float
{
var a2 : CGPoint = CGPoint.Normalize(a);
var b2 : CGPoint = CGPoint.Normalize(b);
var angle : Float = atan2f(Float(a2.x * b2.y – a2.y * b2.x), Float(CGPoint.Dot(a2, v2: b2)));
if( fabs(angle) < FLT_EPSILON ) { return 0; }
return angle;
}

static func RotateByAngle(v : CGPoint, pivot: CGPoint, angle : Float) -> CGPoint
{
var r : CGPoint = CGPoint.Sub(v, v2: pivot);
var cosa : Float = cosf(angle), sina = sinf(angle);
var t : Float = Float(r.x);
r.x = CGFloat(t*cosa – Float(r.y)*sina + Float(pivot.x));
r.y = CGFloat(t*sina + Float(r.y)*cosa + Float(pivot.y));
return r;
}
}

Properties not initialized before super class initialization call

The problem is simple in swift basically you have to initialize properties before the call of super initialization.

If you do not you will get an error message similar to what you find below:

Property ‘your property name‘ not initialized at super.init call

Apple describes the situation as follows:

“Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error:”

Safety check 1 “A designated initializer must ensure that all of the “properties introduced by its class are initialized before it delegates up to a superclass initializer.”

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/

 Solution: Use Swift functionality related to optionals to get around the problem, that is: the exclamation mark ! and the question mark ?:

https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html

 

Some sample code below, the solution is marked with the green color:

class GameScene : CCScene

{
var hunter : Hunter!;
var bird : Bird!;
var birdsCount = 0;
var batchNode : CCSpriteBatchNode!;

override init()
{

super.init()
userInteractionEnabled = true
self.createBatchNode();

self.addHunter();

}
func addHunter()
{
self.hunter = Hunter();
var viewSize = CCDirector.sharedDirector().viewSize();
var hunterPositionX = viewSize.width * 0.5 – 250;
var hunterPositionY = viewSize.height * 0.3;
self.hunter.position = CGPointMake(hunterPositionX, hunterPositionY);
self.batchNode.addChild(self.hunter);
}

}

 

public class Hunter : CCSprite
{
var torso : CCSprite?;

override init() {

super.init(imageNamed: “hunter_bottom.png”);
self.torso = CCSprite(imageNamed: “hunter_top_0.png”);
self.torso!.anchorPoint = CGPointMake( 0.5,10/44);
self.torso!.position = CGPointMake(self.boundingBox().size.width/2, self.boundingBox().size.height);
self.addChild(torso, z: -1);
}

}

Swift Objective-C and Cocos2d

I got interested in making something for my iPad and play around with Swift. I decided to use the Cocos2d Open Source Framework but because it is written in Objective-C I was puzzled how to use it properly in Swift code. Thanks to Apple documentation there is a solution.

To use Cocos2d code within a Swift project follow these instructions in the Apple documentation.

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html