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);
}

}

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.