曾經經典的微信打飛機遊戲還有人記得嗎?

語言: CN / TW / HK

theme: geek-black

我正在參加掘金社區遊戲創意投稿大賽個人賽,詳情請看:遊戲創意投稿大賽

前言

記得很多年前,微信推出來打飛機的小遊戲,當時在社交軟件中加入了這樣一個遊戲確實挺新鮮的。為了能讓自己能在排行榜前列,一遍又一遍的打飛機,有時候次數用完了,還會分享給很久沒有聯繫的朋友。講真,當時社交+遊戲的結合真的很棒。是否也有小夥伴像我一樣一次又一次的刷榜呢?

RPReplay_Final1649419258.2022-04-21 18_48_37.gif

偶然有一天突然想完微信打飛機的遊戲,然後就自己寫了個簡單的Demo,簡單的實現了打飛機的玩法。玩家可以通過接受降落傘包獲取炸彈以及加強的子彈,玩家擊毀更多的敵機以獲取更多的得分。

這個項目具體的實現是通過iOS中游戲引擎框架SpriteKit。在這篇文章中,我將介紹下游戲的整體實現。 在介紹之前,我先簡單介紹下SpriteKit中基礎的幾個概念以便我們更好的理解一個遊戲的實現。

首先在遊戲中,一般會有由一個場景(Scene)或者多個場景組成,在場景中我們可以添加很多節點(Node),每個節點可以繪製自己的紋理(Texture),有自己的行為(Action)。這樣我們就可以根據這些元素來構建一個物理世界。但是似乎還少了些什麼吧。其實就是物理碰撞檢測,在各個節點的物理邊界發生碰撞時,由遊戲引擎提供的方法回調給開發者來響應。

在我們簡單瞭解這些之後,就可以上手擼一個飛機了。

微信打飛機遊戲

我們將飛機大戰遊戲的實現大體分為一下幾個步驟:

  • 構建遊戲的場景
  • 初始化背景
  • 初始化飛機、敵機
  • 初始化降落傘
  • 物理碰撞檢測
  • 豐富玩法

構建遊戲的場景

我們需要先初始化遊戲視圖SKView,使用SKView呈現遊戲場景SKScene

[self.view addSubview:self.skView]; [self.skView presentScene:self.scene]; 在遊戲場景中,我們引入物理世界SKPhysicsWorld- (void)initPhysicsWorld {     self.physicsWorld.contactDelegate = self;     self.physicsWorld.gravity = CGVectorMake(0, 0); }

創建紋理集

遊戲中經常會把多個圖片資源打包成一個紋理集,紋理集包含一個圖片和一個plist文件(包含每個圖片的位置、大小,角度,偏移等關鍵信息)。如果有人問為什麼使用紋理集呢?那麼建議你動手來製作一個小遊戲,這樣一定會有所收穫。

我們需要把遊戲中所需要用到的紋理提前通過SKTextureAtlas來獲取出來。這裏,我們創建了一個單例類SKSharedAtles

+ (instancetype)shared {     static dispatch_once_t onceToken;     dispatch_once(&onceToken, ^{         // 創建紋理集         _shared = (SKSharedAtles *)[SKSharedAtles atlasNamed:@"gameArts-hd"];     });     return _shared; }

然後依次取出背景、飛機、子彈、降落傘等等的紋理 - (SKTexture *)textureNamed:(NSString *)name;

``` + (SKTexture *)textureWithBomb {     return [[[self class] shared] textureNamed:@"bomb.png"]; }

  • (SKTexture *)textureWithSKTextureTypeBulletType:(NSInteger )type {     return [[[self class] shared] textureNamed:[NSString stringWithFormat:@"bullet%ld.png",type]]; }

... ```

有些紋理是用來組成一個動畫的,比如説,飛機被擊中後爆炸會有一個序列幀動畫,那麼在這裏我們先將紋理裝入數組中,然後通過一系列的紋理來初始化一個動畫。 ``` + (SKAction )playerPlaneAction{     NSMutableArray textures = [[NSMutableArray alloc]init];     for (int i= 1; i<=2; i++) {         SKTexture *texture = [[self class] playerPlaneTextureWithIndex:i];         [textures addObject:texture];     }     return [SKAction repeatActionForever:[SKAction animateWithTextures:textures timePerFrame:0.1]]; }

... ```

初始化背景

通常在飛機大戰中游戲背景會一直隨着遊戲的進行而移動,並且播放背景音效。這個也是遊戲世界構建的第一步。 在SpriteKit中萬物皆是精靈,背景也是SKSpriteNode,所以我們只需取出背景的紋理,並且設置好錨點以及比例。背景設置完之後,我們開始循環播放背景音效。 ``` - (void)initBackground {     _adjustmentBackgroundPosition = self.size.height;     _background1 = [SKSpriteNode spriteNodeWithTexture:[SKSharedAtles textureWithType:SKTextureTypeBackground]];     _background1.position = CGPointMake(self.size.width/2, 0);     _background1.anchorPoint = CGPointMake(0.5, 0.0);     _background1.zPosition = 0;     _background1.xScale=1.5;     _background1.yScale=1.5;

_background2 = [SKSpriteNode spriteNodeWithTexture:[SKSharedAtles textureWithType:SKTextureTypeBackground]];     _background2.anchorPoint = CGPointMake(0.5, 0.0);     _background2.position = CGPointMake(self.size.width / 2, HEIGHT - 1);     _background2.zPosition = 0;     _background2.xScale=1.5;     _background2.yScale=1.5;     [self addChild:_background1];     [self addChild:_background2];     [_NearbyArray addObject:_background1];     [_NearbyArray addObject:_background2];     [self runAction:[SKAction repeatActionForever:[SKAction playSoundFileNamed:@"game_music.mp3" waitForCompletion:YES]]]; } ```

初始化玩家飛機、敵機

在初始化玩家飛機以及敵機時,需要設置物理體。 categoryBitMask:設置物理體標識符,collisionBitMask:設置碰撞標識符,contactTestBitMask:設置可以那類物理體碰撞; 物理體這裏的設置決定後面的物理碰撞檢測。

// 初始化玩家飛機 - (**void**)initPlayerPlane{     _playerPlane = [SKSpriteNode spriteNodeWithTexture:[SKSharedAtles textureWithType:SKTextureTypePlayerPlane]];     _playerPlane.position = CGPointMake(160, 50);     _playerPlane.zPosition = 1;     _playerPlane.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(_playerPlane.size.width-20, _playerPlane.size.height) ];     _playerPlane.physicsBody.categoryBitMask = SKRoleCategoryPlayerPlane;     _playerPlane.physicsBody.collisionBitMask = 0;     _playerPlane.physicsBody.contactTestBitMask = SKRoleCategoryFoePlane;     [self addChild:_playerPlane];     [_playerPlane runAction:[SKSharedAtles playerPlaneAction]]; }

初始化完玩家飛機之後,我們需要對玩家飛機進行操作,這裏就需要用到在拖動的回調中處玩家飛機的位置。

``` // 移動 - (void)touchesMoved:(NSSet )touches withEvent:(UIEvent )event{

for (UITouch touch in touches) {         CGPoint location = [touch locationInNode:self];         // 超出屏幕         if (location.x >= self.size.width - (_playerPlane.size.width / 2)) {             location.x = self.size.width - (_playerPlane.size.width / 2);         } else if (location.x <= (_playerPlane.size.width / 2)) {

location.x = _playerPlane.size.width / 2;         }

if (location.y >= self.size.height - (_playerPlane.size.height / 2)) {             location.y = self.size.height - (_playerPlane.size.height / 2);         } else if (location.y <= (_playerPlane.size.height / 2)) {             location.y = (_playerPlane.size.height / 2);         }         SKAction *action = [SKAction moveTo:CGPointMake(location.x, location.y) duration:0];         [_playerPlane runAction:action];     } } ```

敵機一般都是在場景中隨機出現的,這個需要我們要在SKScene中的場景更新中- (void)update:(NSTimeInterval)currentTime;隨機初始化敵人飛機,以及滾動背景。

- (void)update:(NSTimeInterval)currentTime {     [self createFoePlane];     [self createParachute];     [self scrollBackground]; }

初始化炮彈

玩家的飛機已經初始化完成了,但飛機還需要射出一梭梭的子彈。我們可以先將子彈創建方法寫好,設置子彈的物理體,以及子彈的音效。最後我們重複的發射子彈就可以。

``` // 創建炮彈 - (void)createBulletsWithType:(NSInteger)bulletType {     _bullet = [SKSpriteNode spriteNodeWithTexture:[SKSharedAtles textureWithSKTextureTypeBulletType:bulletType]];     _bullet.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_bullet.size];     _bullet.physicsBody.categoryBitMask = SKRoleCategoryBullet;     _bullet.physicsBody.collisionBitMask = SKRoleCategoryFoePlane;     _bullet.physicsBody.contactTestBitMask = SKRoleCategoryFoePlane;     _bullet.zPosition = 1;     _bullet.position = CGPointMake(_playerPlane.position.x, _playerPlane.position.y+ (_playerPlane.size.height/2));     [self addChild:_bullet];     SKAction actionMove = [SKAction moveTo:CGPointMake(_playerPlane.position.x, self.size.height) duration:0.8];     SKAction actionMoveDone = [SKAction removeFromParent];     [_bullet runAction:[SKAction sequence:@[actionMove,actionMoveDone]]];     [self runAction:[SKAction playSoundFileNamed:@"bullet.mp3" waitForCompletion:NO]]; }

// 初始化炮彈 - (void)initBullets {     _bulletType = 1; //默認1發炮彈     SKAction action = [SKAction runBlock:^{         [self createBulletsWithType:_bulletType];     }];     SKAction interval = [SKAction waitForDuration:0.2];     //場景循環炮彈的動畫     [self runAction:[SKAction repeatActionForever:[SKAction sequence:@[action,interval]]]withKey:@"Bullets"]; } ```

物理碰撞檢測

物理碰撞檢測是遊戲中的最複雜的部分,這裏需要處理物體之間的碰撞,所用的判斷條件也是在前面設置物理體時的一些設置。在飛機大戰中,我們的飛機不能和敵機發生碰撞,一旦發生碰撞即GameOver,玩家飛機的子彈和敵機發生碰撞即減少敵機血量,血量為零則爆炸擊毀。

- (void)didBeginContact:(SKPhysicsContact *)contact{ if (contact.bodyA.categoryBitMask & SKRoleCategoryFoePlane || contact.bodyB.categoryBitMask & SKRoleCategoryFoePlane) {         SKFoePlane *sprite = (contact.bodyA.categoryBitMask & SKRoleCategoryFoePlane) ? (SKFoePlane *)contact.bodyA.node : (SKFoePlane *)contact.bodyB.node;         if (contact.bodyA.categoryBitMask & SKRoleCategoryPlayerPlane || contact.bodyB.categoryBitMask & SKRoleCategoryPlayerPlane) {             SKSpriteNode *playerPlane = (contact.bodyA.categoryBitMask & SKRoleCategoryPlayerPlane) ? (SKSpriteNode *)contact.bodyA.node : (SKSpriteNode *)contact.bodyB.node;             [self playerPlaneCollisionAnimation:playerPlane];         } else {             SKSpriteNode *bullet = (contact.bodyA.categoryBitMask & SKRoleCategoryFoePlane) ? (SKFoePlane *)contact.bodyB.node : (SKFoePlane *)contact.bodyA.node;             [bullet removeFromParent];             [self foePlaneCollisionAnimation:sprite];         }     } else if (...) { ... } ... }

豐富玩法

飛機大戰的遊戲玩法其實還可以在豐富一些。我們可以通過將一些飛機速度、數量,子彈速度等參數調教。

然後設置簡單,中等,困難的等級去挑戰。在遊戲中,設置合適的數值,將大大增加遊戲的可玩性。除了調參以外,我們也可以將降落傘的軌跡飄忽不定,來增加用户的緊張感,以及犯錯誤的機率。

RPReplay_Final1649419258.2022-04-21 18_51_57.gif

結尾

在本片文章介紹了使用iOS中的SpriteKit完成一個微信打飛機的遊戲的整體思路。小夥伴們感興趣的,可以下載 Demo試玩下。