DeveloperGame DevelopmentiOS DeveloperObjective CProgramming LanguageSocial MediaSocial Media Marketing

iOS Game Developer Tutorial: การเรียกใช้ Collision Detect ผ่าน Cocos2D

การเรียกใช้คำสั่ง Collision Detect ด้วย Cocos2D ให้ตัวละครในเกมยิงกระสุน ไปโดนศัตรูให้หายไปได้ผ่าน XCode สำหรับ iOS Developer มือใหม่ที่อยากพัฒนาเกมจากบทความก่อนหน้านี้ “iOS Game Developer Tutorial: วางภาพ Spriteตัวละครลงในเกมด้วย Cocos2D” ที่มีการวางตัวละครลงไปในเกมแล้ว และมีศัตรูปรากฏวิ่งไปมาแล้วต่อไปเราต้องสร้างกระสุนของตัวละครให้ยิงออกไปโดนศัตรู เริ่มต้นสร้างกระสุน และวิธีในการยิงของเรา ให้ไปเพิ่มคำสั่งเล็กน้อยที่ ฟังก์ชัน init() ครับ ก่อนคำสั่งประกาศขนาดหน้าจอของเรา

self.isTouchEnabled = YES;
CGSize winSize = [[CCDirector sharedDirector] winSize];

หาภาพกราฟิก สำหรับสร้างเป็นลูกกระสุนของเรา ตั้งชื่อว่า “Bullet.png”  ขนาดความก้าว และ ความยาว 40×40 pixels

ไฟล์ชื่อ "Bullet.png"
ไฟล์ชื่อ “Bullet.png”

พร้อมทั้งใส่คำสั่งต่อไปนี้ลงไปในไฟล์  PlayScene.m เป็นการเรียกคำสั่งเดียวกับเมนูคือเมื่อมีการแตะแล้วปล่อยที่พิกัดใดๆ กระสุนจะวิ่งไปยังพิกัดดังกล่าว

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
    // Choose one of the touches to work with
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:[touch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];
    
    // Set up initial location of projectile
    CGSize winSize = [[CCDirector sharedDirector] winSize];
    CCSprite *projectile = [CCSprite spriteWithFile:@"Bullet.png" 
    rect:CGRectMake(0, 0, 40, 40)];
    projectile.position = ccp(20, winSize.height/2);
    
    // Determine offset of location to projectile
    int offX = location.x - projectile.position.x;
    int offY = location.y - projectile.position.y;
    
    // Bail out if we are shooting down or backwards
    if (offX <= 0) return;

    [self addChild:projectile];

    int realX = winSize.width + (projectile.contentSize.width/2);
    float ratio = (float) offY / (float) offX;
    int realY = (realX * ratio) + projectile.position.y;
    CGPoint realDest = ccp(realX, realY);
    
    // Determine the length of how far we're shooting
    int offRealX = realX - projectile.position.x;
    int offRealY = realY - projectile.position.y;
    float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
    float velocity = 480/1; // 480pixels/1sec
    float realMoveDuration = length/velocity;
    
    // Move projectile to actual endpoint
    [projectile runAction:[CCSequence actions:
    [CCMoveTo actionWithDuration:realMoveDuration position:realDest],
    [CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)],
    nil]];
    
}

ทดลอง "Run” ตัวแอพพลิเคชันดู แล้วทำการ คลิกไปที่ บริเวณใดๆ ของหน้าจอจะพบว่าตัวละครจะยิงกระสุนไป ยังบริเวณที่เราคลิก หรือทำการแตะ

กระสุนยิงกระจุย
กระสุนยิงกระจุย

วิธีการตรวจสอบการชนกันของวัตถุ หรือ Collision Detect
Collision Detect เป็นหลักการเดียวที่ทุกเกม ไม่ว่าจะอยู่บนแพลตฟอร์มใดๆ จะต้องใช้งานเป็นสากลเพราะเป็นการตรวจสอบว่าวัตถุชิ้นหนึ่งเมื่อซ้อนทับหรือชนกับอีกชิ้นหนึ่งแล้วจะเกิดเงื่อนไขอะไรขึ้นซึ่งในเกมของเราจะใช้อยู่ 2 เงื่อนไขหลักๆ คือ

  • กระสุนโดนศัตรู ศัตรก็จะตาย
  • ศัตรูโดนเรา เราก็ตาย “GameOver”

ให้ไปที่ไฟล์ PlayScene.h ครับทำการประกาศ ตัวแปลชนิด NSMutableArray ลงไปดังรูปแบบข้างล่าง

#import 
#import "cocos2d.h"
@interface PlayScene : CCLayer {
    NSMutableArray *_targets;
    NSMutableArray *_projectiles;
}

ต่อมาให้ไปเพิ่มคำสั่งในไฟล์ PlayScene.m ครับในฟังก์ชัน init() บรรทัดต่อจาก คำสั่ง self.isTouchEnabled = YES; ไปเลยครับ

-(id) init
{
 if( (self=[super init] )) {
        
 self.isTouchEnabled = YES;
 _targets = [[NSMutableArray alloc] init];
 _projectiles = [[NSMutableArray alloc] init];

ทำการคืนหน่วยความจำของกระสุนปืน และศัตรูของเราลงไปในฟังก์ชัน dealloc() ครับ

-(void)dealloc
{
    [_targets release];
    _targets = nil;
    [_projectiles release];
    _projectiles = nil;
	[super dealloc];
}

เพิ่มคำสั่งลงไปในฟังก์ชัน addTarget() ในตำแหน่งบรรทัดล่างสุดก่อนปิดฟังก์ชัน

[target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
// Add to targets array
target.tag = 1;
[_targets addObject:target];

ให้เข้าไปเพิ่มที่ฟังก์ชัน ccTouchesEnded เช่นกันในตำแหน่งก่อนปิดฟังก์ชันบรรทัดสุดท้าย

// Add to projectiles array
projectile.tag = 2;
[_projectiles addObject:projectile];

ต่อมาแก้ไข ฟังก์ชัน spriteMoveFinished จากเดิมคือ

-(void)spriteMoveFinished:(id)sender {
    CCSprite *sprite = (CCSprite *)sender;
    [self removeChild:sprite cleanup:YES];
}

ให้กลายเป็น

-(void)spriteMoveFinished:(id)sender {
    CCSprite *sprite = (CCSprite *)sender;
    [self removeChild:sprite cleanup:YES];
    
    if (sprite.tag == 1) { // target
        [_targets removeObject:sprite];
    } else if (sprite.tag == 2) { // projectile
        [_projectiles removeObject:sprite];
    }
}

ทำการ "Run” ตรวจสอบดูอีกครั้งว่ามี Error ปรากฏไหม เพราะว่าเรามากันไกลมากแล้ว, ต่อไปจะเป็นการตรวจสอบเงื่อนไขของการ ชนกันของวัตถุ ที่สำคัญมาก แทรกคำสั่งนี้ลงไปในไฟล์ PlayScene.m เลยครับ

- (void)update:(ccTime)dt {
    
    NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init];
    for (CCSprite *projectile in _projectiles) {
        CGRect projectileRect = CGRectMake(
                projectile.position.x - (projectile.contentSize.width/2), 
                projectile.position.y - (projectile.contentSize.height/2), 
                projectile.contentSize.width, 
                projectile.contentSize.height);
        
        NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
        for (CCSprite *target in _targets) {
            CGRect targetRect = CGRectMake(
                target.position.x - (target.contentSize.width/2), 
                target.position.y - (target.contentSize.height/2), 
                target.contentSize.width, 
                target.contentSize.height);
            
            if (CGRectIntersectsRect(projectileRect, targetRect)) {
                [targetsToDelete addObject:target];				
            }						
        }
        
        for (CCSprite *target in targetsToDelete) {
            [_targets removeObject:target];
            [self removeChild:target cleanup:YES];									
        }
        
        if (targetsToDelete.count > 0) {
            [projectilesToDelete addObject:projectile];
        }
        [targetsToDelete release];
    }
    
    for (CCSprite *projectile in projectilesToDelete) {
        [_projectiles removeObject:projectile];
        [self removeChild:projectile cleanup:YES];
    }
    [projectilesToDelete release];
}

คำสั่งข้างต้นเป็นการเคลียร์ทุกสิ่งที่มีการ ชนกันโดยจะยึดตามขนาดของความกว้าง และความยาวของวัตถุที่ปรากฏในเกมเป็นหลักเพื่อตรวจสอบการชนกัน

ต่อมาให้แทรกคำสั่งต่อไปนี้ลงไปในฟังก์ชัน init() ต่อจากคำสั่ง Interval ก่อนปิดฟังก์ชัน

[self addChild:bgGame];	
[self addChild:player];		      
[self schedule:@selector(gameLogic:) interval:1.0];
[self schedule:@selector(update:)];

ทำการ "Run” แอพพลิเคชัน แล้วลอง ยิงกระสุนไปยังศัตรูของเราดูจะพบว่า ศัตรูของเราได้หายไปแล้วเมื่อโดนกระสุน

ยิงกระสุนโดนศัตรูกราดให้พอใจ
ยิงกระสุนโดนศัตรูกราดให้พอใจ

ในบทเรียนต่อไปจะเป็นพาไปรู้จักกับการตรวจสอบการชนกันเมื่อศัตรูเลื่อนมาโดนเราแล้วแสดงผลหน้าจอ Game Over และองคฺประกอบเพิ่มเติมต่อไปนี้

  • การใส่ไฟล์เสียงลงไป (Audio)
  • การทำฉากหลังเลื่อนได้ (Paralax BackGround)
  • การหยุดเกม (Pause)
  • การเปลี่ยนหน้า (Winner, Game Over)

Asst. Prof. Banyapon Poolsawas

อาจารย์ประจำสาขาวิชาการออกแบบเชิงโต้ตอบ และการพัฒนาเกม วิทยาลัยครีเอทีฟดีไซน์ & เอ็นเตอร์เทนเมนต์เทคโนโลยี มหาวิทยาลัยธุรกิจบัณฑิตย์ ผู้ก่อตั้ง บริษัท Daydev Co., Ltd, (เดย์เดฟ จำกัด)

Related Articles

Leave a Reply

Back to top button

Adblock Detected

เราตรวจพบว่าคุณใช้ Adblock บนบราวเซอร์ของคุณ,กรุณาปิดระบบ Adblock ก่อนเข้าอ่าน Content ของเรานะครับ, ถือว่าช่วยเหลือกัน