輕掃手勢處理器在遊戲中的應用十分普遍,好比在經典的貪食蛇遊戲中,可讓用戶經過輕掃手勢處理器來改變蛇的移動方向。java
貪食蛇的關鍵數據就是記錄蛇的身體,蛇身包含多個點,多個點的數據用於肯定蛇身的位置,因爲蛇身的長度會動態改變,所以程序將會使用NSMutableArray來記錄蛇身的每一個點。數組
除此以外,貪食蛇只有兩個須要處理的邏輯。dom
蛇的移動:程序使用一個定時器控制蛇身的移動。對於蛇身的移動來講,蛇頭受移動方向的控制,當移動方向爲向左時,表明蛇頭的點的X座標減1;當移動方向爲向右時,表明蛇頭的點的X座標加1;當移動方向爲向上時,表明蛇頭的點的Y座標減1;當移動方向爲向下時,表明蛇頭的點的Y座標加1。除此以外,蛇身的其餘點老是去「追逐」前一個點,也就是將第i個點的座標設置爲第i+1個點的座標。ide
蛇的方向改變:經過輕掃手勢處理器檢測用戶的輕掃手勢,根據輕掃手勢的方向來改變蛇的移動方向。ui
建立一個SingleView Application,該應用將會包含一個應用程序委託類和一個視圖控制器類,但程序還須要一個自定義UIView控件類,該控件類負責繪製貪食蛇的遊戲界面。atom
下面是自定義的FKSnakeView類(該類繼承了UIView)的接口代碼。spa
程序清單:codes/01/1.3/Snake/Snake/FKSnakeView.hcode
// 記錄地圖上的寬和高有多少個格子blog
#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 p_w_picpathNamed:@"cherry.png"];
// 加載遊戲背景圖片,並將背景圖片轉換爲平鋪形式的顏色
bgColor = [UIColor colorWithPatternImage:
[UIImage p_w_picpathNamed:@"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個點的座標。
該自定義類還在①號粗體字代碼處啓動了定時器,該定時器會控制程序每隔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所示的遊戲界面。
圖1.7 貪食蛇遊戲界面