آموزش برنامه نویسی ios: طراحی بازی مشابه بازی انگری برد (قسمت سوم)

آموزش برنامه نویسی ios: طراحی بازی مشابه بازی انگری برد (قسمت سوم)

در دو قسمت قبل آموزش ساخت بازی فلاپی برد، مراحل مختلف ساخت بازی را توضیح دادیم در قسمت اول نحوه ایجاد محیط فیزیکی، پرنده را آموختیم و در قسمت دوم نحوه حرکت دادن به پرنده و کدهای مربوط به حرکت پرنده با زدن ضربه را نوشتیم در این قسمت که قسمت سوم و آخر این مجموعه است موانعی را در بازی ایجاد می کنیم.

 آخرین عنصری که در بازی قرار می گیرد لوله می باشد که بصورت موانعی در صحنه بازی قرار دارند به نحوی که پرنده نباید با آنها برخورد کند و در صورت برخورد پرنده بازی ریستارت می شود. برای انجام اینکار دو تصویر گرافیکی pipe1.png و pipe2.png را به پروژه اضافه می کنیم. از دیدگاه گرافیکی، لوله ها در فضای بازی عناصری خاص هستند چراکه اجزایی مثل زمین، آسمان و پرنده ها در بازی ثابت و بدون تغییر هستند اما لوله ها متغیر هستند، بنابراین با تعریف دو عنصر گرافیکی pipe1  و pipe2 یک لوله از بالا صفحه نمایش را گسترش می دهد و لوله دیگر از پایین صفحه نمایش.

لوله ها با دو متد  با نام SKSprintNode S تعریف شده اند و این دو متد را در متد خالی SKNode  (بعنوان والد) قرار می دهیم.

 

تعیین فاصله بین لوله ها می تواند دلخواه باشد که ما در اینجا این فاصله را 100 انتخاب کردیم

@implementation MyScene

 

static NSInteger const kVerticalPipeGap = 100;

 

-(id)initWithSize:(CGSize)size {

لوله ها در خارج از صفحه نمایش قرار می گیرند.

یک جفت از لوله ها در قسمت بیرونی سمت راست صفحه نمایش قرار میگیرند.دستور zPosition به نحوی برای لوله ها انتخاب شده است که همیشه در پشت زمین بازی قرار گیرند.

// Create pipes

 

SKTexture* _pipeTexture1 = [SKTexture textureWithImageNamed:@"Pipe1"];

_pipeTexture1.filteringMode = SKTextureFilteringNearest;

SKTexture* _pipeTexture2 = [SKTexture textureWithImageNamed:@"Pipe2"];

_pipeTexture2.filteringMode = SKTextureFilteringNearest;

 

SKNode* pipePair = [SKNode node];

pipePair.position = CGPointMake( self.frame.size.width + _pipeTexture1.size.width * 2, 0 );

pipePair.zPosition = -10;

 

CGFloat y = arc4random() % (NSInteger)( self.frame.size.height / 3 );

 

SKSpriteNode* pipe1 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture1];

[pipe1 setScale:2];

pipe1.position = CGPointMake( 0, y );

pipe1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe1.size];

pipe1.physicsBody.dynamic = NO;

[pipePair addChild:pipe1];

 

SKSpriteNode* pipe2 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture2];

[pipe2 setScale:2];

pipe2.position = CGPointMake( 0, y + pipe1.size.height + kVerticalPipeGap );

pipe2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe2.size];

pipe2.physicsBody.dynamic = NO;

[pipePair addChild:pipe2];

 

SKAction* movePipes = [SKAction repeatActionForever:[SKAction moveByX:-1 y:0 duration:0.02]];

[pipePair runAction:movePipes];

 

[self addChild:pipePair];

 

پرنده با برخورد با لوله ها، با حرکت صفحه نمایش از صفحه خارج می شود.

 

میخواهیم این لوله ها بصورت منظم در صفحه نمایش داده شوند و دیگر حرکت نکنند برای انجام اینکار مجددا از SKAction استفاده می کنیم.اما به شیوه ای متفاوت :

برای تولید مجدد و منظم لوله ها در صفحه بازی باید حالت و حرکت لوله ها را ذخیره کنیم:

@interface MyScene () {

    SKSpriteNode* _bird;

    SKColor* _skyColor;

    SKTexture* _pipeTexture1;

    SKTexture* _pipeTexture2;

    SKAction* _moveAndRemovePipes;

}

@end

 

 

عملکرد _moveAndRemovePipes درست بعد از کد قبلی اضافه می شود

CGFloat distanceToMove = self.frame.size.width + 2 * _pipeTexture1.size.width;

SKAction* movePipes = [SKAction moveByX:-distanceToMove y:0 duration:0.01 * distanceToMove];

SKAction* removePipes = [SKAction removeFromParent];

_movePipesAndRemove = [SKAction sequence:@[movePipes, removePipes]];

 

متد spawn برای ایجاد لوله های جدید است:

-(void)spawnPipes {

    SKNode* pipePair = [SKNode node];

    pipePair.position = CGPointMake( self.frame.size.width + _pipeTexture1.size.width, 0 );

    pipePair.zPosition = -10;

     

    CGFloat y = arc4random() % (NSInteger)( self.frame.size.height / 3 );

     

    SKSpriteNode* pipe1 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture1];

    [pipe1 setScale:2];

    pipe1.position = CGPointMake( 0, y );

    pipe1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe1.size];

    pipe1.physicsBody.dynamic = NO;

    [pipePair addChild:pipe1];

     

    SKSpriteNode* pipe2 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture2];

    [pipe2 setScale:2];

    pipe2.position = CGPointMake( 0, y + pipe1.size.height + kVerticalPipeGap );

    pipe2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe2.size];

    pipe2.physicsBody.dynamic = NO;

    [pipePair addChild:pipe2];

     

    [pipePair runAction:_movePipesAndRemove];

     

    [self addChild:pipePair];

}

 

برا ی اطمینان از اینکه spawnPipesکار خود را بدرستی انجام میدهد و بصورت دائم لوله ها را تولید می کند  با استفاده از متد  SKActionکه به SKScene اینکار را انجام می دهیم:

SKAction* spawn = [SKAction performSelector:@selector(spawnPipes) onTarget:self];

SKAction* delay = [SKAction waitForDuration:2.0];

SKAction* spawnThenDelay = [SKAction sequence:@[spawn, delay]];

SKAction* spawnThenDelayForever = [SKAction repeatActionForever:spawnThenDelay];

[self runAction:spawnThenDelayForever];

 

این متد spawnPipes نامیده می شود که عملکرد آن به اینصورت است که حین بازی دو ثانیه متوقف می شود و سپس دوباره شروع به کار میکند.

همانطور که در تصویر زیر می بینید لوله ها بصورت مرتب  و منظم در صحنه بازی قرار می گیرند.

تشخیص برخورد و پاسخ

حال ما نیاز داریم پس از برخورد پرنده با موانع، تغییراتی را در صحنه بازی مشاهده کنیم که برای انجام این کار از متد SKPhysicsContactDelegate استفاده می کنیم.

علاوه براین نیاز داریم، چندین مجموعه را با مدل های مختلف برای نمایش واکنش صفحه نمایش، به کدها اضافه کنیم:

@interface MyScene () <SKPhysicsContactDelegate> {

    SKSpriteNode* _bird;

    SKColor* _skyColor;

    SKTexture* _pipeTexture1;

    SKTexture* _pipeTexture2;

    SKAction* _movePipesAndRemove;

}

@end

 

@implementation MyScene

 

static const uint32_t birdCategory = 1 << 0;

static const uint32_t worldCategory = 1 << 1;

static const uint32_t pipeCategory = 1 << 2;

 

static NSInteger const kVerticalPipeGap = 100;

 

-(id)initWithSize:(CGSize)size {   

    if (self = [super initWithSize:size]) {

        /* Setup your scene here */

         

        self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 );

        self.physicsWorld.contactDelegate = self;

با استفاده از category می توان دسته بندی های مختلفی را تعریف کرد و تعیین کرد در صورت برخورد این اجزا با یکدیگر چه اتفاقی بیفتد .مانند آنچه در کد بالا توضیح داده شده است.

ما در اینجا برای سه عنصر لوله، جهان(که همان محیط بازی است) و پرنده، category تعریف کردیم دقت کنید که نیازی نیست برای همه عناصر کتگوری تعریف شود، فقط عناصری که برای واکنش نشان دادن حین برخورد دخالت دارند، مهم هستند.

 

برای متوقف کردن حرکت پرنده پس از برخورد، مولفه speed را برای متد SKAction تعریف می کنیم به نحوی که وقتی سرعت برابر صفر شد SKAction متوقف شود.

@interface MyScene () <SKPhysicsContactDelegate> {

    SKSpriteNode* _bird;

    SKColor* _skyColor;

    SKTexture* _pipeTexture1;

    SKTexture* _pipeTexture2;

    SKAction* _movePipesAndRemove;

    SKNode* _moving;

}

@end

 

تمام عناصر ایجاد شده از ابتدا کدنویسی به این گره پدر متصل می شوند.

_skyColor = [SKColor colorWithRed:113.0/255.0 green:197.0/255.0 blue:207.0/255.0 alpha:1.0];

[self setBackgroundColor:_skyColor];

 

_moving = [SKNode node];

[self addChild:_moving];

 

SKTexture* birdTexture1 = [SKTexture textureWithImageNamed:@"Bird1"];

birdTexture1.filteringMode = SKTextureFilteringNearest;

 

...

 

// Create ground

 

...

 

for( int i = 0; i < 2 + self.frame.size.width / ( groundTexture.size.width * 2 ); ++i ) {

    ...

    [_moving addChild:sprite];

}

 

// Create skyline

 

...

 

for( int i = 0; i < 2 + self.frame.size.width / ( skylineTexture.size.width * 2 ); ++i ) {

    ...

    [_moving addChild:sprite];

-(void)spawnPipes {

    ...   

    [pipePair runAction:_movePipesAndRemove];

     

    [_moving addChild:pipePair];

}

 

حال تمامی عناصر به جز پرنده را به گره پدر متصل کردیم و بدین ترتیب هر اتفاقی برای گره پدر بیفتد بر روی فرزندان نیز اعمال می شوداز طرفی می خواهیم حرکات پرنده فقط یکبار در بازی متوقف شود که بصورت کد زیر انجام می شود.

(void)didBeginContact:(SKPhysicsContact *)contact {

    if( _moving.speed > 0 ) {

        _moving.speed = 0;

     

        // Flash background if contact is detected

        ...

    }

حتما متوجه شده اید که پرنده را به گره ی _moving متصل نکردیم اینکار برای این است که می خواهیم پرنده همچنان حرکت داشته باشد و تا زمانیکه مقدار _moving.speed>0   است کنترل تحت نظر بازیکن باشد و با ضربه به صفحه نمایش بتواند حرکات پرنده را کنترل کند.

708
1395/04/28

بلاگ های مرتبط

ثبت نظر جدید

نظرات کاربران

آیا سوالی دارید ؟

با تلگرام 09108454545 و یا با ایمیل آدرس ایمیل سروش پرداز تماس برقرار کنید.

برای تماس با مشاورین میتوانید با شماره های 02186085365 و 02186084238 و 02186083746 تماس حاصل فرمایید همچنین برای تسهیل در ارتباطات میتوانید با تلگرام شرکت به شماره 09108454545 تماس بگیرید.


لوگو ساماندهی سروش پرداز