十月长假也就这么过去了,利用假期想磨练一下自己的MVC架构的思想,于是找了一个相对比较麻烦的Demo来锻炼下,在之前很喜欢植物大战僵尸这个游戏,也算为游戏致敬了,但是这个植物大战僵尸肯定不是之前玩过的那个游戏,只是致敬,类似打地鼠,但是代码感觉非常多,所以也算楼主的目前博客里面最有料的了,不得不说,想逻辑的时候直接的虐心啊,先看一下效果吧
然后看一下如果家中的僵尸达到60个,那么就会跳出一个失败的动画,并伴随着僵尸的奸笑声,这里就不给动态图了
要用MVC架构,逻辑最重要,在写代码的时候明显的感觉的到,没有逻辑步步为艰,并且还会出现写不下去的情况,首先看看以我个人见解建构的MVC目录
Model类:
Zombie(僵尸类): 考虑到以后会更加僵尸种类,所以先创建了一个BaseZombie(僵尸的基类),拥有各种僵尸都有的属性,因为这个小游戏中有四种类型的僵尸,所以上面的图可以看到,有4种类型的僵尸。这里是先理理思路,代码后面会有的。
Manager(管理器):单例
ZomibieManager(僵尸管理器):顾名思义,里面存的是僵尸的对象
MusicManager(背景音乐管理器):能够控制音乐的播放
View类:
BackgroundView(背景视图):继承于UIView,是负责显示背景,也就是绿草坪,标签上的僵尸数以及得分,会根据得分以及僵尸数,分别向VC汇报是否结束游戏,什么时候提高僵尸移动加速
ZombieView(单个僵尸视图):继承于UIImageView,是负责每个僵尸的显示,接收点击事件回调以及动画的形成
Zombies_View(所有僵尸视图):继承于UIView,负责僵尸被点击后的操作、将是进入家中的回调以及负责加快僵尸移动速度
GameOverView(游戏结束视图):继承于UIView,负责展示游戏结束的视图,并能够将重新开始按钮点击实现回调给VC
Controller类:
全局的一个boss,负责指挥所有的子控件,并实现最终的回调
大体的逻辑就如上了,只挂图和讲理论不适合我,还是上代码,会更直接简明一些,那就按照MVC的顺序,注:本demon中所有的汇报回调都是Block回调
全局用的宏
#ifndef ____DEFINE_h
#define ____DEFINE_h
#define GAMEOVERCOUNT 60 //家里的僵尸达到60,游戏结束
#define ADD_SPEED_STAND 20 //加速标准,达到20即加速
#define ZOMBIE_MOVE_SPEED 0.5 //僵尸的移动速度
#define TIMER_SPEED 0.05 //计时器回调的间隔
#define ZOMBIE_COUNT 15 //每轮僵尸的个数
#define ZOMBIE_TYPE_COUNT 4 //僵尸的种类
#define ZOMBIE_ADD_SPEED 0.1 //每次加速时增量
#endif
Model(模型)类
Zombie(僵尸类)
//
// BaseZombie.h
// 打僵尸
//
// Created by YueWen on 15/10/5.
// Copyright (c) 2015年 YueWen. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface BaseZombie : NSObject
@property(nonatomic,assign)NSInteger zombiesFrameCount;//僵尸图片的张数
@property(nonatomic,assign)NSInteger zombiesFrameWidth;//僵尸图片的宽度
@property(nonatomic,assign)NSInteger zombiesFrameHeight;//僵尸图片的高度
@end
不需要实现任何的方法,其他四种类型的model类中,也只需重写init方法即可,这里只给出zombieType1(僵尸类型1)的init方法,其他的一样,只需记住图片的数量,以及高和宽即可。
- (instancetype)init
{
self = [super init];
if (self) {
//为框架赋值
self.zombiesFrameHeight = 72;
self.zombiesFrameWidth = 83;
//图片的数量
self.zombiesFrameCount = 31;
}
return self;
}
ZombiesManager(僵尸对象管理器)
/**
* 获取单例对象
*
* @return 单例对象
*/
+(instancetype)shareZombieManager;
因为要存储僵尸对象,但是又不希望外界能够轻易的改动,那么设置一个开放的数组属性,只能读,即readOnly
/**
* 存放僵尸对象的数组
*/
@property(nonatomic,strong,readonly)NSArray * zombies;
/**
* 加载僵尸
*/
-(void)loadZombies;
这个Manager需要一个回调,是告知VC,他的僵尸数组已经加载完毕,并且返回加载完毕的数组,并用该数组完成下一步的操作
typedef void(^ZombieManagerLoadFinishBlock)(NSArray * zombies);
有回调比有回调属性,有回调属性必然会有设置回调属性的方法
/**
* 为回调的代码块赋值
*
* @param zombieMangerLoadFinishBlock 回调的代码块
*/
-(void)setZombieManagerLoadFinishHandleBlock:(ZombieManagerLoadFinishBlock)zombieMangerLoadFinishBlock;
声明文件完毕,接下来就是实现文件了,延展中的代码块属性不赘余,但是延展中需要一个存放对象的可变数组
@property(nonatomic,strong)NSMutableArray * zombies_m;
-(instancetype)init
{
if (self = [super init])
{
//初始化数组
self.zombies_m = [NSMutableArray array];
}
return self;
}
+(instancetype)shareZombieManager
{
static ZombieManager * zombieManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
zombieManager = [[ZombieManager alloc]init];
});
return zombieManager;
}
加载数组的属性,这里唯一一个小心的地方就是每次加载前需要清空数组,不然会因为单例属性而出现对象数量的累加
/**
* 加载僵尸数组
*/
-(void)loadZombies
{
//开始之前清除数组
[self.zombies_m removeAllObjects];
//开始循环加载僵尸
for (NSInteger i = 0; i < ZOMBIE_COUNT; i++)
{
[self addZombie];
}
}
添加僵尸,貌似只是简单的一句话,但是添加僵尸是随机添加的,不能规定那种僵尸是几个,所以有一个随机返回僵尸对象的方法
/**
* 随机返回僵尸对象
*
* @return 增加的僵尸
*/
-(BaseZombie *)arcdToModeZombie
{
NSInteger temp = arc4random() % ZOMBIE_TYPE_COUNT + 1;//随机数 1~4,并根据数组返回僵尸类型
switch (temp) {
case 1:
return [[ZombieType1 alloc]init];
break;
case 2:
return [[ZombieType2 alloc]init];
break;
case 3:
return [[ZombieType3 alloc]init];
break;
case 4:
return [[ZombieType4 alloc]init];
break;
default:
break;
}
return nil;
}
那么添加僵尸的方法就很简单了,只需给数组添加一个对象即可
//增加一个僵尸
-(void)addZombie
{
[self.zombies_m addObject:[self arcdToModeZombie]];
}