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(,, SCREENHEIGHT, SCREENHEIGHT)];
[self.view addSubview:self.map];
UIColor *lightColor = [UIColor colorWithRed:/255.0 green:/255.0 blue:/255.0 alpha:];
self.map.backgroundColor =lightColor;
self.map.center = self.view.center;
}
}
4,创建食物
//创建食物,每次刷新时都会调用这个方法
-(void)createFood{
//先判断食物是否已经存在,如果存在就不再创建
if(!self.food){
self.food = [[UIView alloc]initWithFrame:CGRectMake(,, 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 = ;
//初始化蛇的身体,开始时只有一个头,两节身体,头为红色,身体为蓝色
self.body = [NSMutableArray arrayWithArray:@[
@{@"bodyx":@"",@"bodyy":@"",@"color":[UIColor redColor]},
@{@"bodyx":@"",@"bodyy":@"",@"color":[UIColor blueColor]},
@{@"bodyx":@"",@"bodyy":@"",@"color":[UIColor blueColor]}
]];
}
for (int i=; i<self.body.count; i++) {
NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithDictionary:self.body[i]];
UIView *body = mDic[@"body"];
if(!body){
body = [[UIView alloc]initWithFrame:CGRectMake(, , 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-;i>;i--){
NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithDictionary:self.body[i]];
NSString *bodyx = self.body[i-][@"bodyx"];
NSString *bodyy = self.body[i-][@"bodyy"];
[mDic setObject:bodyx forKey:@"bodyx"];
[mDic setObject:bodyy forKey:@"bodyy"];
self.body[i] = mDic;
}
//设置蛇头
NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithDictionary:self.body[]];
NSString *headx=mDic[@"bodyx"] ;
NSString *heady=mDic[@"bodyy"];
if(self.directionCode==){//上
heady = [NSString stringWithFormat:@"%d",[heady intValue]-];
}else if (self.directionCode==){//下
heady = [NSString stringWithFormat:@"%d",[heady intValue]+];
}else if (self.directionCode==){//左
headx = [NSString stringWithFormat:@"%d",[headx intValue]-];
}else if (self.directionCode==){//右
headx = [NSString stringWithFormat:@"%d",[headx intValue]+];
}
[mDic setObject:headx forKey:@"bodyx"];
[mDic setObject:heady forKey:@"bodyy"];
self.body[] = mDic;
//判断是否吃到食物
if([headx integerValue]==self.foodx&&[heady integerValue]==self.foody){
[self.body addObject:@{@"bodyx":@"-1",@"bodyy":@"",@"color":[UIColor blueColor]}];
if(self.time>0.25){
self.time-=0.05;
[self start];
// [self createTimer];
} [self createFood];
}
//判断是否撞到边界
if([headx integerValue]<||[headx integerValue]>LINECOUNT-||[heady intValue]<||[heady intValue]>LINECOUNT-){
// [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(, , (SCREENWIDTH-SCREENHEIGHT)/, SCREENHEIGHT/)];
[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(, SCREENHEIGHT/, (SCREENWIDTH-SCREENHEIGHT)/, *SCREENHEIGHT/)];
[self.view addSubview:leftDirectionView];
//右边的方向键view
UIView *rightDirectionView = [[UIView alloc]initWithFrame:CGRectMake(CGRectGetMaxX(self.map.frame), SCREENHEIGHT/, (SCREENWIDTH-SCREENHEIGHT)/, *SCREENHEIGHT/)];
[self.view addSubview:rightDirectionView]; float btnH = *SCREENHEIGHT/;
for (int i=; i<; i++) {
NSString *title;
float x;
float y;
float w;
UIColor *backColor;
switch (i) {
case :
x = ;
y = ;
w = (SCREENWIDTH-SCREENHEIGHT)/;
title = @"上";
backColor = [UIColor redColor];
break;
case :
x = ;
y = *btnH;
w = (SCREENWIDTH-SCREENHEIGHT)/;
title = @"下";
backColor = [UIColor greenColor];
break;
case :
x = ;
y = btnH;
w = (SCREENWIDTH-SCREENHEIGHT)/;
title = @"左";
backColor = [UIColor grayColor];
break;
default:
x= (SCREENWIDTH-SCREENHEIGHT)/;
y=btnH;
w = (SCREENWIDTH-SCREENHEIGHT)/;
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
OC版贪吃蛇的更多相关文章
- TOJ 3973 Maze Again && TOJ 3128 简单版贪吃蛇
TOJ3973传送门:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=3973 时间限制(普通 ...
- 如何用python制作贪吃蛇以及AI版贪吃蛇
用python制作普通贪吃蛇 哈喽,大家不知道是上午好还是中午好还是下午好还是晚上好! 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很 ...
- js版贪吃蛇
之前没有写博客的习惯,这是我的第一个博客,有些的不好的地方,希望大家多多提意见 js版的贪吃蛇相对比较简单,废话不多说直接上代码,有需要注意的地方我会标红,github源码地址https://gith ...
- JavaScript 实现简易版贪吃蛇(Day_13)
时光永远在变迁,你始终要丢下过去. 使用语言 JavaScript 概述 运用JavaScript 实现简易版<贪吃蛇>. Html 页面 1 <!DOCTYPE htm ...
- Javascript基础示例:用JS写简易版贪吃蛇(面向对象)
废话不多说,代码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> & ...
- JavaScript版—贪吃蛇小组件
最近在学习JavaScript,利用2周的时间看完了<JavaScript高级编程>,了解了Js是一门面向原型编程的语言,没有像C#语言中的class,也没有私有.公有.保护等访问限制的级 ...
- Winfrom 极简版贪吃蛇源码
该源码是我在百度知识库借助前辈的的经验,加上自己的一点小改动写的一个非常简陋的贪吃蛇小程序.如果你们有更好的改动方案,欢迎评论. 进入主题吧! 1.创建一个桌面应运程序,拖一个定时器控件.这样,程序界 ...
- 用GUI实现java版贪吃蛇小游戏
项目结构 新建一个JFrame窗口,作为程序入口 public class GameStart{ public static void main(String[] args) { JFrame jFr ...
- c语言版贪吃蛇小游戏
编译环境:windows 7 64位 编译工具:codeblocks 13.12 备注:未使用graphics.h 声明:个人原创,未经允许,禁止转载!!! 数据结构:双向链表 1.程序未使用grap ...
随机推荐
- angularJS 源码阅读之一:toDebugString
简介: 这个函数返回调试字符串: number,boolean,string,null,undefined,都会转为字符串. function 中括号前面有空格的,会去除函数体,没空格的,会输出函数的 ...
- Linux下进行硬盘挂载、分区、删除分区,格式化,卸载方法
本文简单介绍了下文件系统及其操作(df命令),磁盘分区.格式化,还有最主要是挂载操作. 在这里对"挂载"做个说明,我们都知道文件系统是创建在磁盘上面的,每个文件系统都有独立的ino ...
- 谈谈tableView的重要属性内边距
全屏穿透效果需要做到两点 tableView的可视范围占据整个父控件(或者屏幕)--设置contentsize滚动范围. 所有的cell都可以被看到,也就是说tableView中的cell不会被导航栏 ...
- 算法设计与分析 上机题Mergesort
#include <iostream>using namespace std; #define N 100 int g_array[N]; //存放输入的数字static int ...
- 在ASP.NET MVC中利用Aspose.cells 将查询出的数据导出为excel,并在浏览器中下载。
正题前的唠叨 本人是才出来工作不久的小白菜一颗,技术很一般,总是会有遇到一些很简单的问题却不知道怎么做,这些问题可能是之前解决过的.发现这个问题,想着提升一下自己的技术水平,将一些学的新的'好'东西记 ...
- jmeter连接配置带跳板机(SSH)的mysql服务器
jmeter连接配置mysql服务器时,如果数据库服务器没有通过ssh连接,则只需要配置相应的jdbc参数就可以了,即请求域名或ip地址:3306,如果数据库服务器是通过SSH连接的,那需要通过中间远 ...
- Threads(线程)(二)
前一章我们提到了同步异步,多线程:在开始今天的文章之前,先来总结一下上一篇文章的内容,多线程的优点. 多线程有哪些优点呢,在这里通过代码依次来总结. 异步多线程的三大特点 1)同步方法卡界面,原因是主 ...
- Java并发包基石-AQS详解
目录 1 基本实现原理 1.1 如何使用 1.2 设计思想 2 自定义同步器 2.1 同步器代码实现 2.2 同步器代码测试 3 源码分析 3.1 Node结点 3.2 独占式 3.3 共享式 4 总 ...
- Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口
Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器 ...
- OPNET中FIN,FOUT以及FRET的作用 分类: opnet 2014-05-12 16:07 144人阅读 评论(0) 收藏
为了使一个用户定义的函数被执行,该函数必须与一个特殊的堆栈跟踪代码相连.堆栈跟踪技术靠在函数的入口点和出口点插入预处理器宏指令完成(一个函数只有一个入口点,但可以有多个出口点(由C语言的return声 ...