يعد Sprite Kit واحدا من اكثر التقنيات تشويقا فهي متاحة مع iOS 7 SDK و Xcode 5, و لكن كيف يمكن مقارنته مع محرك العاب اساسي مثل Cocos2D? سيزودك هذا الدرس بمقدمة مختصرة عن Sprite Kit قبل اخذ نظرة شاملة على كيفية مقارنته مع Cocos2D.
مقدمة عن Sprite Kit
من بين كل الالعاب المتوفرة في متجر App, فان العاب 2D تعد من اكثر الالعاب تحميلا و الاكثر ربحا. تضم بعض العنوين الايقونية في هذه المجموعة Angry Birds, و Tiny Wings, و Cut the Rope. هنالك العيدي من الخصائص التي تقود الى نجاح هذه الالعاب: الصور الجميلة, تأثيرات الجزيئات, محرك الفيزياء, الاحياء المحبوك و تأثيرات الصوت.
يمكن اختصار Sprite Kit منطقيا الى ثلاثة اجزاء:
- مشاهد- كما هو الحال في Cocos2D, فان المشاهد هي الطبقة المرئية للعبة. هي حيث تتحكم بالخلفية, الكائنات ) مثل الاشجار, السيارات, الطائرات...الخ).
- النشاطات-يعد الاحياء اللطيف جزاء حيويا لأي لعبة. قامت Apple بتصميم نظام النشاط في اسلوب بديهي, و يسمح لك تقريبا بعفل كل شيء. بعض اهم النشاطات الشائعة هي: الحركة, المقياس, الحجم, التدوير, احياء النسج, و وضع النشاطات في مجموعات. بالاضافة الى ذلك, اذا لم يعرف نشاط معين, يمكنك دائما انشاء كتلة تقليدية من الكود لتشكيل نشاطك و التلاعب بالكائن.
- الفيزياء-اذا اردت لعبة تتصرف بشكل واقعي, ستحتاج الى اضافة محرك فيزياء. فأنت لا ترغب برصاصة لا تتبع الى طريق محدد, او بكرة لا تقفز عندما تصدم بالارض, و تأثيرات اخرى. لحسن الحظ, تأتي Sprite Kit مع محرك فيزياء مجتمعة.
تطوير اثبات اضافي
يعد كتابة لعبة باستخدام اطار عمل من طرف ثالث او محرك لعبة سيف ذو حدين. فلا نعلم فيما اذا الادوات ستكون متوافقة مع تحديثات منصات المستقبل, او حتى اذا ستعمل اللعبة جيدا بعد التحديث. عندما تتعطل الاشياء, انه غير مؤكد كم ستطول مدة تصليح هذه الاخطاء.
تعد Cocos2D مثالا عن مشروع مفتوح المصدر يجب ان يتعامل مع هذه المشكلة. تتطور الكود باستمرار, و يجب عند كل اطلاق جديد اتباع عدة خطوات امان من اجل ضمان بناء التطبيقات مع Cocos2D و التي ستعمل على احدث نسخة من iOS و احدث هاردوير.
مع Sprite Kit, زودت Apple مجموعة من الادوات لضمان ان كود اللعبة ستعمل على كل اداة متوافقة بدون اية مشاكل. لاحظ ان Sprite Kit هي ليست فقط اطار عمل iOS. يستطيع المطورون البدء ببناء العاب Sprite Kit لـ OS X ايضا, من البديهي ان العاب Sprite Kit ستعمل على اية اداة من iOS ايضا.
المطور الودود
كانت الطمأنينة بالاستخدام عامل رئيسي وراء نجاح محركات الالعاب مثل Cocos2D.. بشكل عام, وجد المطورون ان Cocos2D اسهل بكثير من انشاء بدائل اخرى OpenGL ES. مع Cocos2D, كل الاتصالات ذات المستوى المنخفض API نقلت الى الطرق البسيطة.
لاحظ ان, في بداية عرض Sprite Kit, Ricardo Quesada, غرد المطور الاساسي لـ Cocos2D على توتير قائلا:
يعد Sprite Kit جديا جدا. مع وجود ميزات اقل من Cocos2D, و لكن افضل. احب وحدة الفيزياء.
يعد ذلك مدحا مهما قادما كم اهم العقول وراء Cocos2D!
ميزات Sprite Kit & Cocos2D
مقارنة المشروع
لذا, كيف تبدو المشاريع مع كل محرك لعبة؟ للاجابة عن هذا السؤال, ادخل المؤلفون اكمال كود المصدر لكلا مشروعي Sprite Kit و Cocos2D. يمكنك استخدام هذه المشاريع كمقارنة عالية المستوى لكل محرك لعبة.
مقارنة كود المصدر
في هذا القسم سنحلل المهام و المفاهيم الشائعة, و سنري كيفية انشائهم باستخدام Cocos2D و Sprite Kit.
CClayer vs. SKScene
يتم التنقل بين المشاهد في Cocos2D باتباع الخطوات التالية:
GameScene* gameScene = [[GameScene alloc] init];
[[CCDirector sharedDirector] replaceScene:gameScene];
لاحظ ان ملف GameScene.h يجب ان يكون تابع لمجموعة CCLayer و لديه بادئ محدد متاح.
@interface GameScene : CCLayer {}
+(CCScene *) scene;
In GameScene.m, the initial implementation is:
+(CCScene *)scene
{
CCScene *scene = [CCScene node];
GameScene *layer = [GameScene node];
[scene addChild: layer];
return scene;
}
-(id) init{
if( (self=[super init] )) {
// Your code here
}
return self;
}
يعد النقل مشابها في Sprite Kit:
GameScene* gameScene = [[GameScene alloc] initWithSize:CGSizeMake(1024, 768)];
[self.scene.view presentScene:gameScene];
}
يجب ان يكون GameScene من مجموعة SKScene, و حجم -(id)initWithSize:(CGSize) هو بادئ تقليدي. مثال بسيط على ذلك هو:
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
// Your code
}
return self;
}
CCSprite vs. SKSpriteNode
بينما الانشاء في Sprite Kit هو:
SKSpriteNode* planeShadow = [SKSpriteNode spriteNodeWithImageNamed:@"player.png"];
planeShadow.scale = 0.5;
planeShadow.position = CGPointMake(CGRectGetMidX(self.frame)+100,CGRectGetMidY(self.frame)+200);
[self addChild:planeShadow];
CCLabelTTF vs. SKLabelNode
تستخدم الكائنات التصنيفية لعرض النص. يمكن ان يكون لديها خواص عديدة, بما في ذلك النص, حجم النص, لون النص, الموقع, و اخرى عديدة. يعد انشاء كلا Cocos2D و Sprite Kit متشابها. انشاء Cocos2D هو:
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];
// ask director for the window size
CGSize size = [[CCDirector sharedDirector] winSize];
label.position = ccp( size.width /2 , size.height/2 );
[self addChild: label];
انشاء Sprite Kit هو:
SKLabelNode* gameScene = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
[gameScene setText:@"New Game"];
[gameScene setFontSize:18];
gameScene setPosition:CGPointMake(CGRectGetMidX(self.frame)+5,CGRectGetMidY(self.frame)-40)];
[self addChild:gameScene];
CCMenu and CCMenuItem vs. Sprite Kit Menu
في Cocos2D, يتم انشاء القوائم باستخدام كائنين: CCMenu و CCMenuItem. يعرض المثال التالي قائمة مع خيارين في Cocos2D:
CGSize size = [[CCDirector sharedDirector] winSize];
[CCMenuItemFont setFontSize:28];
CCMenuItem *itemNewGame = [CCMenuItemFont itemWithString:@"New Game" block:^(id sender) {
// Your code
}];
CCMenuItem *itemOptions = [CCMenuItemFont itemWithString:@"Options" block:^(id sender) {
NSLog(@"Second item");
}];
CCMenu *menu = [CCMenu menuWithItems:itemNewGame, itemOptions, nil];
[menu alignItemsHorizontallyWithPadding:20];
[menu setPosition:ccp( size.width/2, size.height/2 - 50)];
[self addChild:menu];
يستخدم المثال التالي SKLabelNode كمادة قائمة. اولا, سنعرف SKLabelNode:
SKLabelNode* gameScene = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
[gameScene setText:@"New Game"];
[gameScene setFontSize:18];
[gameScene setPosition:CGPointMake(CGRectGetMidX(self.frame)+5,CGRectGetMidY(self.frame)-40)];
[self addChild:gameScene];
داخل طريقة -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event سننشئ ممسك الحدث الذي سيقاطع حدث اللمس:
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
if ([gameScene containsPoint:location]) {
// Scene Transition Animation
SKTransition* reveal = [SKTransition revealWithDirection:SKTransitionDirectionDown duration:1];
GameScene* gameScene = [[GameScene alloc] initWithSize:CGSizeMake(1024, 768)];
[self.scene.view presentScene:gameScene transition:reveal];
NSLog(@"Touched gameScene!!!!");
}
}
تعمل الكود الانفة الذكر عدة اشياء:
- تفعل احداث اللمس.
- تحول موقع النقر الى الموقع الداخلي.
- تفحص اذا كان موقع النقر داخل كائن gameScene SKLabelNode.
- تنشئ احياء النقل.
- تغير المشهد.
النشاط ضد SKAction
يكمن الاختلاف الرئيسي بين النشاط و SKAction هو ان SKAction كائن معقد ذو عدة خواص. النشاط في Cocos2D هو عبارة فقط عن نشاط يعرفه, و يستدعيه و يتعامل معه المبرمج فقط.
سنركز الان على نشاطات الحركة.
في Cocos2D نحتاج الى تعريف مجدول للبرنامج لاستدعاء طريقة تقليدية:
[self schedule:@selector(addSprite:) interval:1];
- (void) addSprite:(ccTime)dt
{
CCSprite* aMovableSprite = [CCSprite spriteWithFile:@"frankenstein.png"];
aMovableSprite.scale = .8;
[self addChild:aMovableSprite];
CGSize winSize = [CCDirector sharedDirector].winSize;
int minX = aMovableSprite.contentSize.width / 2;
int maxX = winSize.width - aMovableSprite.contentSize.width/2;
int rangeX = maxX - minX;
int actualY = (arc4random() % rangeX) + minX;
CCCallBlockN * actionMoveDone = [CCCallBlockN actionWithBlock:^(CCNode *node) {
NSLog(@"Sprite free!");
}];
NSMutableArray *arrayBezier = [[NSMutableArray alloc] init];
ccBezierConfig bezier;
id bezierAction1;
float splitDuration = 6 / 6.0;
for(int i = 0; i< 6; i++){
if(i % 2 == 0){
bezier.controlPoint_1 = ccp(actualY+100,winSize.height-(100+(i*200)));
bezier.controlPoint_2 = ccp(actualY+100,winSize.height-(100+(i*200)));
bezier.endPosition = ccp(actualY,winSize.height-(200+(i*200)));
bezierAction1 = [CCBezierTo actionWithDuration:splitDuration bezier:bezier];
}
else{
bezier.controlPoint_1 = ccp(actualY-100,winSize.height-(100+(i*200)));
bezier.controlPoint_2 = ccp(actualY-100,winSize.height-(100+(i*200)));
bezier.endPosition = ccp(actualY,winSize.height-(200+(i*200)));
bezierAction1 = [CCBezierTo actionWithDuration:splitDuration bezier:bezier];
}
[arrayBezier addObject:bezierAction1];
}
[arrayBezier addObject:actionMoveDone];
id seq = [CCSequence actionsWithArray:arrayBezier];
[aMovableSprite runAction:seq];
}
في Sprite Kit, يمكننا استخدام SKAction للتحكم بما سيحدث لكائن في بداية و نهاية الحركة. توضح الخطوط التالية كيفية تحريك أي كائن على خط مستقيم:
SKSpriteNode* playerSprite = [SKSpriteNode spriteNodeWithImageNamed:@"player.png"];
[playerSprite setScale:0.4];
SKAction *movement =[SKAction moveTo:CGPointMake(900, 500) duration:5];
SKAction *remove = [SKAction removeFromParent];
[playerSprite runAction:[SKAction sequence:@[movement,remove]]];
[self addChild:playerSprite];
و لكن, يمكننا تعريف نشاط تقليدي و استخدام SKAction لتتشيط ذلك النشاط. يوضح المثال حركة Bézier.
SKAction *wait = [SKAction waitForDuration:1];
SKAction *callEnemies = [SKAction runBlock:^{
[self sendNewSKSpriteNode];
}];
SKAction *updateSKSpriteNodeOnScreen = [SKAction sequence:@[wait,callEnemies]];
[self runAction:[SKAction repeatActionForever:updateSKSpriteNodeOnScreen]];
ستتولى طريقة sendNewSKSpriteNode حركة الكائن التقليدي.
-(void) sendNewSKSpriteNode{
CGRect screenRect = [[UIScreen mainScreen] bounds];
// Custom SKAction
SKSpriteNode* enemy = [SKSpriteNode spriteNodeWithImageNamed:@"frankenstein.png"];
enemy.scale = 0.6;
CGMutablePathRef cgpath = CGPathCreateMutable();
//random values
float xStart = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float xEnd = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
//ControlPoint1
float cp1X = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float cp1Y = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.height ];
//ControlPoint2
float cp2X = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float cp2Y = [self getRandomNumberBetween:0 to:cp1Y];
CGPoint s = CGPointMake(xStart, 1024.0);
CGPoint e = CGPointMake(xEnd, -100.0);
CGPoint cp1 = CGPointMake(cp1X, cp1Y);
CGPoint cp2 = CGPointMake(cp2X, cp2Y);
CGPathMoveToPoint(cgpath,NULL, s.x, s.y);
CGPathAddCurveToPoint(cgpath, NULL, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
SKAction *planeDestroy = [SKAction followPath:cgpath asOffset:NO orientToPath:YES duration:5];
[self addChild:enemy];
SKAction *remove2 = [SKAction removeFromParent];
[enemy runAction:[SKAction sequence:@[planeDestroy,remove2]]];
CGPathRelease(cgpath);
}
CCParticleExplosion vs. Emitter
ليس لدى Cocos2D أي نوع من محررات الجزيئات. يجب استخدام تطبيق خارجي لانشاء الجزيء و من ثم استخدام خواص CCParticleExplosion محددة لتغيير سلوكها.
CCParticleExplosion* _particleExplosion;
particleExplosion = [[CCParticleExplosion alloc] initWithTotalParticles:800];
particleExplosion.texture = [[CCTextureCache sharedTextureCache] addImage:@"texture.png"];
particleExplosion.life = 0.0f;
particleExplosion.lifeVar = 0.708f;
particleExplosion.startSize = 40;
particleExplosion.startSizeVar = 38;
particleExplosion.endSize = 14;
particleExplosion.endSizeVar = 0;
particleExplosion.angle = 360;
particleExplosion.angleVar = 360;
particleExplosion.speed = 243;
particleExplosion.speedVar = 1;
CGPoint g = CGPointMake(1.15, 1.58);
particleExplosion.gravity = g;
ccColor4F startC = {0.89f, 0.56f, 0.36f, 1.0f};
particleExplosion.startColor = startC;
ccColor4F endC = {1.0f,0.0f,0.0f,1.0f};
particleExplosion.endColor = endC;
[self addChild:_particleExplosion];
particleExplosion.position = ccp(_size.width/5, _size.height/5);
[particleExplosion resetSystem];
تستخدم المنبعثات داخل Sprite Kit لتوليد الجزيء. من اجل استخدامهم, تحتاج الى اضافة جزيء الى مشروعك. اذهب الى جديد -> ملف-> مورد-> كلف جزيء Sprite Kit. ثم يجب عليك تسميته و اختيار اية نوع جزيء (نار, سحر, دخان, اخرى). سترى الان انه سيظهر ملفين على مشروع Xcode. ستنشئ ذلك مع:
SKEmitterNode* smokeTrail;
NSString *smokePath = [[NSBundle mainBundle] pathForResource:@"MyParticle" ofType:@"sks"];
smokeTrail = [NSKeyedUnarchiver unarchiveObjectWithFile:smokePath];
smokeTrail.position = CGPointMake(CGRectGetMidX(self.frame)+40,CGRectGetMidY(self.frame)-100);
[self addChild:smokeTrail];
يعد الصوت جزا نشيطا من اية لعبة او تطبيقات multimedia. في Cocos2D, يمكننا تحقيق ذلك من خلال خطوتين. تتضمن الاولى ملف SimpleAudioEngine.
#import "SimpleAudioEngine.h"
ثم تقوم باستخدام الاسطر التالية لاستدعاء ملف الموسيقا داخل المشروع:
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"sound.caf" loop:YES];
[[SimpleAudioEngine sharedEngine] setEffectsVolume:0.4f];
مع Sprite Kit,يعد ادخال الاصوات سهلا جدا:
SKAction* soundAction = [SKAction playSoundFileNamed:@"preview.mp3" waitForCompletion:NO];
[self runAction:soundAction];
لاحظ ان انجاز ذلك مع Sprite Kit يستخدم مرة ثانية مع كائن SKAction.
الخاتمة
كما يمكنك ان ترى من التحليل في الاعلى, هنالك عدة اوجه شبه بين Cocos2D و Sprite Kit. تستخدم Cocos2D عدة طبقات لكل كائن, بينما تضم Sprite Kit اكثر و تستخدم صنف اعلى NSObject لتحقيق اه\اف معينة (مثل الازرار و القوائم).
من حيث ان المستخدم ودود, فان Sprite Kit يشع عندما ترغب باستخدام نظام الجزيئات او منجز النشاطات. و لكن, عند العمل مع كائنات عامة أكثر, فان كلا اطاري العمل يعدان تقريبا بنفس مستوى الصعوبة.
و على الرغم من ذلك, فان بناء اللعبة باستخدام Sprite Kit يجلب فوائد عديدة, بما في ذلك محرك بحث موحد, ادوات تدفق عمل فعالة في Xcode 5, التوافق مع كلا iOS و OS X, و صيانة رسمية من قبل Apple.
ليست هناك تعليقات: