วิธีการเขียนเกมบน iPhone โดยไม่ใช้ Cocos2D มาเกี่ยวเลย ให้ได้เกมแนว FlappyBird แบบง่ายที่สุด ไม่ต้องใช้ Framework ในการพัฒนา พร้อม Source Code ในบทความจากบทความนี้ครับ ปิดตำนานเกมฮิต FlappyBird ถอนแอพพลิเคชันออกไปแล้ว ทำให้หลายคนเริ่มอยากจะเล่น และอยากจะรู้ว่าเกมนี้มันเขียนยากไหม ในต่างประเทศมีคนขาย Source code เจ้าเกมแนวนี้เยอะมาก ปาไป 3,000 บาทเลยแหละทั้ง Flappy Crocodile และ Flappy Penguin สำหรับ iOS Game ก็มีไอเดียการพัฒนาแยกย่อยแตกต่างไปทั้งแบบมี Framework Cocos2DX หรือไม่ใช่ ในตัวอย่างที่ผมลองไปหา Ref จากเมืองนอกมาโมครับ ก็เป็นแนวง่ายๆ
โปรเจ็คนี้ไม่ต้องเขียนด้วย Cocos2D นะครับขอย้ำ ให้เริ่มโดยการทำดังนี้
เปิด New Project ขึ้นมาใหม่เป็น Single View Application ครับ
ตั้งชื่อเท่ๆ ของเกมเราผม ตั้งชื่อว่า FlappySuck ครับ
วิธีการก็คือเปิดไฟล์ MainStoryBoard ขึ้นมา เลือก Control คือ View วางลงเป็นฉากก่อนนะครับ ตามด้วยพื้น ต่อมาเลือก UIImage View ครับใส่ภาพนกลงไปก่อน
เปิดไฟล์ ViewController.h ขึ้นมาทำการ Link สร้าง IBOutlet ทุกตัวครับ UIImageView ตั้งชื่อว่า block และ View ทั้งฉากหลัง และพื้นตั้งว่า sky และ ground
#import
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *ground;
@property (strong, nonatomic) IBOutlet UIView *sky;
@property (strong, nonatomic) IBOutlet UIImageView *block;
@end
ระหว่างนั้นเรียงลำดับ Layer ของ ViewController บน MainStoryBoard ให้ดีนะครับ ตัวล่างสุดอยู่ Layer บนสุดครับ
เปิดไฟล์ ViewController.m ขึ้นมาครับ ประกาศ Header ตามนี้
#import "ViewController.h"
@interface ViewController ()
@end
#define PIPE_SPACE 200
#define PIPE_WIDTH 75
#define DEFAULT_OFFSET 320.0
#define NEPHRITIS [UIColor colorWithRed:39.0/255 green:174.0/255.0 blue:96.0/255.0 alpha:1.0]
@implementation ViewController{
UIView *pipeBounds;
UIDynamicAnimator *blockAnimator;
UICollisionBehavior *blockCollision;
UICollisionBehavior *groundCollision;
UIDynamicItemBehavior *blockDynamicProperties;
UIDynamicItemBehavior *pipesDynamicProperties;
UIGravityBehavior *gravity;
UIPushBehavior *flapUp;
UIPushBehavior *movePipes;
int points2x;
int lastYOffset;
UIAlertView *gameOver;
Boolean firstFlap;
}
ต่อมาไปที่ Method ของ ViewDidLoad() ครับ เพิ่ม Code ไปตามนี้ เป็นการระบุ การสร้าง พื้น Ground และ Generate ตัวท่อออกมาครับ ให้เป็นสีเขียว
- (void)viewDidLoad
{
[super viewDidLoad];
firstFlap = NO;
// Create Block Animator
blockAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
blockDynamicProperties = [[UIDynamicItemBehavior alloc] initWithItems:@[self.ground]];
blockDynamicProperties.allowsRotation = NO;
blockDynamicProperties.density = 1000;
// Block flap behavior
flapUp = [[UIPushBehavior alloc] initWithItems:@[self.block] mode:UIPushBehaviorModeInstantaneous];
flapUp.pushDirection = CGVectorMake(0, -1.1);
flapUp.active = NO;
// Block Pipe Collision
blockCollision = [[UICollisionBehavior alloc] initWithItems:@[self.block]];
[blockCollision addBoundaryWithIdentifier:@"LEFT_WALL" fromPoint:CGPointMake(-1*PIPE_WIDTH, 0) toPoint:CGPointMake(-1*PIPE_WIDTH, self.view.bounds.size.height)];
blockCollision.collisionDelegate = self;
// Block Ground Collision
groundCollision = [[UICollisionBehavior alloc] initWithItems:@[self.block, self.ground]];
groundCollision.collisionDelegate = self;
[blockAnimator addBehavior:blockDynamicProperties];
[blockAnimator addBehavior:flapUp];
[blockAnimator addBehavior:blockCollision];
[blockAnimator addBehavior:groundCollision];
// Create Pipes Animator
points2x = 0;
lastYOffset = -100;
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(TapEvenntGesture:)];
[self.view addGestureRecognizer:singleTapGestureRecognizer];
[singleTapGestureRecognizer setNumberOfTapsRequired:1];
}
โดย Code ส่วนนั้นจะเกี่ยวข้องกับ การสร้างท่อ หรือ Pipe ดังนี้ครับ
- (void)getPipeGame:(float)xOffset {
lastYOffset = lastYOffset + (arc4random_uniform(3)*40) * getRand();
lastYOffset = (lastYOffset < -200)?-200:lastYOffset;
lastYOffset = (lastYOffset > 0)?0:lastYOffset;
UIView *topPipe = [[UIView alloc] initWithFrame:CGRectMake(xOffset, lastYOffset, PIPE_WIDTH, 300)];
[topPipe setRestorationIdentifier:@"TOP"];
[topPipe setBackgroundColor:NEPHRITIS];
[self.view addSubview:topPipe];
UIView *bottomPipe = [[UIView alloc] initWithFrame:CGRectMake(xOffset, lastYOffset+topPipe.bounds.size.height+PIPE_SPACE, PIPE_WIDTH, 300)];
[bottomPipe setRestorationIdentifier:@"BOTTOM"];
[bottomPipe setBackgroundColor:NEPHRITIS];
[self.view addSubview:bottomPipe];
pipesDynamicProperties= [[UIDynamicItemBehavior alloc] initWithItems:@[topPipe, bottomPipe]];
pipesDynamicProperties.allowsRotation = NO;
pipesDynamicProperties.density = 1000;
[blockCollision addItem:topPipe];
[blockCollision addItem:bottomPipe];
// Push Pipes across the screen
movePipes = [[UIPushBehavior alloc] initWithItems:@[topPipe, bottomPipe] mode:UIPushBehaviorModeInstantaneous];
movePipes.pushDirection = CGVectorMake(-2800, 0);
movePipes.active = YES;
[blockAnimator addBehavior:pipesDynamicProperties];
[blockAnimator addBehavior:movePipes];
}
อ้างอิงโดยการสุ่มการสร้างในเมธอด getRand()
int getRand() {
return (arc4random() % 2 ? 1 : -1);
}
ต่อมาเพิ่ม code ส่วนของ Event เมื่อมีการแตะหน้าจอครับ ให้นกกระเด้งขึ้น
- (void) TapEvenntGesture:(UITapGestureRecognizer *)gestureRecognizer {
if (!firstFlap) {
// Block gravity
gravity = [[UIGravityBehavior alloc] initWithItems:@[self.block]];
gravity.magnitude = 1.1;
[blockAnimator addBehavior:gravity];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
if (!gameOver.isHidden)[self getPipeGame:DEFAULT_OFFSET];
});
firstFlap = YES;
}
[flapUp setActive:YES];
}
พิจารณาหากแตะแล้วไม่ต่อเนื่อง นกจะร่วงตาม Gravity ของสูตรนี้
if (!firstFlap) {
// Block gravity
gravity = [[UIGravityBehavior alloc] initWithItems:@[self.block]];
gravity.magnitude = 1.1;
[blockAnimator addBehavior:gravity];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
if (!gameOver.isHidden)[self getPipeGame:DEFAULT_OFFSET];
});
firstFlap = YES;
}
ต่อมาถ้านก ร่วงชนพื้นหรือ ชนท่อก็ให้ใช้ หลักการนี้ครับ Collision Detect ตัวโปรแกรมใส่ลงไป
- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id)item withBoundaryIdentifier:(id)identifier atPoint:(CGPoint)p {
if ([(NSString*)identifier isEqualToString:@"LEFT_WALL"]) {
points2x++;
[blockCollision removeItem:item];
[blockAnimator removeBehavior:pipesDynamicProperties];
[blockAnimator removeBehavior:movePipes];
if (points2x%2 == 0) [self getPipeGame:DEFAULT_OFFSET];
}
}
- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id)item1 withItem:(id)item2 atPoint:(CGPoint)p {
[blockAnimator removeAllBehaviors];
gameOver = [[UIAlertView alloc] initWithTitle:@"มีความพยายาม" message:@"แต่ก็ยัง อ่อนหัด" delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
[gameOver show];
}
ตั้งค่าเกมไม่ให้มี การ พลิกหน้าจอเกม แนวตั้ง แนวนอน ให้เป็นแนวตั้งอย่างเดียว
- (BOOL)shouldAutorotate {
return NO;
}
ลองทดสอบ รันเกมของเราดูครับ โดยการ “Run”
ตัวอย่าง Source Code ตัว Project นี้ครับดาวน์โหลดที่นี่ครับ http://adf.ly/dPtAP
Ref: FlappyBlock, Cocos2D Resource