【300行小游戏】一、推箱子
最近略闲。本来学习学习cocos2dx,u3d神马的。但是为人太懒了,于是嚼了嚼老知识。写了点所谓的游戏自娱自乐。纯OC代码,仅仅实现了游戏主要内容。每个游戏限制在300行代码内(主要是为了代码可读性,不然可以再压缩一半)。
不多说。
一、搬箱子。
开始之前,我们要确定到底要做什么?做到什么程度?
1、搬箱子必须的元素:小人,箱子,目标
2、成功条件:目标全部被箱子占领
3、失败条件:不设置
4、具体细节,地图的大小(正矩形不超过9*9),小人的移动(可以往前进方向推动箱子以及穿越目标),箱子的移动(靠边不能移动),关卡的设置(第一关一个箱子,每关递增一个)等
开始。(笔者再次声明:本人没做过游戏,这个只是写来自己玩的,如有误导,纯属坑爹)
先贴出笔者自定义的常量和枚举类型,看面看不懂的可以回过头看看
#define kCount_Grid 6 //地图大小-小于10 #define kTotalRound 6 //关卡数 #define kHero [UIColor yellowColor] #define kBox [UIColor blackColor] #define kDragon [UIColor redColor] #define kNormal [UIColor brownColor] typedef NS_ENUM(NSInteger, GridType) { GridTypeNormal = 0, GridTypeBox = 1, GridTypeHero = 2, GridTypeDragon = 3, }; typedef NS_ENUM(NSInteger, MoveDirection) { MoveDirectionUp = 0, MoveDirectionDown = 1, MoveDirectionLeft = 2, MoveDirectionRight = 3, };
首先,我们来绘制我们的游戏地图
-(void)initMap { CGRect frame = self.view.frame; CGFloat width_Grid = frame.size.width/kCount_Grid; vMap = [[UIView alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.width)]; [vMap setCenter:CGPointMake(frame.size.width/2, frame.size.height/2)]; [self.view addSubview:vMap]; for (int i=1; i<kCount_Grid+1; i++) { for (int j=1; j<kCount_Grid+1; j++) { UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake((i-1)*width_Grid, (j-1)*width_Grid, width_Grid, width_Grid)]; btn.layer.borderWidth = 1; btn.layer.borderColor = [[UIColor lightGrayColor]CGColor]; [btn setBackgroundColor:[UIColor brownColor]]; [btn setTag:i+j*10]; [vMap addSubview:btn]; } } }
如果是和笔者一样的菜鸟需注意一个细节,int i和int j是从1而非0开始的。
然后初始化小人,箱子,和目标的位置(笔者YY成Hero,box,和Dragon)
-(void)initHero { btnHero = [self randomPoint:GridTypeHero]; [btnHero setBackgroundColor:kHero]; [btnHero setSelected:YES]; } -(void)initBox { for (int i=0; i<round; i++) { UIButton *btn = [self randomPoint:GridTypeBox]; btn = [self checkRepeat:btn gridType:GridTypeBox]; [btn setBackgroundColor:kBox]; [btn setSelected:YES]; [array_Box addObject:btn]; } } -(void)initDragon { for (int i=0; i<round; i++) { UIButton *btn = [self randomPoint:GridTypeDragon]; btn = [self checkRepeat:btn gridType:GridTypeDragon]; [btn setBackgroundColor:kDragon]; [btn setSelected:YES]; [array_Dragon addObject:btn]; } }
位置的初始化笔者采用的随机位置,因为懒得画地图
-(UIButton *)randomPoint:(GridType)type { int x = 1; int y = 1; if (type == GridTypeBox) { x = arc4random()%(kCount_Grid-2); y = arc4random()%(kCount_Grid-2); x++; y++; }else{ x = arc4random()%kCount_Grid; y = arc4random()%kCount_Grid; } x++; y++; UIButton *btn = (UIButton *)[vMap viewWithTag:(x+y*10)]; return btn; }
当然,得查一下重。不能一开始,所有的随机都随机到一个位置上去了是吧
此外,还应考虑到箱子的位置,不能初始化就靠再墙壁上。不然很容易就死局了。
-(UIButton *)checkRepeat:(UIButton*)btn gridType:(GridType)type { if ([btn isSelected] == YES) { btn = [self randomPoint:type]; return [self checkRepeat:btn gridType:type]; }else{ return btn; } }
为了界面的简单,我抛弃了摆方向控制键的想法,全部用手势搞定
-(void)initAction { UISwipeGestureRecognizer *recognizer; recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(goRight)]; [recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)]; [[self view] addGestureRecognizer:recognizer]; recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(goLeft)]; [recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)]; [[self view] addGestureRecognizer:recognizer]; recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(goUp)]; [recognizer setDirection:(UISwipeGestureRecognizerDirectionUp)]; [[self view] addGestureRecognizer:recognizer]; recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(goDown)]; [recognizer setDirection:(UISwipeGestureRecognizerDirectionDown)]; [[self view] addGestureRecognizer:recognizer]; }
这里暴露了笔者的菜鸟真相,顺便求一句,向之上的方法怎么传参啊?因为不知道如何传参,笔者多写了四个方法
-(void)goUp { [self moveAction:btnHero direction:MoveDirectionUp]; } -(void)goDown { [self moveAction:btnHero direction:MoveDirectionDown]; } -(void)goLeft { [self moveAction:btnHero direction:MoveDirectionLeft]; } -(void)goRight { [self moveAction:btnHero direction:MoveDirectionRight]; }
然后再调到真正想执行的方法
-(BOOL)moveAction:(UIButton *)btn direction:(MoveDirection)direction { int tag = btn.tag; int x = tag%10; int y = tag/10; switch (direction) { case MoveDirectionUp: { if (y<2) return NO; return [self moveItem:btn tag:(x+(y-1)*10) direction:MoveDirectionUp]; break; } case MoveDirectionDown: { if (y+1>kCount_Grid) return NO; return [self moveItem:btn tag:(x+(y+1)*10) direction:MoveDirectionDown]; break; } case MoveDirectionLeft: { if (x<2) return NO; return [self moveItem:btn tag:((x-1)+y*10) direction:MoveDirectionLeft]; break; } case MoveDirectionRight: { if (x+1>kCount_Grid) return NO; return [self moveItem:btn tag:((x+1)+y*10) direction:MoveDirectionRight]; break; } } return NO; }
稍微解释一下,方格游戏的移动,其实就是坐标的+1-1,在这里笔者偷懒使用tag作为坐标,个位数表示x轴,十位数表示y轴。所以地图大小不能超过9*9
继续,具体的移动方法
-(BOOL)moveItem:(UIButton *)btn tag:(int)tag direction:(MoveDirection)direction { UIButton *btnTarget = (UIButton*)[vMap viewWithTag:tag]; if (btn == btnHero) { //移动英雄 if ([btnTarget isSelected] == YES) { if ([array_Box containsObject:btnTarget]) { if ([self moveAction:btnTarget direction:direction] == NO) { return NO; } } } [btnHero setBackgroundColor:kNormal]; [btnHero setSelected:NO]; if ([array_Dragon containsObject:btnHero]) { [btnHero setBackgroundColor:kDragon]; [btnHero setSelected:YES]; } btnHero = btnTarget; [btnHero setBackgroundColor:kHero]; [btnHero setSelected:YES]; }else{ //移动箱子 [btn setBackgroundColor:kNormal]; [btn setSelected:NO]; [array_Box removeObject:btn]; if ([array_Dragon containsObject:btn]) { count_Cover--; } [btnTarget setBackgroundColor:kBox]; [btnTarget setSelected:YES]; [array_Box addObject:btnTarget]; if ([array_Dragon containsObject:btnTarget]) { count_Cover++; } if (count_Cover == round) { if (round >= kTotalRound) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"通过所有关卡" message:nil delegate:self cancelButtonTitle:@"再玩一遍" otherButtonTitles:nil]; alert.tag = 100; [alert show]; }else{ UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"过关" message:nil delegate:self cancelButtonTitle:@"进入下一关" otherButtonTitles:nil]; alert.tag = 200; [alert show]; } } } return YES; }
一切为了简单,所以笔者有些地方就纯粹用逻辑堆出来。
于是呢、第一个小游戏基本上就竣工了……当然还存在些许BUG,比如从第四管开始,有可能会出现四个箱子随机恰好挨着组成一个大正方形,然后直接死局。不过随意吧,这只怪玩家人品不好了,不是吗?
完整代码下载:Demo