OC版貪吃蛇,oc貪吃蛇
昨天寫了一個js版貪吃蛇,今天突然想寫一個OC版的,來對比一下兩種語言的區別
oc版功能,適配所有尺寸iphone,可暫停,可設定地圖和蛇的比例,可加速
對比一下會發現js版的相對OC版的會簡單一些,有想看js版的可以看我上一篇隨筆
程式中沒用到任何素材,如下:
github源碼地址:https://github.com/masterChunlinHan/snake_OC
下面開始,跟js版一樣,為了方便學習,所有代碼都寫在一個controller中,所以標頭檔中什麼也不用寫
#import <UIKit/UIKit.h>@interface ViewController : UIViewController@end
1,首先看需要定義的宏,和需要的全域變數,各個變數的含義都已經寫在注釋中
#import "ViewController.h"#define SCREENWIDTH [UIScreen mainScreen].bounds.size.width//螢幕的高度#define SCREENHEIGHT [UIScreen mainScreen].bounds.size.height//螢幕寬度#define LINECOUNT 30 //地圖上可顯示的行數,這裡規定地圖是正方形,所以只需要一個數#define BOXWIDTH SCREENHEIGHT/LINECOUNT //每格的寬度,既是食物的寬度也是每節蛇身的寬度@interface ViewController () @property(nonatomic,strong)UIView *map;//地圖只有一個,定義成全域的 @property(nonatomic,strong)UIView *food;//食物也只有一個 @property(nonatomic,assign)NSInteger foodx;//食物的x座標,以格子的寬度為單位 @property(nonatomic,assign)NSInteger foody;//食物的y座標 @property(nonatomic,strong)NSMutableArray *body;//蛇的身體是變化的,所以用一個可變數組來存 @property(nonatomic,strong)NSTimer *timer;//定時器 @property(nonatomic,assign)NSInteger directionCode;//定義一個數字來代表方向0,1,2,3分別代表上下左右 @property(nonatomic,assign)float time;//定時器的時間間隔,預設為0.5秒走一步@end
2,一些初始化工作
@implementation ViewController//初始化定時器的間隔-(float)time{ if(!_time){ _time=0.5; } return _time;}//viewDidLoad中建立一些不會變的控制項,這些控制項始終只有一份,不需要建立第二次- (void)viewDidLoad { [super viewDidLoad]; //建立地圖 [self createMap]; //添加重設按鈕,預設進入程式不會立刻開始遊戲,需要點擊這個按鈕才開始 [self createReloadButton]; //添加開始暫停按鈕 [self createControlButtons]; //添加方向鍵 [self createDirectionButtons];}
3,建立地圖
-(void)createMap{ //先判斷地圖是否已經存在,如果存在就不再建立 if(!self.map){ self.map = [[UIView alloc]initWithFrame:CGRectMake(0,0, SCREENHEIGHT, SCREENHEIGHT)]; [self.view addSubview:self.map]; UIColor *lightColor = [UIColor colorWithRed:238/255.0 green:238/255.0 blue:238/255.0 alpha:1]; self.map.backgroundColor =lightColor; self.map.center = self.view.center; }}
4,建立食物
//建立食物,每次重新整理時都會調用這個方法-(void)createFood{ //先判斷食物是否已經存在,如果存在就不再建立 if(!self.food){ self.food = [[UIView alloc]initWithFrame:CGRectMake(0,0, BOXWIDTH, BOXWIDTH)]; UIColor *foodColor = [UIColor redColor]; self.food.backgroundColor = foodColor; [self.map addSubview:self.food]; } //每次重新整理食物的座標都是一個隨機值 self.foodx = arc4random()%LINECOUNT; self.foody = arc4random()%LINECOUNT; self.food.frame = CGRectMake(self.foodx*BOXWIDTH, self.foody*BOXWIDTH, BOXWIDTH, BOXWIDTH);}
5,建立蛇
//建立蛇,每次重新整理時先讓蛇的身體座標發生改變,再調用這個方法,讓蛇重新顯示-(void)createSnake{ //先判斷食物是否已經存在,如果存在就不再建立 if(!self.body){ //初始化蛇前進的方向為向右 self.directionCode = 3; //初始化蛇的身體,開始時只有一個頭,兩節身體,頭為紅色,身體為藍色 self.body = [NSMutableArray arrayWithArray:@[ @{@"bodyx":@"2",@"bodyy":@"2",@"color":[UIColor redColor]}, @{@"bodyx":@"1",@"bodyy":@"2",@"color":[UIColor blueColor]}, @{@"bodyx":@"0",@"bodyy":@"2",@"color":[UIColor blueColor]} ]]; } for (int i=0; i<self.body.count; i++) { NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithDictionary:self.body[i]]; UIView *body = mDic[@"body"]; if(!body){ body = [[UIView alloc]initWithFrame:CGRectMake(0, 0, BOXWIDTH, BOXWIDTH)]; [self.map addSubview:body]; [mDic setObject:body forKey:@"body"]; } float bodyx =BOXWIDTH*[mDic[@"bodyx"] intValue]; float bodyy =BOXWIDTH*[mDic[@"bodyy"] intValue]; body.frame = CGRectMake(bodyx, bodyy, BOXWIDTH, BOXWIDTH); body.backgroundColor = mDic[@"color"]; self.body[i] = mDic; }}
6,蛇移動的方法
//蛇移動,每次重新整理時會在這個方法裡改變蛇的座標,並計算與食物的碰撞和自身的碰撞-(void)snakeMove{ //設定蛇身 for(NSInteger i=self.body.count-1;i>0;i--){ NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithDictionary:self.body[i]]; NSString *bodyx = self.body[i-1][@"bodyx"]; NSString *bodyy = self.body[i-1][@"bodyy"]; [mDic setObject:bodyx forKey:@"bodyx"]; [mDic setObject:bodyy forKey:@"bodyy"]; self.body[i] = mDic; } //設定蛇頭 NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithDictionary:self.body[0]]; NSString *headx=mDic[@"bodyx"] ; NSString *heady=mDic[@"bodyy"]; if(self.directionCode==0){//上 heady = [NSString stringWithFormat:@"%d",[heady intValue]-1]; }else if (self.directionCode==1){//下 heady = [NSString stringWithFormat:@"%d",[heady intValue]+1]; }else if (self.directionCode==2){//左 headx = [NSString stringWithFormat:@"%d",[headx intValue]-1]; }else if (self.directionCode==3){//右 headx = [NSString stringWithFormat:@"%d",[headx intValue]+1]; } [mDic setObject:headx forKey:@"bodyx"]; [mDic setObject:heady forKey:@"bodyy"]; self.body[0] = mDic; //判斷是否吃到食物 if([headx integerValue]==self.foodx&&[heady integerValue]==self.foody){ [self.body addObject:@{@"bodyx":@"-1",@"bodyy":@"0",@"color":[UIColor blueColor]}]; if(self.time>0.25){ self.time-=0.05; [self start];// [self createTimer]; } [self createFood]; } //判斷是否撞到邊界 if([headx integerValue]<0||[headx integerValue]>LINECOUNT-1||[heady intValue]<0||[heady intValue]>LINECOUNT-1){// [self reload]; [self stop]; [self gameOver]; return; } //判斷是否撞到自己 [self createSnake];}
7,建立計時器,因為建立計時器的工作和暫停之後繼續遊戲的方法功能一樣,所以直接調用繼續遊戲的方法
//建立定時器-(void)createTimer{ [self start];}
繼續遊戲
//繼續,這裡建立定時器之前先銷毀舊的計時器可以保證始終只有一個計時器在工作-(void)start{ //如果不先銷毀定時器,不斷點擊繼續按鈕會建立很多個定時器,會出現蛇跑很快的bug, [self.timer invalidate]; self.timer = [NSTimer timerWithTimeInterval:self.time repeats:YES block:^(NSTimer * _Nonnull timer) { [self snakeMove]; }]; [[NSRunLoop currentRunLoop]addTimer:self.timer forMode:NSDefaultRunLoopMode];}
8,暫停遊戲,只要將計時器銷毀就行了
//暫停-(void)stop{ [self.timer invalidate]; }
9,設定重設按鈕方法,每一次執行(包括第一次)都會清空地圖再建立蛇、食物、並重設計時器,所以第一次點擊相當於開始遊戲
//reload方法,執行之後一切重設,移除地圖上的所有東西,並重新建立-(void)reload{ self.body = nil; for(UIView *subview in self.map.subviews){ if(subview!=self.food){ [subview removeFromSuperview]; } } [self createFood]; [self createSnake]; [self createTimer];}//建立重設按鈕-(void)createReloadButton{ UIButton *reloadBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, (SCREENWIDTH-SCREENHEIGHT)/2, SCREENHEIGHT/3)]; [self.view addSubview:reloadBtn]; [reloadBtn setTitle:@"重設/開始" forState:UIControlStateNormal]; reloadBtn.backgroundColor = [UIColor orangeColor]; [reloadBtn addTarget:self action:@selector(reload) forControlEvents:UIControlEventTouchUpInside];}
10,建立各種按鍵,在地圖建立好之後就可以調用,因為有些按鈕要相對於地圖的位置來放置
//下面是建立各種按鈕,就不多說了//上下左右按鍵-(void)createDirectionButtons{// float btnW = (SCREENWIDTH-SCREENHEIGHT)/2; //左邊的方向鍵view UIView *leftDirectionView = [[UIView alloc]initWithFrame:CGRectMake(0, SCREENHEIGHT/3, (SCREENWIDTH-SCREENHEIGHT)/2, 2*SCREENHEIGHT/3)]; [self.view addSubview:leftDirectionView]; //右邊的方向鍵view UIView *rightDirectionView = [[UIView alloc]initWithFrame:CGRectMake(CGRectGetMaxX(self.map.frame), SCREENHEIGHT/3, (SCREENWIDTH-SCREENHEIGHT)/2, 2*SCREENHEIGHT/3)]; [self.view addSubview:rightDirectionView]; float btnH = 2*SCREENHEIGHT/9; for (int i=0; i<4; i++) { NSString *title; float x; float y; float w; UIColor *backColor; switch (i) { case 0: x = 0; y = 0; w = (SCREENWIDTH-SCREENHEIGHT)/2; title = @"上"; backColor = [UIColor redColor]; break; case 1: x = 0; y = 2*btnH; w = (SCREENWIDTH-SCREENHEIGHT)/2; title = @"下"; backColor = [UIColor greenColor]; break; case 2: x = 0; y = btnH; w = (SCREENWIDTH-SCREENHEIGHT)/4; title = @"左"; backColor = [UIColor grayColor]; break; default: x= (SCREENWIDTH-SCREENHEIGHT)/4; y=btnH; w = (SCREENWIDTH-SCREENHEIGHT)/4; title=@"右"; backColor = [UIColor orangeColor]; break; } //左邊的按鈕 UIButton*btnLeft = [[UIButton alloc]initWithFrame:CGRectMake(x, y, w, btnH)]; btnLeft.tag = i; [btnLeft setBackgroundColor:backColor]; [btnLeft setTitle:title forState:UIControlStateNormal]; [btnLeft addTarget:self action:@selector(directionChanged:) forControlEvents:UIControlEventTouchUpInside]; //右邊的按鈕 UIButton*btnRight = [[UIButton alloc]initWithFrame:CGRectMake(x, y, w, btnH)]; btnRight.tag = i; [btnRight setBackgroundColor:backColor]; [btnRight setTitle:title forState:UIControlStateNormal]; [btnRight addTarget:self action:@selector(directionChanged:) forControlEvents:UIControlEventTouchUpInside]; [leftDirectionView addSubview:btnLeft]; [rightDirectionView addSubview:btnRight];// [self.view addSubview:btn]; }}//點擊方向按鈕後讓代表方向的全域變數改變,這樣在蛇移動的方法中才能擷取到-(void)directionChanged:(UIButton*)btn{ self.directionCode = btn.tag; // NSLog(@"%zd",btn.tag);}@end