前言
最近寫(xiě)了一款釣魚(yú)小游戲,自己平時(shí)也沒(méi)做過(guò)游戲钦购,本來(lái)以為這種游戲要用cocos2d什么的實(shí)現(xiàn)戴陡,后來(lái)發(fā)現(xiàn)其實(shí)動(dòng)畫(huà)就可以實(shí)現(xiàn)很棒的效果,先看看效果圖命辖。
思維導(dǎo)圖
首先我們看下思維導(dǎo)圖况毅,本游戲主要分為4大塊分蓖,其中魚(yú)的實(shí)現(xiàn)最為復(fù)雜
項(xiàng)目結(jié)構(gòu)
準(zhǔn)備工作
首先將需要的圖準(zhǔn)備好,這個(gè)魚(yú)其實(shí)就是一組圖片尔许,圖片大小固定么鹤,每一幀位置變化,所以看起來(lái) 是一個(gè)上下游動(dòng)的魚(yú)母债。
魚(yú)鉤模塊
- 擺動(dòng)動(dòng)畫(huà)
魚(yú)鉤的擺動(dòng)范圍是[M_PI/4.0,-M_PI/4.0] (垂直向下為0度午磁,順時(shí)針為正),這里利用了計(jì)時(shí)器進(jìn)行角度的更改,計(jì)時(shí)器用的CADisplayLink毡们,它是一個(gè)和屏幕刷新率一致的定時(shí)器迅皇,如果沒(méi)有卡頓,每秒刷新次數(shù)是60次衙熔,本Demo很多計(jì)時(shí)器用的都是CADisplayLink登颓。下面是魚(yú)鉤的主要代碼(重點(diǎn):1、設(shè)置錨點(diǎn)后重置frame红氯,2框咙、更改角度,3痢甘、旋轉(zhuǎn))喇嘱。 其中定義了一個(gè)block將角度angle回傳到FishingView界面計(jì)算魚(yú)鉤落到池塘的位置。
@property (nonatomic, strong) CADisplayLink *linkTimer;
@property (nonatomic, assign) BOOL isReduce;//改變方向
@property (nonatomic, assign) CGFloat angle;//擺動(dòng)的角度
- (void)initView{
[self setAnchorPoint:CGPointMake(0.5, 0) forView:self];
UIImageView *gouImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.frame.size.height - 35 , 30, 35)];
gouImageView.image = [UIImage imageNamed:@"fish_catcher_tong"];
[self addSubview:gouImageView];
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake((self.frame.size.width - 3)/2.0, 0, 3, self.frame.size.height - 35)];
lineView.backgroundColor = HEXCOLOR(0x9e664a);
[self addSubview:lineView];
// 創(chuàng)建一個(gè)對(duì)象計(jì)時(shí)器
_linkTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(hookMove)];
//啟動(dòng)這個(gè)link
[_linkTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
//設(shè)置錨點(diǎn)后重新設(shè)置frame
- (void) setAnchorPoint:(CGPoint)anchorpoint forView:(UIView *)view{
CGRect oldFrame = view.frame;
view.layer.anchorPoint = anchorpoint;
view.frame = oldFrame;
}
#pragma mark - 魚(yú)鉤擺動(dòng)
- (void)hookMove{
if (self.isReduce){
_angle-=1.8*cos(1.5*_angle)*0.01;//計(jì)算角度,利用cos模擬上升過(guò)程中減慢塞栅,下降加快
if (_angle < -M_PI/180*45){
self.isReduce = NO;
}
}else {
_angle+=1.8*cos(1.5*_angle)*0.01;
if (_angle > M_PI/180*45){
self.isReduce = YES;
}
}
if (self.angleBlock){
self.angleBlock(_angle);
}
// DLog(@"魚(yú)鉤角度%f",_angle);
//旋轉(zhuǎn)動(dòng)畫(huà)
self.transform = CGAffineTransformMakeRotation(_angle);
}
魚(yú)模塊
魚(yú)模塊是繼承自UIImageView的一個(gè)類(lèi)
魚(yú)模塊提供了三種初始化方式者铜,可垂釣的魚(yú)、不可垂釣的魚(yú)(可以不用)放椰、釣到的魚(yú)三種魚(yú)作烟。
魚(yú)的移動(dòng)方式有兩種,使用枚舉定義砾医,從左到右拿撩,從右到左
魚(yú)的種類(lèi)有六種,用枚舉進(jìn)行了定義
typedef NS_ENUM(NSInteger, FishModelImageViewType){
FishModelImageViewTypeXHY = 0, //小黃魚(yú)
FishModelImageViewTypeSBY = 1, //石斑魚(yú)
FishModelImageViewTypeHSY = 2, //紅杉魚(yú)
FishModelImageViewTypeBWY = 3, //斑紋魚(yú)
FishModelImageViewTypeSHY = 4, //珊瑚魚(yú)
FishModelImageViewTypeSY = 5, //鯊魚(yú)
};
提供了一個(gè)釣到魚(yú)后的代理
FishModelImageViewDelegate
//魚(yú)的種類(lèi)-游動(dòng)方向-贏取金額
方法 - (void)catchTheFishWithType:(FishModelImageViewType)type
andDirection:(FishModelImageViewDirection)dir
andWinCount:(int)count;
- 1如蚜、動(dòng)態(tài)的魚(yú)
加載動(dòng)態(tài)魚(yú)的方法
//初始化UIImageView
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 55, 55)];
//如果圖片的名字是有順序的压恒,例如xhy1,xhy2,xhy3...,可以取去掉序號(hào)的名字,然后會(huì)自動(dòng)將所有的圖片都加載進(jìn)來(lái)错邦,duration是動(dòng)畫(huà)時(shí)長(zhǎng)
imageView.image = [UIImage animatedImageNamed:@"xhy" duration:1];
[self.view addSubview:imageView];
初始化不同的魚(yú)涎显,不同的魚(yú)大小不同,移動(dòng)的速度不同兴猩,所以動(dòng)畫(huà)時(shí)長(zhǎng)不一樣
//初始化小魚(yú) git動(dòng)畫(huà)時(shí)長(zhǎng)
- (void)initViewWithType:(FishModelImageViewType)type andDuration:(double)time{
self.fishType = type;
switch (type) {
case FishModelImageViewTypeXHY://小黃魚(yú)
self.duration = 6.0;
self.frame = CGRectMake(-100, 0, 35, 40); //魚(yú)的大小要定義好
self.image = [UIImage animatedImageNamed:@"xhy" duration:time];
break;
case FishModelImageViewTypeSBY://石斑魚(yú)
self.duration = 7.0;
self.frame = CGRectMake(-100, 0, 50, 50);
self.image = [UIImage animatedImageNamed:@"sby" duration:time];
break;
case FishModelImageViewTypeHSY://紅杉魚(yú)
self.duration = 8.0;
self.frame = CGRectMake(-100, 0, 50, 40);
self.image = [UIImage animatedImageNamed:@"hsy" duration:time];
break;
case FishModelImageViewTypeBWY://斑紋魚(yú)
self.duration = 8.5;
self.frame = CGRectMake(-100, 0, 65, 53);
self.image = [UIImage animatedImageNamed:@"bwy" duration:time];
break;
case FishModelImageViewTypeSHY://珊瑚魚(yú)
self.duration = 9.0;
self.frame = CGRectMake(-100, 0, 55, 55);
self.image = [UIImage animatedImageNamed:@"shy" duration:time];
break;
case FishModelImageViewTypeSY://鯊魚(yú)
self.duration = 11.0;
self.frame = CGRectMake(-200, 0, 145, 90);
self.image = [UIImage animatedImageNamed:@"sy" duration:time];
break;
}
}
- 2、移動(dòng)的魚(yú)
提供的圖片都是頭朝左的(見(jiàn)上面的動(dòng)圖)早歇,所以從左往右游的話(huà)圖片需要進(jìn)行鏡像反轉(zhuǎn)
對(duì)于魚(yú)是否可以垂釣是用通知進(jìn)行傳遞信息的倾芝,可垂釣讨勤、不可垂釣兩種狀態(tài)
可垂釣:魚(yú)鉤沉到魚(yú)塘?xí)r受到垂釣通知(將魚(yú)鉤底部的坐標(biāo)傳過(guò)來(lái)),現(xiàn)在魚(yú)可以垂釣晨另,當(dāng)根據(jù)上鉤概率等因素判斷魚(yú)上鉤后潭千,對(duì)魚(yú)進(jìn)行旋轉(zhuǎn),然后執(zhí)行上鉤動(dòng)畫(huà)借尿。動(dòng)畫(huà)結(jié)束后執(zhí)行代理刨晴。
//初始化可以垂釣的魚(yú)
- (instancetype)initCanCatchFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir{
if (self = [super init]){
self.direction = dir;
[self initViewWithType:type andDuration:1];
if (dir == FishModelImageViewFromLeft){//從左往右,默認(rèn)所有的魚(yú)都是從右往左
self.transform = CGAffineTransformMakeScale(-1, 1); //鏡像
}
[self initFishView];
}
return self;
}
#pragma mark - 可以垂釣的魚(yú)(計(jì)時(shí)器)
- (void)initFishView{
//接收可以垂釣的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationCanCatch:) name:NotificationFishHookStop object:nil];
//接收不可垂釣的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationCannotCatch) name:NotificationFishHookMove object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeTimer) name:NotificationRemoveFishModelTimer object:nil];
//創(chuàng)建計(jì)時(shí)器
_linkTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(fishMove)];
//啟動(dòng)這個(gè)link(加入到線程池)
[_linkTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
_offsetX = ScreenWidth;
_offsetY = 100;
_fishWidth = self.frame.size.width;
//Y可變高度范圍
_randomRange = (int) (YuTangHeight - self.frame.size.height - OffSetYRange);
self.speed = (ScreenWidth + _fishWidth)/self.duration;//游動(dòng)速度
self.changeX = self.speed/60.0;//計(jì)時(shí)器每秒60次
DLog(@"魚(yú)游動(dòng)的速度:%f,每次位移:%f", self.speed,self.changeX);
}
魚(yú)移動(dòng)動(dòng)畫(huà)和上鉤動(dòng)畫(huà)
- (void)fishMove{
if (self.direction == FishModelImageViewFromLeft){//從左至右
if (_offsetX > ScreenWidth + _fishWidth){
_offsetY = arc4random()%_randomRange + OffSetYRange;
_offsetX = - _fishWidth - _offsetY;
}
_offsetX+=self.changeX;
self.frame = [self resetFrameOrigin:CGPointMake(_offsetX, _offsetY)];
if ([self fishCanBeCatchedWithOffsetX:_offsetX + _fishWidth]){
NSLog(@"釣到從左到右的魚(yú)了:%ld",(long)self.fishType);
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, -1, 1);//鏡像
transform = CGAffineTransformRotate(transform, M_PI_2);//旋轉(zhuǎn)90度
self.transform = transform;
self.frame = [self resetFrameOrigin:CGPointMake(ScreenWidth*2, 0)];
[self fishCatchedMoveUpWithOffsetX:_offsetX + _fishWidth];
_offsetX = ScreenWidth + _fishWidth + 1;//重置起點(diǎn)
_linkTimer.paused = YES;//計(jì)時(shí)器暫停
}
}else {//從右到左
if (_offsetX < -_fishWidth){
_offsetY = arc4random()%_randomRange + OffSetYRange;
_offsetX = ScreenWidth + _offsetY;
}
_offsetX-=self.changeX;
self.frame = [self resetFrameOrigin:CGPointMake(_offsetX, _offsetY)];
if ([self fishCanBeCatchedWithOffsetX:_offsetX]){
NSLog(@"釣到從右到左的魚(yú)了:%ld",(long)self.fishType);
self.transform = CGAffineTransformMakeRotation(M_PI_2);
self.frame = [self resetFrameOrigin:CGPointMake(ScreenWidth*2, 0)];
[self fishCatchedMoveUpWithOffsetX:_offsetX];
_offsetX = -_fishWidth-1;//重置起點(diǎn)
_linkTimer.paused = YES;//計(jì)時(shí)器暫停
}
}
}
魚(yú)上鉤的概率和贏得的金幣個(gè)數(shù)
//魚(yú)是否可以被釣上來(lái)(根據(jù)概率計(jì)算)
- (BOOL)fishCanBeCatchedWithOffsetX:(CGFloat)offsetX{
if (!self.isCanCatch) return NO;
if (fabs(offsetX - self.hookX) > self.changeX/2.0) return NO; //判斷是否到達(dá)了可以垂釣的點(diǎn)
int random = arc4random()%100; //[0,99]
DLog(@"random:%d", random);
switch (self.fishType) {
case FishModelImageViewTypeXHY://小黃魚(yú) 80% 金幣2
if (random < 80){
self.moneyCount = 2;
return YES;
}
break;
case FishModelImageViewTypeSBY://石斑魚(yú) 50% 金幣5
if (random < 50) {
self.moneyCount = 5;
return YES;
}
break;
case FishModelImageViewTypeHSY://紅杉魚(yú) 30% 金幣10
if (random < 30) {
self.moneyCount = 10;
return YES;
}
break;
case FishModelImageViewTypeBWY://斑紋魚(yú) 15% 金幣20
if (random < 15) {
self.moneyCount = 20;
return YES;
}
break;
case FishModelImageViewTypeSHY://珊瑚魚(yú) 5% 金幣50
if (random < 5) {
self.moneyCount = 50;
return YES;
}
break;
case FishModelImageViewTypeSY://鯊魚(yú) 1% 金幣100
if (random < 1) {
self.moneyCount = 100;
return YES;
}
break;
}
self.moneyCount = 0;
return NO;
}
- 3.被釣到的魚(yú)
初始化被釣到的魚(yú)方法
//初始化釣到的小魚(yú)
- (instancetype)initCatchedFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir{
if (self = [super init]){
self.direction = dir;
[self initViewWithType:type andDuration:0.5];
//重制x,y坐標(biāo)路翻, 30為魚(yú)鉤的寬度狈癞,85為魚(yú)鉤的長(zhǎng)度
self.x = (30 - self.width)/2.0;
self.y = 85 - 6;
if (dir == FishModelImageViewFromLeft){//從左往右,默認(rèn)所有的魚(yú)都是從右往左
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, -1, 1);//鏡像
transform = CGAffineTransformRotate(transform, M_PI_2);//旋轉(zhuǎn)90度
self.transform = transform;
}else {
self.transform = CGAffineTransformMakeRotation(M_PI_2);
}
}
return self;
}
當(dāng)魚(yú)被抓到后茂契,執(zhí)行上鉤動(dòng)畫(huà)
//魚(yú)被抓到后往上游
- (void)fishCatchedMoveUpWithOffsetX:(CGFloat) offsetX{
//鉤沉到魚(yú)塘的高度為45
//位移動(dòng)畫(huà)
CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"position"];
ani.duration = 0.7;
if (self.fishType == FishModelImageViewTypeSY){//鯊魚(yú)由于太長(zhǎng)蝶桶,所以不進(jìn)行上游動(dòng)畫(huà)了
ani.fromValue = [NSValue valueWithCGPoint:CGPointMake(offsetX,45 + _fishWidth/2.0)];
ani.toValue = [NSValue valueWithCGPoint:CGPointMake(_hookX, 45 + _fishWidth/2.0)];
}else {
ani.fromValue = [NSValue valueWithCGPoint:CGPointMake(offsetX, (_offsetY < 60) ? 45 + _fishWidth/2.0 : _offsetY)];//離鉤子近的話(huà)則不進(jìn)行動(dòng)畫(huà)
ani.toValue = [NSValue valueWithCGPoint:CGPointMake(_hookX, 45 + _fishWidth/2.0)];
}
ani.delegate = self;
//設(shè)置這兩句動(dòng)畫(huà)結(jié)束會(huì)停止在結(jié)束位置
[ani setValue:kFishCatchedMoveUpValue forKey:kFishCatchedMoveUpKey];
[self.layer addAnimation:ani forKey:kFishCatchedMoveUpKey];
}
魚(yú)上游動(dòng)畫(huà)結(jié)束后將翻轉(zhuǎn)的魚(yú)復(fù)位,然后執(zhí)行代理將釣到的魚(yú)通過(guò)代理傳遞出去
#pragma mark - CAAnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if (flag){
if ([[anim valueForKey:kFishCatchedMoveUpKey] isEqualToString:kFishCatchedMoveUpValue]){//魚(yú)上游
if (self.direction == FishModelImageViewFromLeft){
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, -1, 1);//鏡像
transform = CGAffineTransformRotate(transform, 0);//旋轉(zhuǎn)90度
self.transform = transform;
}else {
self.transform = CGAffineTransformMakeRotation(0);
}
if ([self.delegate respondsToSelector:@selector(catchTheFishWithType:andDirection:andWinCount:)]){
[self.delegate catchTheFishWithType:self.fishType andDirection:self.direction andWinCount:self.moneyCount];
}
}
}
}
金幣動(dòng)畫(huà)&&加分動(dòng)畫(huà)
金幣動(dòng)畫(huà)可以參考我的這篇文章:iOS 金幣入袋(收金幣)動(dòng)畫(huà)
加分動(dòng)畫(huà)比較簡(jiǎn)單掉冶,一個(gè)位移加透明度的組合動(dòng)畫(huà)實(shí)現(xiàn)真竖,具體可看代碼
釣魚(yú)View
這是實(shí)現(xiàn)界面了,本來(lái)是寫(xiě)在VC里的厌小,后來(lái)發(fā)現(xiàn)也能提取出來(lái)恢共,所有就提取出來(lái)了,在調(diào)用時(shí)非常簡(jiǎn)單璧亚,像正常View一樣初始化后添加到主View上即可讨韭,在viewDidDisappear中講資源釋放掉即可。
- (void)viewDidLoad {
[super viewDidLoad];
_fishView = [[FishingView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:_fishView];
}
- (void)viewDidDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[_fishView removeFishViewResource];
}
- 1.初始化魚(yú)鉤
初始化魚(yú)鉤
講魚(yú)鉤擺動(dòng)的角度通過(guò)代理傳到本界面
#pragma mark - 魚(yú)鉤
- (void)initHookView{
_fishHookView = [[FishHookView alloc] initWithFrame:CGRectMake((ScreenWidth - 30)/2.0, 5, 30, 85)];
__weak typeof (self) weakSelf = self;
_fishHookView.angleBlock = ^(CGFloat angle) {
weakSelf.angle = angle;
};
[self addSubview:_fishHookView];
UIImageView *yuGanImageView = [[UIImageView alloc] initWithFrame:CGRectMake(ScreenWidth/2.0 - 2, 0, ScreenWidth/2.0, 50)];
yuGanImageView.image = [UIImage imageNamed:@"fish_gan_tong"];
[self addSubview:yuGanImageView];
}
下鉤動(dòng)畫(huà):魚(yú)塘增加了點(diǎn)擊手勢(shì)涨岁,點(diǎn)擊后執(zhí)行釣魚(yú)動(dòng)作拐袜,暫停魚(yú)鉤擺動(dòng)計(jì)時(shí)器,下鉤動(dòng)畫(huà)結(jié)束后發(fā)送通知高速魚(yú)模塊可以上鉤了梢薪,并將魚(yú)鉤的底部中心坐標(biāo)傳遞過(guò)去蹬铺,魚(yú)線用CAShapeLayer繪制,并執(zhí)行strokeEnd動(dòng)畫(huà)
//釣魚(yú)動(dòng)作
- (void)fishBtnAction{
if (self.fishHookState != FishHookStateShake) return; //不是搖擺狀態(tài)不可出桿
[self.fishHookView hookTimerPause];//暫停魚(yú)鉤的計(jì)時(shí)器
double degree = _angle*180/M_PI;//度數(shù)
double rate = tan(_angle);//比列
DLog(@"degree:%f---rate:%f",degree,rate);
//計(jì)算出來(lái)線終點(diǎn)x的位置 , 鉤到水里的深度不變秉撇,即y是固定的
_lineOffsetX = ScreenWidth/2.0 - (FishLineHeigth)*rate;
//鉤子底部xy值
_hookBottomX = ScreenWidth/2.0 - (FishLineHeigth + FishHookHeight)*rate;
_hookBottomY = FishLineHeigth + FishHookHeight;
//動(dòng)畫(huà)時(shí)間
double aniDuration = [self hookOutOfRiver] ? 0.5 : 1;
//繪制路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(ScreenWidth/2.0 ,5)];
[path addLineToPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];
//圖形設(shè)置
_linePathLayer = [CAShapeLayer layer];
_linePathLayer.frame = self.bounds;
_linePathLayer.path = path.CGPath;
_linePathLayer.strokeColor = [HEXCOLOR(0x9e664a) CGColor];
_linePathLayer.fillColor = nil;
_linePathLayer.lineWidth = 3.0f;
_linePathLayer.lineJoin = kCALineJoinBevel;
[self.layer addSublayer:_linePathLayer];
//下鉤動(dòng)畫(huà)
CAKeyframeAnimation *ani = [CAKeyframeAnimation animationWithKeyPath:@"strokeEnd"];
ani.duration = aniDuration;
ani.values = @[@0,@0.8,@1];
ani.keyTimes = @[@0,@0.6,@1];
ani.delegate = self;
[ani setValue:kLineDownAnimationValue forKey:kLineDownAnimationKey];
[_linePathLayer addAnimation:ani forKey:kLineDownAnimationKey];
//位移動(dòng)畫(huà)
_hookAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//移動(dòng)路徑
CGFloat tempOffsetX = ScreenWidth/2.0 - (FishLineHeigth*0.8)*rate;
NSValue *p1 = [NSValue valueWithCGPoint:CGPointMake(ScreenWidth/2.0 ,5)];
NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(tempOffsetX, FishLineHeigth*0.8)];
NSValue *p3 = [NSValue valueWithCGPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];
_hookAnimation.duration = aniDuration;
_hookAnimation.values = @[p1,p2,p3];
_hookAnimation.keyTimes = @[@0,@0.7,@1];//動(dòng)畫(huà)分段時(shí)間
//設(shè)置這兩句動(dòng)畫(huà)結(jié)束會(huì)停止在結(jié)束位置
_hookAnimation.removedOnCompletion = NO;
_hookAnimation.fillMode=kCAFillModeForwards;
[_fishHookView.layer addAnimation:_hookAnimation forKey:@"goukey"];
}
釣魚(yú)動(dòng)作:下鉤動(dòng)畫(huà)結(jié)束后計(jì)時(shí)器打開(kāi)甜攀,執(zhí)行此方法;倒計(jì)時(shí)為最后一秒時(shí)魚(yú)不可上鉤(魚(yú)上鉤動(dòng)畫(huà)0.7s,要留上鉤動(dòng)畫(huà)的時(shí)間)琐馆;計(jì)時(shí)器為0時(shí)發(fā)送不可垂釣通知告訴魚(yú)模塊不可上鉤了规阀,并執(zhí)行上鉤動(dòng)畫(huà)。
//鉤子停在底部
- (void)hookStop:(NSTimer *)timer{
_stopDuration-=1;
//最后一秒不可上鉤
if (_stopDuration == 1){
//發(fā)送不可垂釣的通知
self.fishHookState = FishHookStateUp;
[[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookMove object:nil];
}
if (_stopDuration <= 0){
//關(guān)閉計(jì)時(shí)器
[timer setFireDate:[NSDate distantFuture]];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];
[path addLineToPoint:CGPointMake(ScreenWidth/2.0 ,5)];
_linePathLayer.path = path.CGPath;
//動(dòng)畫(huà)時(shí)間
double aniDuration = [self hookOutOfRiver] ? 0.5 : 1;
//上鉤
CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
ani.duration = aniDuration;
ani.fromValue = [NSNumber numberWithFloat:0];
ani.toValue = [NSNumber numberWithFloat:1];
ani.delegate = self;
ani.removedOnCompletion = NO;
ani.fillMode=kCAFillModeForwards;
[ani setValue:kLineUpAnimationValue forKey:kLineUpAnimationKey];
[_linePathLayer addAnimation:ani forKey:kLineUpAnimationKey];
[_fishHookView.layer removeAllAnimations];
NSValue *p1 = [NSValue valueWithCGPoint:CGPointMake(ScreenWidth/2.0 ,5)];
NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];
_hookAnimation.duration = aniDuration;
_hookAnimation.values = @[p2,p1];
_hookAnimation.keyTimes = @[@0,@1];
[_fishHookView.layer addAnimation:_hookAnimation forKey:@"goukey"];
}
}
金幣動(dòng)畫(huà)&加分動(dòng)畫(huà)
下鉤動(dòng)畫(huà)開(kāi)始瘦麸,總金幣減少10個(gè)
上鉤動(dòng)畫(huà)開(kāi)始谁撼,發(fā)送不可垂釣通知,魚(yú)鉤狀態(tài)為上鉤狀態(tài)
如果有捉到魚(yú)(根據(jù)魚(yú)模塊代理是否執(zhí)行判斷是否捉到)滋饲,執(zhí)行金幣動(dòng)畫(huà)和加分動(dòng)畫(huà)
下鉤動(dòng)畫(huà)結(jié)束厉碟,發(fā)送可以垂釣的通知給魚(yú)模塊喊巍,并將魚(yú)鉤坐標(biāo)傳遞過(guò)去,開(kāi)啟上鉤的計(jì)時(shí)器
上鉤動(dòng)畫(huà)結(jié)束箍鼓,更改魚(yú)鉤狀態(tài)崭参,移除一些View,魚(yú)鉤繼續(xù)擺動(dòng)
#pragma mark - CAAnimationDelegate 動(dòng)畫(huà)代理
//動(dòng)畫(huà)開(kāi)始
- (void)animationDidStart:(CAAnimation *)anim{
//下鉤動(dòng)畫(huà)開(kāi)始
if ([[anim valueForKey:kLineDownAnimationKey] isEqualToString:kLineDownAnimationValue]){
self.fishHookState = FishHookStateDown;//下鉤狀態(tài)
//錢(qián)數(shù)
self.moneyLabel.text = [NSString stringWithFormat:@"%d", _totalMoney-=10];
self.winMoney = 0;
}else if ([[anim valueForKey:kLineUpAnimationKey] isEqualToString:kLineUpAnimationValue]){//上鉤動(dòng)畫(huà)開(kāi)始
self.fishHookState = FishHookStateUp;//上鉤狀態(tài)
[[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookMove object:nil];
}
if (self.isCatched){//釣到魚(yú)后落金幣
HHShootButton *button = [[HHShootButton alloc] initWithFrame:CGRectMake(_lineOffsetX, 0, 10, 10) andEndPoint:CGPointMake(10, 200)];
button.setting.iconImage = [UIImage imageNamed:@"coin"];
button.setting.animationType = ShootButtonAnimationTypeLine;
[self.bgImageView addSubview:button];
[self bringSubviewToFront:button];
[button startAnimation];
HHWinMoneyLabel *winLabel = [[HHWinMoneyLabel alloc] initWithFrame:CGRectMake(_lineOffsetX - 100/2, ScreenFullHeight - FishSeaHeight, 100, 30)];
winLabel.text = [NSString stringWithFormat:@"+%d",_winMoney];
[self addSubview:winLabel];
self.isCatched = !self.isCatched;
//金幣總數(shù)
self.moneyLabel.text = [NSString stringWithFormat:@"%d", _totalMoney+=self.winMoney];
}
}
//動(dòng)畫(huà)結(jié)束
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if (flag){
if ([[anim valueForKey:kLineDownAnimationKey] isEqualToString:kLineDownAnimationValue]){//下鉤動(dòng)畫(huà)結(jié)束
self.fishHookState = FishHookStateStop;//垂釣狀態(tài)
//鉤的位置
NSDictionary *dic = @{@"offsetX":[NSString stringWithFormat:@"%.2f",_hookBottomX],@"offsetY":[NSString stringWithFormat:@"%.2f",_hookBottomY]};
//發(fā)送可以垂釣的通知,鉤的位置傳過(guò)去
[[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookStop object:nil userInfo:dic];
_stopDuration = [self hookOutOfRiver] ? 1 : arc4random()%3 + 3; //默認(rèn)時(shí)間[3,5),拋到岸上1s
//開(kāi)啟上鉤定時(shí)器
[_fishTimer setFireDate:[NSDate distantPast]];
}else if ([[anim valueForKey:kLineUpAnimationKey] isEqualToString:kLineUpAnimationValue]){//上鉤動(dòng)畫(huà)結(jié)束
self.fishHookState = FishHookStateShake;//搖擺狀態(tài)
[_linePathLayer removeFromSuperlayer];
[_fishHookView hoolTimerGoOn];//魚(yú)鉤計(jì)時(shí)器繼續(xù)
_catchedHeight = 0;
//移除釣上來(lái)的魚(yú)
[self removeTheCatchedFishes];
}
}
}
魚(yú)模塊的代理方法
創(chuàng)建一個(gè)被釣到的魚(yú)款咖,加在魚(yú)鉤上何暮,這樣便可和魚(yú)鉤一起執(zhí)行上鉤動(dòng)畫(huà)了
#pragma mark - FishModelImageViewDelegate 釣到魚(yú)后的代理
- (void)catchTheFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir andWinCount:(int)count{
self.isCatched = YES;
FishModelImageView *fishImageView = [[FishModelImageView alloc] initCatchedFishWithType:type andDirection:dir];
[self.fishHookView addSubview:fishImageView];
fishImageView.y = fishImageView.y + _catchedHeight;
_catchedHeight += 8;//每釣到一個(gè)y坐標(biāo)往下移
//贏得錢(qián)數(shù)
self.winMoney += count;
}
2.初始化魚(yú)塘
簡(jiǎn)單的創(chuàng)建魚(yú)背景并添加點(diǎn)擊手勢(shì)3.初始化魚(yú)
通過(guò)for循環(huán)可以創(chuàng)建出多個(gè)某種魚(yú)
//小黃魚(yú)
for (int i = 0; i < 8; i++){
FishModelImageView *model1 = [[FishModelImageView alloc] initCanCatchFishWithType:FishModelImageViewTypeXHY andDirection: (i%2 == 0) ? FishModelImageViewFromRight : FishModelImageViewFromLeft];
model1.delegate = self;
[self.bgImageView addSubview:model1];
}
- 4.資源移除
由于計(jì)時(shí)器不銷(xiāo)毀會(huì)造成循環(huán)引用,導(dǎo)致內(nèi)存泄漏铐殃,所以必須手動(dòng)移除他海洼,還有動(dòng)畫(huà)如果執(zhí)行了代理,并且設(shè)置了結(jié)束后停留在結(jié)束位置背稼,也會(huì)得不到釋放贰军,所以都要手動(dòng)釋放資源
- (void)removeFishViewResource{
//解決魚(yú)鉤上鉤動(dòng)畫(huà)循環(huán)引用的問(wèn)題
_linePathLayer = nil;
//釣魚(yú)計(jì)時(shí)器關(guān)閉
[_fishTimer invalidate];
_fishTimer = nil;
//釋放魚(yú)鉤的計(jì)時(shí)器
[self.fishHookView hoolTimerInvalidate];
//發(fā)送通知釋放小魚(yú)資源
[[NSNotificationCenter defaultCenter] postNotificationName:NotificationRemoveFishModelTimer object:nil];
}
總結(jié)
至此,本游戲已經(jīng)完成了蟹肘,寫(xiě)的比較多词疼,也比較亂,有什么不好的地方歡迎批評(píng)指正帘腹,希望對(duì)大伙有所幫助吧贰盗,本demo地址傳送門(mén)。