瘋狂ios講義之實例:貪吃蛇

實例:貪食蛇

輕掃手勢處理器在遊戲中的應用十分廣泛,比如在經典的貪食蛇遊戲中,可以讓用戶通過輕掃手勢處理器來改變蛇的移動方向。

貪食蛇的關鍵數據就是記錄蛇的身體,蛇身包含多個點,多個點的數據用於確定蛇身的位置,由於蛇身的長度會動態改變,因此程序將會使用NSMutableArray來記錄蛇身的每個點。

除此之外,貪食蛇只有兩個需要處理的邏輯。

蛇的移動:程序使用一個定時器控制蛇身的移動。對於蛇身的移動來說,蛇頭受移動方向的控制,當移動方向爲向左時,代表蛇頭的點的X座標減1;當移動方向爲向右時,代表蛇頭的點的X座標加1;當移動方向爲向上時,代表蛇頭的點的Y座標減1;當移動方向爲向下時,代表蛇頭的點的Y座標加1。除此之外,蛇身的其他點總是去“追逐”前一個點,也就是將第i個點的座標設置爲第i+1個點的座標。

蛇的方向改變:通過輕掃手勢處理器檢測用戶的輕掃手勢,根據輕掃手勢的方向來改變蛇的移動方向。

創建一個SingleView Application,該應用將會包含一個應用程序委託類和一個視圖控制器類,但程序還需要一個自定義UIView控件類,該控件類負責繪製貪食蛇的遊戲界面。

下面是自定義的FKSnakeView類(該類繼承了UIView)的接口代碼。

程序清單:codes/01/1.3/Snake/Snake/FKSnakeView.h

// 記錄地圖上的寬和高有多少個格子

#define WIDTH 15

#define HEIGHT 22

// 定義每個格子的大小

#define CELL_SIZE 20

typedefenum {

kDown = 0,

kLeft,

kRight,

kUp

} Orient;

@interface FKSnakeView : UIView<UIAlertViewDelegate>

// 定義蛇的移動方向

@property (nonatomic , assign) Orient orient;

@end


上面接口部分定義了一些宏變量,並使用了一個枚舉類型,該枚舉類型用於定義蛇的移動方向。

程序清單:codes/01/1.3/Snake/Snake/FKSnakeView.m

@implementation FKSnakeView

// 記錄蛇的點,最後一個點代表蛇頭

NSMutableArray* snakeData;

// 定義食物所在的點

FKPoint* foodPos;

NSTimer* timer;

UIColor* bgColor;

UIImage* cherryImage;

UIAlertView * overAlert;

// 代表遊戲音效變量

SystemSoundID gu;

SystemSoundID crash;

@synthesize orient;

- (id)initWithFrame:(CGRect)frame

{

self =[super initWithFrame:frame];

if(self) {

// 加載食物圖片

cherryImage = [UIImage imageNamed:@"cherry.png"];

// 加載遊戲背景圖片,並將背景圖片轉換爲平鋪形式的顏色

bgColor = [UIColor colorWithPatternImage:

[UIImage imageNamed:@"grass.png"]];

// 獲取兩個音效文件的URL

NSURL* guUrl = [[NSBundle mainBundle]

URLForResource:@"gu" withExtension:@"mp3"];

NSURL* crashUrl = [[NSBundle mainBundle]

URLForResource:@"crash" withExtension:@"wav"];

// 加載兩個音效文件

AudioServicesCreateSystemSoundID((__bridge CFURLRef)guUrl , &gu);

AudioServicesCreateSystemSoundID((__bridgeCFURLRef)crashUrl , &crash);

overAlert = [[UIAlertView alloc] initWithTitle:@"遊戲結束"

message:@"您輸了,是否重新再來?" delegate:self

cancelButtonTitle:@"不來了"otherButtonTitles:@"再來一盤!", nil];

[self startGame];

}

returnself;

}

- (void) startGame

{

//FKPoint的第1個參數控制位於水平第幾格,第2個參數控制位於垂直第幾格

snakeData = [NSMutableArray arrayWithObjects:

[[FKPoint alloc] initWithX:1 y:0],

[[FKPoint alloc] initWithX:2 y:0],

[[FKPoint alloc] initWithX:3 y:0],

[[FKPoint alloc] initWithX:4 y:0],

[[FKPoint alloc] initWithX:5 y:0],nil];

// 定義蛇的初始移動方向

orient =kRight;

timer = [NSTimerscheduledTimerWithTimeInterval:0.3 target:self

selector:@selector(move) userInfo:nilrepeats:YES]; //

}

- (void) move

{

// 除了蛇頭受方向控制之外,其他點都是佔它的前一個點

// 獲取最後一個點,作爲蛇頭

FKPoint*first = [snakeData objectAtIndex: snakeData.count - 1];

FKPoint*head = [[FKPoint alloc] initWithX:first.x y:first.y];

switch(orient)

{

case kDown: // 代表向下

// 新蛇頭的位置

head.y = head.y + 1;

break;

case kLeft: // 代表向左

// 新蛇頭的位置

head.x = head.x - 1;

break;

case kRight: // 代表向右

// 新蛇頭的位置

head.x = head.x + 1;

break;

case kUp: // 代表向上

// 新蛇頭的位置

head.y = head.y - 1;

break;

}

// 如果移動後蛇頭超出界面或與蛇身碰撞,則遊戲結束

if(head.x < 0 || head.x > WIDTH - 1

||head.y < 0 || head.y > HEIGHT - 1

||[snakeData containsObject:head])

{

// 播放碰撞的音效

AudioServicesPlaySystemSound(crash);

[overAlert show];

[timer invalidate];

};

// 表明蛇頭與食物點重合

if([headisEqual:foodPos])

{

// 播放吃食物的音效

AudioServicesPlaySystemSound(gu);

// 將食物點添加成新的蛇頭

[snakeData addObject:foodPos];

// 食物清空

foodPos = nil;

}

else

{

// 從第1個點開始,控制蛇身向前

for (int i = 0 ; i < snakeData.count- 1; i++)

{

// 將第i個點的座標設置爲第i+1個點的座標

FKPoint* curPt = [snakeDataobjectAtIndex:i];

FKPoint* nextPt = [snakeDataobjectAtIndex:i + 1];

curPt.x = nextPt.x;

curPt.y = nextPt.y;

}

// 重新設置蛇頭座標

[snakeData setObject:headatIndexedSubscript:(snakeData.count - 1)];

}

if(foodPos == nil)

{

while(true)

{

FKPoint* newFoodPos = [[FKPoint alloc]

initWithX:arc4random() % WIDTH

y:arc4random() % HEIGHT];

// 如果新產生的食物點沒有位於蛇身上

if(![snakeData containsObject:newFoodPos])

{

foodPos = newFoodPos;

break; // 成功生成了食物的位置,跳出循環

}

}

}

[selfsetNeedsDisplay];

}

// 定義繪製蛇頭的方法

- (void) drawHeadInRect:(CGRect)rectcontext:(CGContextRef)ctx

{

CGContextBeginPath(ctx);

// 根據蛇頭的方向,決定開口的角度

CGFloatstartAngle;

switch(orient) {

casekUp:

startAngle = M_PI * 7 / 4;

break;

casekDown:

startAngle = M_PI * 3 / 4;

break;

casekLeft:

startAngle = M_PI * 5 / 4;

break;

casekRight:

startAngle = M_PI * 1 / 4;

break;

default:

break;

}

// 添加一段弧作爲路徑

CGContextAddArc(ctx, CGRectGetMidX(rect), CGRectGetMidY(rect)

,CELL_SIZE / 2, startAngle, M_PI * 1.5 + startAngle, 0);

// 將繪製點移動到中心

CGContextAddLineToPoint(ctx, CGRectGetMidX(rect), CGRectGetMidY(rect));

// 關閉路徑

CGContextClosePath(ctx);

CGContextFillPath(ctx);

}

- (void)drawRect:(CGRect)rect

{

// 獲取繪圖API

CGContextRef ctx = UIGraphicsGetCurrentContext();

CGContextSetFillColorWithColor(ctx, [bgColor CGColor]);

// 繪製背景

CGContextFillRect(ctx, CGRectMake(0 , 0

,WIDTH * CELL_SIZE , HEIGHT * CELL_SIZE));

// 繪製文字

[@"瘋狂貪食蛇" drawAtPoint:CGPointMake(50,20)

withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:

[UIFont fontWithName:@"Heiti SC" size: 40] , NSFontAttributeName,

[UIColor colorWithRed:1 green:0 blue:1 alpha:.4],

NSForegroundColorAttributeName,nil]];

[@"www.fkjava.org" drawAtPoint:CGPointMake(50 ,60)

withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:

[UIFont fontWithName:@"Heiti SC" size: 26] ,NSFontAttributeName,

[UIColor colorWithRed:1 green:0 blue:1 alpha:.4],

NSForegroundColorAttributeName, nil]];

// 設置繪製蛇的填充顏色

CGContextSetRGBFillColor(ctx, 1, 1, 0, 1);

// 遍歷蛇的數據,繪製蛇的數據

for (int i = 0 ; i < snakeData.count ;i++ )

{

// 爲每個蛇的點(記錄的是在數組中的位置),在屏幕上繪製一個圓點

FKPoint* cp = [snakeDataobjectAtIndex:i];

// 定義將要繪製蛇身點的矩形

CGRect rect = CGRectMake(cp.x * CELL_SIZE, cp.y * CELL_SIZE

, CELL_SIZE , CELL_SIZE);

// 繪製蛇尾巴,讓蛇的尾巴小一些

if(i < 4)

{

CGFloat inset =(4 - i);

CGContextFillEllipseInRect(ctx,CGRectInset(rect,inset, inset));

}

// 如果是最後一個元素,代表蛇頭,繪製蛇頭

else if (i == snakeData.count - 1)

{

[self drawHeadInRect:rectcontext:ctx];

}

else

{

CGContextFillEllipseInRect(ctx,rect);

}

}

// 繪製“食物”圖片

[cherryImage drawAtPoint:CGPointMake(foodPos.x * CELL_SIZE

,foodPos.y * CELL_SIZE)];

}

-(void)alertView:(UIAlertView *)alertView

clickedButtonAtIndex:(NSInteger)buttonIndex

{

// 如果用戶單擊了第2個按鈕,則重新開始遊戲

if(buttonIndex== 1)

[self startGame];

}

@end


上面程序中,move方法中第1段粗體字代碼負責根據移動方向控制蛇頭的移動;move方法中第2段粗體字代碼採用循環將第i個點的座標設置爲第i+1個點的座標。

wKiom1M7Z9zAEgZ1AAErNWsWh-k617.jpg

該自定義類還在①號粗體字代碼處啓動了定時器,該定時器會控制程序每隔0.3秒執行一次move方法,move方法負責控制蛇身的移動,並在蛇身移動完成後通知FKSnakeView重繪自身。

接下來只要在視圖控制器中創建並添加FKSnakeView控件即可,除此之外,還需要在視圖控制器中使用輕掃手勢處理器來改變蛇的移動方向。下面是視圖控制器類的實現代碼(接口部分幾乎是空的)。

程序清單:codes/01/1.3/Snake/Snake/FKViewController.m

@implementation FKViewController

FKSnakeView* snakeView;

- (void)viewDidLoad

{

[super viewDidLoad];

// 創建FKSnakeView控件

snakeView = [[FKSnakeView alloc] initWithFrame:

CGRectMake(10, 10, WIDTH*CELL_SIZE , HEIGHT * CELL_SIZE)];

// snakeView控件設置邊框和圓角

snakeView.layer.borderWidth = 3;

snakeView.layer.borderColor = [[UIColor redColor] CGColor];

snakeView.layer.cornerRadius = 6;

snakeView.layer.masksToBounds = YES;

// 設置self.view控件支持用戶交互

self.view.userInteractionEnabled = YES;

// 設置self.view控件支持多點觸碰

self.view.multipleTouchEnabled = YES;

for(int i = 0 ; i < 4 ; i++)

{

// 創建手勢處理器,指定使用該控制器的handleSwipe:方法處理輕掃手勢

UISwipeGestureRecognizer*gesture = [[UISwipeGestureRecognizer alloc]

initWithTarget:selfaction:@selector(handleSwipe:)];

// 設置該手勢處理器只處理i個手指的輕掃手勢

gesture.numberOfTouchesRequired = 1;

// 指定該手勢處理器只處理1<< i方向的輕掃手勢

gesture.direction = 1 << i;

// self.view控件添加手勢處理器

[self.viewaddGestureRecognizer:gesture];

}

[self.view addSubview:snakeView];

}

// 實現手勢處理器的方法,該方法應該聲明一個形參

// 當該方法被激發時,手勢處理器會作爲參數傳給該方法的參數

- (void)handleSwipe:(UISwipeGestureRecognizer*)gesture

{

// 獲取輕掃手勢的方向

NSUInteger direction = gesture.direction;

switch (direction)

{

caseUISwipeGestureRecognizerDirectionLeft:

if(snakeView.orient!= kRight) // 只要不是向右,即可改變方向

snakeView.orient = kLeft;

break;

caseUISwipeGestureRecognizerDirectionUp:

if(snakeView.orient!= kDown) // 只要不是向下,即可改變方向

snakeView.orient = kUp;

break;

caseUISwipeGestureRecognizerDirectionDown:

if(snakeView.orient != kUp) // 只要不是向上,即可改變方向

snakeView.orient = kDown;

break;

caseUISwipeGestureRecognizerDirectionRight:

if(snakeView.orient!= kLeft) // 只要不是向左,即可改變方向

snakeView.orient = kRight;

break;

}

}

@end


上面程序中第1段粗體字代碼爲該控制器的UIView(即整個界面)註冊了4個方向的手勢處理器,第2段粗體字代碼位於手勢處理方法中,這些粗體字代碼會根據輕掃方向改變蛇的移動方向。編譯、運行該程序,即可看到如圖1.7所示的遊戲界面。

wKiom1M7Z_zyGqJ8AAFwSpJWhwQ465.jpg

1.7 貪食蛇遊戲界面


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章