مساحة اعلانية

آخر المواضيع

بناء لعبة اليرقات باستخدام :Cocos2D

الأشباح ولوحة اللعب

في هذه السلسلة التعليمية سنقوم بإعادة بناء لعبة الأتاري الشهيرة (الحريش أو أم أربعة وأربعين) باستخدام  Cocos2D محرك الألعاب لل IOS، هنا سوف تتعلم كيفية إعداد لوح اللعب وملئه بالعفاريت


اليوم سنتكلم بالتفصيل عن إعداد واجهة المستخدم للعبة ورسم لوحة اللعب.
الخطوة الأولى: حقل البرعم - كائن اللعبة
في الإصدار الأصلي للعبة الحريش، الفطر كان يستخدم لتعريف منطقة اللعب للحريش. سنستخدم نفس الفكرة و ننشئ البراعم.
قبل أن نبدأ بالحديث عن "in-game objects"، نحتاج لتعريف كلاس رئيسي يحتوي على بعض ال methods والخصائص والتي سنحتاجها لكل كائن. وهذا سيوفر كثير من أسطر الكود باستدعاء هذه ال methods بدلاً من إعادة كتبة الكود في كل مكان نحتاجه.

قم بإنشاء ملف جديد وأعطه اسم GameObject.m وفي ملف ال header اكتب الكود التالي:
#import "cocos2d.h"#import "GameLayer.h"
@interface GameObject : NSObject
@property(nonatomic, retain) CCSprite *sprite;@property (nonatomic, assign) GameLayer *gameLayer;@property(nonatomic) CGPoint position;
- (id)initWithGameLayer:(GameLayer *)layer;- (CGRect)getBounds;
@end
وفيما يلي وصف بسيط لكل من الخصائص المستخدمة هنا. وال methods سوف نقوم بشرحها تفصيلياً عند عرض ال implementation الخاص بها.
· الشبح: كل كائن في اللعبة يحتاج لشيء يعبر عنه. وهو هنا سيكون كائن صورة الشبح المستخدم لل draw _this_ object
· gameLayerببساطة مؤشر على الطبقة الرئيسية للعبة. وهذه ستكون مفيدة للرد على التصادمات كما ستستخدم للسماح للكائنات بإضافة وحوشهم إلى مساحة اللعب.
· Positionوهو يعبر عن موقع الكائن في اللعبة.
والآن لنرى بناء الكود المطلوب. افتح GameObject.m وقم بإضافة الكود التالي:
#import "GameObject.h"
@implementation GameObject@synthesize sprite = _sprite;@synthesize gameLayer = _gameLayer;@synthesize position = _position;- (void)dealloc { [_sprite release]; [super dealloc];}- (id)initWithGameLayer:(GameLayer *)layer { if(self = [super init]) { self.gameLayer = layer; } return self;}// 1- (void) setPosition:(CGPoint)position { _position = position; _sprite.position = position;}// 2- (CGRect)getBounds { CGSize size = [self.sprite contentSize]; return CGRectMake(self.position.x - size.width * self.sprite.anchorPoint.x, self.position.y - size.height * self.sprite.anchorPoint.y, size.width, size.height);}@end
بما أن موقع الكائن الخاص بنا يختلف عن موقع الوحش، سنحتاج لإعادة كتابة محدد خاصية الموقع للتأكد من أن الوحش  فعلياً تحرك على الشاشة.
هذه ال method ستستخدم فيما بعد عندما نقوم ببناء فحص التصادم. فهي تحدد محور الصندوق المحيط للكائن وتقوم بإرجاعه.
الخطوة الثانية: حقل البرعم- كائن البرعم

الآن لدينا GameObject،كائن البرعم سيصبح أبسط. الغرض الوحيد من البراعم هي أن يكون عائق للاعب وللحريش.
قم بإنشاء كلاس فرعي جديد من GameObject وأعطه اسم Sprout وهذا لأننا نريد استخدام بعض ال methods والخصائص الموجودة فيه، قم بفتح الملف وأضف الكود التالي:#import "GameObject.h"
@interface Sprout : GameObject
@property (nonatomic, assign) NSInteger lives;
@end
الإضافة الوحيدة التي يضيفها كائن ال sprout لل GameObject أنها تمتلك خاصية  lives . عندما يقوم اللاعب بالتصويب على البرعم نريده أن يُضرب عدد معين من المرات في هذه الحالة(ثلاث مرات) قبل أن يختفي.
بناء كائن ال Sprout أيضاً سهل، قم بإضافة الكود التالي إلى ملف Sprout.m.
#import "Sprout.h"#import "GameConfig.h"
@implementation Sprout
@synthesize lives = _lives;
- (id) initWithGameLayer:(GameLayer *)layer {    // 1    if(self = [super initWithGameLayer:layer]) {        // 2        self.sprite = [CCSprite spriteWithSpriteFrameName:@"sprout.png"];                 // 3        [self.gameLayer.spritesBatchNode addChild:self.sprite];                 // 4        self.lives = kSproutLives;    }    return self;}
// 5- (void)setLives:(NSInteger)lives {    _lives = lives;      self.sprite.opacity = (_lives / 3.0) * 255;}
@end
1-  بما أننا نقوم بعمل كتابة من جديد ل initWithGameLayer method ، سنحتاج إلى استدعاء ال superclass. وهذا سوف يؤكد أن كل شيء تم إعداده بالشكل المناسب.
2- هذا يعمل إعداد للبرعم الحالي ليأتي خارجاً CCSpriteBatchNode باسم sprout.png.
3- إضافة البرعم ليتم رسمه في طبقة  batch node.
4- تحديد عدد مرات الحياة للبراعم إلى إعداد kSproutLives  
5- نريد تزويد المستخدم ببعض التغذية الراجعة عندما يضرب برعم بحيث يتم توضيح كم الحياة المتبقي. وقد قمت بعمل هذا بإسقاط التعتيم للمرة الثالثة لضربه.
وكما ذكرت في النقطة 4 نحتاج لعمل بعض الإعدادات الأساسية للعبة. ولحسن الحظ Cocos2D قامت بتوفير ملف يُدعى GameConfig.h فقط لعمل هذا قم بفتحه وأضف هذا السطر عليه
#define kSproutLives 3
الآن قمنا بإنشاء كائن البرعم الأساسي. ويمكننا استخدامه لتوليد الحقل المطلوب للعبتنا.
الخطوة الثالثة: حقل البرعم- المواقع العشوائية
خريطة اللعبة سيتم توليدها عشوائياً بأقل كمية من البراعم في اللعبة في أي وقت. ولعمل هذا سنحتاج لحفظ المسارات التي قمنا بوضع براعم فيها مسبقاً. وكذلك عدد البراعم المضاف إلى الخريطة سيعتمد على المستوى الحالي، كلما كان عدد البراعم أكثر على الخريطة كلما كانت اللعبة أصعب على اللاعب. لذلك قم بفتح ملف GameLayer.h وقم بإضافة ivar وخاصيتين. سأقوم فقط بعرض الإضافات بدلاً من إعادة كتابة الكود الذي قمت بعرضه سابقاً.

#import "GameConfig.h"
@interface GameLayer : CCLayer {    // 1    BOOL _locations[kRows][kColumns];}
// 2@property (nonatomic, assign) NSInteger level;@property (nonatomic, retain) NSMutableArray *sprouts// ...Other properties...
1- هذه عبارة عن مصفوفة ذات اتجاهين من قيم نوعها Boolean استخدمت لتعبر عن المكان الذي تم وضع البرعم فيه. القيمة True تعني أنه موجود بينما القيمة False تعني أنه غير موجود.
2- هذا هو المستوى الحالي للعبة. العديد من الأحداث سوف تعتمد على هذه الخاصية. الخاصية الأخرى تحتوي مصفوفة من كائنات البراعم الموجودة حالياً في اللعب. تأكد من عمل @synthesize لهذه الخصائص في ملف ال implementation بمجرد إفراغ مصفوفة البراعم. هذه هي المرة الأخيرة التي سأخبرك أن تقوم بعمل synthesize و release حيث سأفترض أنها أصبحت بديهيات بالنسبة لك.
شيء آخر لتلاحظه وهو أنك تحتاج لعمل استيراد import لـ GameConfig.h لتستطيع استخدام kRows و kColumns. وسنقوم بشرح تفاصيلهم فيما بعد.
الآن قم بفتح GameLayer.m ابدأ بإضافة واجهة خاصة ل GameLayer في الأعلى بحيث تستطيع تعريف بعض ال methods التي ستقوم باستخدامها. كذلك يجب أن تتأكد من أنك قد عملت import ل Sprout.h لأنك ستحتاجها هنا.
قبل جزئية implementation قم بإضافة الكود التالي:

#import "Sprout.h"
@interface GameLayer(Private)- (CGPoint)randomEmptyLocation;- (void)placeRandomSprout;@end
هذه اثنتان من الـ methods  helper الإضافية والتي سنستخدمها , وسأقوم بتفصيلها أثناء بناءها.
 والآن قم بإضافة الكود التالي في نهاية 
init method  داخل أقواس
if( (self=[super init]))
// 1self.level = 1; // 2 for(int i = 0; i < kRows; i++) { for(int j = 0; j < kColumns; j++) { _locations[i][j] = NO; }}// 3srand(time(NULL));_sprouts = [[NSMutableArray alloc] init];for(int i = 0; i < kStartingSproutsCount; i++) { // 4 [self placeRandomSprout];}
1- ابدأ اللعبة من المستوى الأول.
2- قم بعمل loop على _locations، قم بتعريفهم جميعاً و أعطهم القيمة  NO
3- قم بعمل loop  بعدد kStartingSproutsCount  وضع براعم عشوائية في الشبكة.
في هذا الكود هناك المزيد من الثوابت التي علينا تعريفها، لنقم بتعريف هذه الثوابت في ملف GameConfig.h
#define kGridCellSize 16#define kColumns 18#define kRows 20#define kStartingSproutsCount 50
#define kGameAreaStartX 24#define kGameAreaStartY 64#define kGameAreaHeight 353#define kGameAreaWidth 288

الخطوة الأخيرة هنا هي بناء ال methods الاثنتان واللتين قمنا بتعريفهم في الأعلى. قم بإضافة هذا الكود إلى ملف GameLayer.m
- (void)placeRandomSprout {    // 1    Sprout *sprout = [[[Sprout alloc] initWithGameLayer:self] autorelease];    CGPoint p = [self randomEmptyLocation];          
    // 2    sprout.position = ccp(p.x * kGridCellSize + kGameAreaStartX,                      (kGameAreaStartY - kGridCellSize + kGameAreaHeight) -          p.y * kGridCellSize - kGridCellSize/2);    // 3    [self.sprouts addObject:sprout];}
- (CGPoint) randomEmptyLocation {    int column;    int row;    BOOL found = NO;
    // 1    while(!found) {             // 2    column = (arc4random() % kColumns);        row = (arc4random() % kRows);             // 3        if(!_locations[row][column]) {            found = YES;            _locations[row][column] = YES;        }    }         // 4    return CGPointMake(column, row);}

placeRandomSprout

1- نبدأ بتعريف كائن برعم جديد و اختيار موقع عشوائي لوضعه. في هذه المرحلة، الموقع في ال grid space و سنحتاج إلى تحويله إلى world space لرسمه.
2- نحن نقوم ببعض العمليات الحسابية الأساسية  لربط  هذا البرعم من grid space إلى world space في حجم مساحة اللعب وحجم الخلية.
3-     وأخيراً يتم إضافة كائن البرعم إلى مصفوفة البراعم الحية.

randomEmptyLocation

1- هذه ال loop لن تكتمل حتى نجد موقع فارغ. باعتقادي أنه سيكون هناك وقت ليس فيه أي مواقع متاحة وستستمر ال loop إلى المالانهاية، اللعبة ستكون سرعتها  ridiculousبحيث لا يستطيع أي لاعب أن يلعبها. ممكن عمل تعديل بحيث يتم عمل فحص للتأكد من أن المسافات غير مملوءة أولاً.
2- نختار صف وعمود بشكل عشوائي
3- نفحص إذا كان هناك برعم موجود في ذاك الموقع، إذا كان لا يوجد أي برعم فم بعمل تحديث _locations ivar وننهي ال loop
4- وأخيراً، نقوم بإرجاع النقطة الجديدة التي تم إنشاءها.
بعد ذلك إذا قمنا بتشغيل الكود سترى أن حقل البرعم تم إنشاؤه وانتشر حول منطقة اللعب. وقمت بمراعاة أن البرعم لا spawn أبداً في الصفوف العلوية أو السفلية لتكون الأمور أكثر متعة للاعب واليرقات.
لعبتك الآن يجب أن تبدو كذلك

الخطوة الرابعة: كائن(ال (object الخاص باللاعب
الخطوة المنطقية التالية في إنشاء واجهة المستخدم هي بناء ال object الخص باللاعب. وبالطبع سنستخدم فيه super class وسيكون إنشاؤه بسيطاً بناءً على ذلك.
قم بعمل كلاس فرعي من GameObject  وسميه player وانسخ الكود التالي وقم بلصقه في ملف Player.h
#import "GameObject.h"
@interface Player : GameObject
@property(nonatomic) NSInteger lives;@property(nonatomic) NSInteger score;
@end
اللاعب يضيف حيوات و أهداف إلى ال object الرئيسي GameObject. سنقوم باستخدام هاتين الخاصيتين لعرض عدد ال lives المتبقية وكذلك عدد الأهداف في الجزء القادم.
الآن قم بفتح Player.m وأضف الكود التالي:
#import "Player.h"#import "GameLayer.h"
@implementation Player
@synthesize score = _score;@synthesize lives = _lives;
- (id)initWithGameLayer:(GameLayer *)layer {    if(self = [super initWithGameLayer:layer]) {        self.sprite = [CCSprite spriteWithSpriteFrameName:@"player.png"];        [self.gameLayer.spritesBatchNode addChild:self.sprite];    }    return self;}
@end 

الـ initWithGameLayer method للاعب بالضبط مثل Sprout's method ماعدا أنها تضيف وحش مختلف إلى اللعبة

هذا كل شيء بالنسبة لكائن اللاعب(player object)، الخطوة الأخيرة هي تعريف اللاعب وإضافته للعبة.

يجب أن نكون عرفنا كائن من نوع لاعب في GameLayer.h، وتأكد من عمل import لكائن ال player هنا أيضاً
@property (nonatomic, retain) Player *player;
الآن قم بإضافة الكود التالي أسفل الكود الذي قمت بكتابته في اخر جزء في ال init method_player = [[Player alloc] initWithGameLayer:self];_player.lives = kPlayerStartingLives;_player.position = ccp(kGameAreaStartX + (kGameAreaWidth / 2), 88);_player.score = 0;
وهذا يعرف ثابت جديد لنضيفه في ملف GameConfig.h
#define kPlayerStartingLives 3

إذا قمت بتشغيل اللعبة الآن يجب أن ترى مربع اللاعب الأحمر مضافاً إلى اللعبة.


الخطوة الخامسة: عرض عدد مرات حياة اللاعب
نحتاج هنا إلى إعطاء اللاعب نوع من التغذية الراجعة مثل عدد مرات حياة اللاعب المتبقية (أي كم فرصة مازالت لديه للحياة). الطريقة التي اخترتها لبناء هذا هي عرض player's sprite في الزاوية العليا على اليسار. إذا كان اللاعب لديه ثلاث فرص للحياة، سيتم عرض ثلاث مربعات حمراء.
هناك بعض الطرق لعمل هذا. ولقد اخترت أن أنشئ مصفوفة لعدد مرات player sprites واستخدامها لإدارة عرض عدد مرات حياة اللاعب. بدءاً بإضافة خاصية جديدة إلى كلاس GameLayer اسمها livesSprites.

@property(nonatomic, retain) NSMutableArray *livesSprites;
والآن قم بإضافة الكود التالي لل init method
// init live sprites_livesSprites = [[NSMutableArray alloc] init];     // Register for lives notifications[[NSNotificationCenter defaultCenter] addObserver:self                                         selector:@selector(updateLives:)                                             name:kNotificationPlayerLives                                           object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationPlayerLives                                                        object:nil];

لاحظ أننا قمنا بإضافة GameLayer كمراقب لإخطار اسمه kNotificationPlayerLives. ولأننا قد لا نقوم بإنقاص عدد مرات حياة اللاعب داخل GameLayer سنحتاج لعمل UI respond عندما يتم ضرب اللاعب.
 هذه أسهل طريقة لعمل ذلك، ولاحظ أيضاً أنني أقوم بإرسال الإخطار في الفور. ولأن هذه ال 
method مسؤولة عن رسم عدد مرات حياة اللاعب، سنقوم باستدعائها من خلال init لعرض عدد مرات حياة اللاعب في بداية اللعبة.
 وهنا أيضاً نقوم بإضافة ثابت آخر اسمه 
kNotificationPlayerLives. تأكد من أنك أضفته في ملف GameConfig.h.
 وسنقوم بعمل نفس الشيء مع عدد أهداف اللاعب في الدقيقة، ولذلك سنقوم بإضافة الثوابت للغرضين.

#define kNotificationPlayerLives @"PlayerLivesNotification"#define kNotificationPlayerScore @"PlayerScoreNotification"

الآن سنقوم ببناء updateLives method في الكود. والتي ستكون مسؤولة عن رسم عدد مرات حياة اللاعب والانتقال إلى شاشة انتهاء اللعبة في حال انتهاء عدد مرات حياة اللاعب ووصولها إلى الصفر، قم بإضافة الكود التالي إلى ملف GameLayer.m
- (void) updateLives:(NSNotification *) notification {     NSInteger lifeCount = self.player.lives;
    // 1    [self.livesSprites enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {        [self.spritesBatchNode removeChild:obj cleanup:YES];    }];      [self.livesSprites removeAllObjects];
    // 2    for(int i = 0; i < lifeCount; i++) {        CCSprite *sprite = [CCSprite spriteWithSpriteFrameName:@"player.png"];        sprite.position = ccp(kGameAreaStartX + (i * kGridCellSize * 2), 435);        [self.livesSprites addObject:sprite];        [self.spritesBatchNode addChild:sprite];    }
}
(1) في البداية نقوم بمسح مصفوفة عدد مرات حياة اللاعب ومسح ما يعبر عنهم على شاشة اللعب
(2) ثم نقوم بعمل loop على ما تبقى من حياة اللاعب واضافتها إلى شاشة اللعب
الآن انتهينا من هذا وبإمكانك تشغيل التطبيق وسترى ما يعبر عن حياة اللاعب معروض في الأعلى

الخطوة السادسة: عرض عدد أهداف اللاعب
هذه العملية مشابهة جداً لسابقتها، ولكنا سوف نقوم باستخدام CCLabelTTF لعرض أهداف اللاعب. وهي تستخدم في Cocos2D لعرض نص في الألعاب.
ابدأ بفتح الملف GameLayer.h وقم بإضافة الخاصية التالية:

@property(nonatomic, retain) CCLabelTTF *playerScoreLabel;

ثم قم بفتح init method من GameLayer.m وقم بتعريف التسميات والإشعارات بكتابة الكود التالي:
_playerScoreLabel = [[CCLabelTTF labelWithString:@"0"                                      dimensions:CGSizeMake(100, 25)                                       alignment:UITextAlignmentRight                                        fontName:@"Helvetica"                                        fontSize:18] retain];_playerScoreLabel.position = ccp(254,435);[self addChild:_playerScoreLabel];
[[NSNotificationCenter defaultCenter] addObserver:self                                         selector:@selector(updateScore:)                                             name:kNotificationPlayerScore                                           object:nil];     [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationPlayerScore                                                    object:nil]; 

الخطوة التالية هي بناء updateScore method لعرض الأهداف. قم بإضافة هذا الكود GameLayer.m

- (void) updateScore:(NSNotification *) notification {    [self.playerScoreLabel setString:[NSString stringWithFormat:@"%d",self.player.score]];}
CCLabelTTF شبيهة جدا ب UILabel في UIKit. بمجرد أن نقوم بتعديل عدد الأهداف في اللعبة نقوم بإرسال إشعار وسيتم تحديث ال UI تبعاً لذلك.
أخر إصدار من اللعبة سيكون بهذا الشكل
المشروع يبدو الآن كلعبة حقيقية.
في المرة القادمة
في السلسة التعليمية التالية سنغطي الجزء الأكثر تعقيداً ومتعة من هذه السلسلة. حيث سنقوم ببناء كائن اليرقة و ال AI الخاصة به.

الكــاتــب

    • مشاركة

ليست هناك تعليقات:

جميع الحقوق محفوظة لــ الشبح للمعلوميات 2019 ©