前言
最近寫了一款釣魚小游戲,自己平時也沒做過游戲,本來以為這種游戲要用cocos2d什么的實現,后來發現其實動畫就可以實現很棒的效果,先看看效果圖。
思維導圖
首先我們看下思維導圖,本游戲主要分為4大塊,其中魚的實現最為復雜
思維導圖
項目結構
準備工作
首先將需要的圖準備好,這個魚其實就是一組圖片,圖片大小固定,每一幀位置變化,所以看起來 是一個上下游動的魚。
單張圖片
魚鉤模塊
擺動動畫
魚鉤的擺動范圍是[m_pi/4.0,-m_pi/4.0] (垂直向下為0度,順時針為正),這里利用了計時器進行角度的更改,計時器用的cadisplaylink,它是一個和屏幕刷新率一致的定時器,如果沒有卡頓,每秒刷新次數是60次,本demo很多計時器用的都是cadisplaylink。下面是魚鉤的主要代碼(重點:1、設置錨點后重置frame,2、更改角度,3、旋轉)。 其中定義了一個block將角度angle回傳到fishingview界面計算魚鉤落到池塘的位置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
@property (nonatomic, strong) cadisplaylink *linktimer; @property (nonatomic, assign) bool isreduce; //改變方向 @property (nonatomic, assign) cgfloat angle; //擺動的角度 - ( 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]; // 創建一個對象計時器 _linktimer = [cadisplaylink displaylinkwithtarget:self selector:@selector(hookmove)]; //啟動這個link [_linktimer addtorunloop:[nsrunloop mainrunloop] formode:nsdefaultrunloopmode]; } //設置錨點后重新設置frame - ( void ) setanchorpoint:(cgpoint)anchorpoint forview:(uiview *)view{ cgrect oldframe = view.frame; view.layer.anchorpoint = anchorpoint; view.frame = oldframe; } #pragma mark - 魚鉤擺動 - ( void )hookmove{ if (self.isreduce){ _angle-=1.8* cos (1.5*_angle)*0.01; //計算角度,利用cos模擬上升過程中減慢,下降加快 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(@"魚鉤角度%f",_angle); //旋轉動畫 self.transform = cgaffinetransformmakerotation(_angle); } |
魚模塊
魚模塊是繼承自uiimageview的一個類
魚模塊提供了三種初始化方式,可垂釣的魚、不可垂釣的魚(可以不用)、釣到的魚三種魚。
魚的移動方式有兩種,使用枚舉定義,從左到右,從右到左
魚的種類有六種,用枚舉進行了定義
1
2
3
4
5
6
7
8
|
typedef ns_enum(nsinteger, fishmodelimageviewtype){ fishmodelimageviewtypexhy = 0, //小黃魚 fishmodelimageviewtypesby = 1, //石斑魚 fishmodelimageviewtypehsy = 2, //紅杉魚 fishmodelimageviewtypebwy = 3, //斑紋魚 fishmodelimageviewtypeshy = 4, //珊瑚魚 fishmodelimageviewtypesy = 5, //鯊魚 }; |
提供了一個釣到魚后的代理
1
2
3
4
5
|
fishmodelimageviewdelegate //魚的種類-游動方向-贏取金額 方法 - ( void )catchthefishwithtype:(fishmodelimageviewtype)type anddirection:(fishmodelimageviewdirection)dir andwincount:( int )count; |
1、動態的魚
加載動態魚的方法
1
2
3
4
5
|
//初始化uiimageview uiimageview *imageview = [[uiimageview alloc] initwithframe:cgrectmake(0, 0, 55, 55)]; //如果圖片的名字是有順序的,例如xhy1,xhy2,xhy3...,可以取去掉序號的名字,然后會自動將所有的圖片都加載進來,duration是動畫時長 imageview.image = [uiimage animatedimagenamed:@ "xhy" duration:1]; [self.view addsubview:imageview]; |
初始化不同的魚,不同的魚大小不同,移動的速度不同,所以動畫時長不一樣
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
//初始化小魚 git動畫時長 - ( void )initviewwithtype:(fishmodelimageviewtype)type andduration:( double ) time { self.fishtype = type; switch (type) { case fishmodelimageviewtypexhy: //小黃魚 self.duration = 6.0; self.frame = cgrectmake(-100, 0, 35, 40); //魚的大小要定義好 self.image = [uiimage animatedimagenamed:@ "xhy" duration: time ]; break ; case fishmodelimageviewtypesby: //石斑魚 self.duration = 7.0; self.frame = cgrectmake(-100, 0, 50, 50); self.image = [uiimage animatedimagenamed:@ "sby" duration: time ]; break ; case fishmodelimageviewtypehsy: //紅杉魚 self.duration = 8.0; self.frame = cgrectmake(-100, 0, 50, 40); self.image = [uiimage animatedimagenamed:@ "hsy" duration: time ]; break ; case fishmodelimageviewtypebwy: //斑紋魚 self.duration = 8.5; self.frame = cgrectmake(-100, 0, 65, 53); self.image = [uiimage animatedimagenamed:@ "bwy" duration: time ]; break ; case fishmodelimageviewtypeshy: //珊瑚魚 self.duration = 9.0; self.frame = cgrectmake(-100, 0, 55, 55); self.image = [uiimage animatedimagenamed:@ "shy" duration: time ]; break ; case fishmodelimageviewtypesy: //鯊魚 self.duration = 11.0; self.frame = cgrectmake(-200, 0, 145, 90); self.image = [uiimage animatedimagenamed:@ "sy" duration: time ]; break ; } } |
2、移動的魚
提供的圖片都是頭朝左的(見上面的動圖),所以從左往右游的話圖片需要進行鏡像反轉
對于魚是否可以垂釣是用通知進行傳遞信息的,可垂釣、不可垂釣兩種狀態
可垂釣:魚鉤沉到魚塘時受到垂釣通知(將魚鉤底部的坐標傳過來),現在魚可以垂釣,當根據上鉤概率等因素判斷魚上鉤后,對魚進行旋轉,然后執行上鉤動畫。動畫結束后執行代理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
//初始化可以垂釣的魚 - (instancetype)initcancatchfishwithtype:(fishmodelimageviewtype)type anddirection:(fishmodelimageviewdirection)dir{ if (self = [super init]){ self.direction = dir; [self initviewwithtype:type andduration:1]; if (dir == fishmodelimageviewfromleft){ //從左往右,默認所有的魚都是從右往左 self.transform = cgaffinetransformmakescale(-1, 1); //鏡像 } [self initfishview]; } return self; } #pragma mark - 可以垂釣的魚(計時器) - ( 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]; //創建計時器 _linktimer = [cadisplaylink displaylinkwithtarget:self selector:@selector(fishmove)]; //啟動這個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; //游動速度 self.changex = self.speed/60.0; //計時器每秒60次 dlog(@ "魚游動的速度:%f,每次位移:%f" , self.speed,self.changex); } |
魚移動動畫和上鉤動畫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
- ( 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(@ "釣到從左到右的魚了:%ld" ,( long )self.fishtype); cgaffinetransform transform = cgaffinetransformidentity; transform = cgaffinetransformscale(transform, -1, 1); //鏡像 transform = cgaffinetransformrotate(transform, m_pi_2); //旋轉90度 self.transform = transform; self.frame = [self resetframeorigin:cgpointmake(screenwidth*2, 0)]; [self fishcatchedmoveupwithoffsetx:_offsetx + _fishwidth]; _offsetx = screenwidth + _fishwidth + 1; //重置起點 _linktimer.paused = yes; //計時器暫停 } } 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(@ "釣到從右到左的魚了:%ld" ,( long )self.fishtype); self.transform = cgaffinetransformmakerotation(m_pi_2); self.frame = [self resetframeorigin:cgpointmake(screenwidth*2, 0)]; [self fishcatchedmoveupwithoffsetx:_offsetx]; _offsetx = -_fishwidth-1; //重置起點 _linktimer.paused = yes; //計時器暫停 } } } |
魚上鉤的概率和贏得的金幣個數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
//魚是否可以被釣上來(根據概率計算) - ( bool )fishcanbecatchedwithoffsetx:(cgfloat)offsetx{ if (!self.iscancatch) return no; if ( fabs (offsetx - self.hookx) > self.changex/2.0) return no; //判斷是否到達了可以垂釣的點 int random = arc4random()%100; //[0,99] dlog(@ "random:%d" , random); switch (self.fishtype) { case fishmodelimageviewtypexhy: //小黃魚 80% 金幣2 if (random < 80){ self.moneycount = 2; return yes; } break ; case fishmodelimageviewtypesby: //石斑魚 50% 金幣5 if (random < 50) { self.moneycount = 5; return yes; } break ; case fishmodelimageviewtypehsy: //紅杉魚 30% 金幣10 if (random < 30) { self.moneycount = 10; return yes; } break ; case fishmodelimageviewtypebwy: //斑紋魚 15% 金幣20 if (random < 15) { self.moneycount = 20; return yes; } break ; case fishmodelimageviewtypeshy: //珊瑚魚 5% 金幣50 if (random < 5) { self.moneycount = 50; return yes; } break ; case fishmodelimageviewtypesy: //鯊魚 1% 金幣100 if (random < 1) { self.moneycount = 100; return yes; } break ; } self.moneycount = 0; return no; } |
3.被釣到的魚
初始化被釣到的魚方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//初始化釣到的小魚 - (instancetype)initcatchedfishwithtype:(fishmodelimageviewtype)type anddirection:(fishmodelimageviewdirection)dir{ if (self = [super init]){ self.direction = dir; [self initviewwithtype:type andduration:0.5]; //重制x,y坐標, 30為魚鉤的寬度,85為魚鉤的長度 self.x = (30 - self.width)/2.0; self.y = 85 - 6; if (dir == fishmodelimageviewfromleft){ //從左往右,默認所有的魚都是從右往左 cgaffinetransform transform = cgaffinetransformidentity; transform = cgaffinetransformscale(transform, -1, 1); //鏡像 transform = cgaffinetransformrotate(transform, m_pi_2); //旋轉90度 self.transform = transform; } else { self.transform = cgaffinetransformmakerotation(m_pi_2); } } return self; } |
當魚被抓到后,執行上鉤動畫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//魚被抓到后往上游 - ( void )fishcatchedmoveupwithoffsetx:(cgfloat) offsetx{ //鉤沉到魚塘的高度為45 //位移動畫 cabasicanimation *ani = [cabasicanimation animationwithkeypath:@ "position" ]; ani.duration = 0.7; if (self.fishtype == fishmodelimageviewtypesy){ //鯊魚由于太長,所以不進行上游動畫了 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)]; //離鉤子近的話則不進行動畫 ani.tovalue = [nsvalue valuewithcgpoint:cgpointmake(_hookx, 45 + _fishwidth/2.0)]; } ani.delegate = self; //設置這兩句動畫結束會停止在結束位置 [ani setvalue:kfishcatchedmoveupvalue forkey:kfishcatchedmoveupkey]; [self.layer addanimation:ani forkey:kfishcatchedmoveupkey]; } |
魚上游動畫結束后將翻轉的魚復位,然后執行代理將釣到的魚通過代理傳遞出去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#pragma mark - caanimationdelegate - ( void )animationdidstop:(caanimation *)anim finished:( bool )flag{ if (flag){ if ([[anim valueforkey:kfishcatchedmoveupkey] isequaltostring:kfishcatchedmoveupvalue]){ //魚上游 if (self.direction == fishmodelimageviewfromleft){ cgaffinetransform transform = cgaffinetransformidentity; transform = cgaffinetransformscale(transform, -1, 1); //鏡像 transform = cgaffinetransformrotate(transform, 0); //旋轉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]; } } } } |
釣魚view
這是實現界面了,本來是寫在vc里的,后來發現也能提取出來,所有就提取出來了,在調用時非常簡單,像正常view一樣初始化后添加到主view上即可,在viewdiddisappear中講資源釋放掉即可。
1
2
3
4
5
6
7
8
9
|
- ( 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.初始化魚鉤
初始化魚鉤
講魚鉤擺動的角度通過代理傳到本界面
1
2
3
4
5
6
7
8
9
10
11
12
|
#pragma mark - 魚鉤 - ( 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]; } |
下鉤動畫:魚塘增加了點擊手勢,點擊后執行釣魚動作,暫停魚鉤擺動計時器,下鉤動畫結束后發送通知高速魚模塊可以上鉤了,并將魚鉤的底部中心坐標傳遞過去,魚線用cashapelayer繪制,并執行strokeend動畫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
//釣魚動作 - ( void )fishbtnaction{ if (self.fishhookstate != fishhookstateshake) return ; //不是搖擺狀態不可出桿 [self.fishhookview hooktimerpause]; //暫停魚鉤的計時器 double degree = _angle*180/m_pi; //度數 double rate = tan (_angle); //比列 dlog(@ "degree:%f---rate:%f" ,degree,rate); //計算出來線終點x的位置 , 鉤到水里的深度不變,即y是固定的 _lineoffsetx = screenwidth/2.0 - (fishlineheigth)*rate; //鉤子底部xy值 _hookbottomx = screenwidth/2.0 - (fishlineheigth + fishhookheight)*rate; _hookbottomy = fishlineheigth + fishhookheight; //動畫時間 double aniduration = [self hookoutofriver] ? 0.5 : 1; //繪制路徑 uibezierpath *path = [uibezierpath bezierpath]; [path movetopoint:cgpointmake(screenwidth/2.0 ,5)]; [path addlinetopoint:cgpointmake(_lineoffsetx, fishlineheigth)]; //圖形設置 _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]; //下鉤動畫 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]; //位移動畫 _hookanimation = [cakeyframeanimation animationwithkeypath:@ "position" ]; //移動路徑 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]; //動畫分段時間 //設置這兩句動畫結束會停止在結束位置 _hookanimation.removedoncompletion = no; _hookanimation.fillmode=kcafillmodeforwards; [_fishhookview.layer addanimation:_hookanimation forkey:@ "goukey" ]; } |
釣魚動作:下鉤動畫結束后計時器打開,執行此方法;倒計時為最后一秒時魚不可上鉤(魚上鉤動畫0.7s,要留上鉤動畫的時間);計時器為0時發送不可垂釣通知告訴魚模塊不可上鉤了,并執行上鉤動畫。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
//鉤子停在底部 - ( void )hookstop:(nstimer *)timer{ _stopduration-=1; //最后一秒不可上鉤 if (_stopduration == 1){ //發送不可垂釣的通知 self.fishhookstate = fishhookstateup; [[nsnotificationcenter defaultcenter] postnotificationname:notificationfishhookmove object:nil]; } if (_stopduration <= 0){ //關閉計時器 [timer setfiredate:[nsdate distantfuture]]; uibezierpath *path = [uibezierpath bezierpath]; [path movetopoint:cgpointmake(_lineoffsetx, fishlineheigth)]; [path addlinetopoint:cgpointmake(screenwidth/2.0 ,5)]; _linepathlayer.path = path.cgpath; //動畫時間 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" ]; } } |
金幣動畫&加分動畫
下鉤動畫開始,總金幣減少10個
上鉤動畫開始,發送不可垂釣通知,魚鉤狀態為上鉤狀態
如果有捉到魚(根據魚模塊代理是否執行判斷是否捉到),執行金幣動畫和加分動畫
下鉤動畫結束,發送可以垂釣的通知給魚模塊,并將魚鉤坐標傳遞過去,開啟上鉤的計時器
上鉤動畫結束,更改魚鉤狀態,移除一些view,魚鉤繼續擺動
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#pragma mark - caanimationdelegate 動畫代理 //動畫開始 - ( void )animationdidstart:(caanimation *)anim{ //下鉤動畫開始 if ([[anim valueforkey:klinedownanimationkey] isequaltostring:klinedownanimationvalue]){ self.fishhookstate = fishhookstatedown; //下鉤狀態 //錢數 self.moneylabel.text = [nsstring stringwithformat:@ "%d" , _totalmoney-=10]; self.winmoney = 0; } else if ([[anim valueforkey:klineupanimationkey] isequaltostring:klineupanimationvalue]){ //上鉤動畫開始 self.fishhookstate = fishhookstateup; //上鉤狀態 [[nsnotificationcenter defaultcenter] postnotificationname:notificationfishhookmove object:nil]; } if (self.iscatched){ //釣到魚后落金幣 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; //金幣總數 self.moneylabel.text = [nsstring stringwithformat:@ "%d" , _totalmoney+=self.winmoney]; } } //動畫結束 - ( void )animationdidstop:(caanimation *)anim finished:( bool )flag{ if (flag){ if ([[anim valueforkey:klinedownanimationkey] isequaltostring:klinedownanimationvalue]){ //下鉤動畫結束 self.fishhookstate = fishhookstatestop; //垂釣狀態 //鉤的位置 nsdictionary *dic = @{@ "offsetx" :[nsstring stringwithformat:@ "%.2f" ,_hookbottomx],@ "offsety" :[nsstring stringwithformat:@ "%.2f" ,_hookbottomy]}; //發送可以垂釣的通知,鉤的位置傳過去 [[nsnotificationcenter defaultcenter] postnotificationname:notificationfishhookstop object:nil userinfo:dic]; _stopduration = [self hookoutofriver] ? 1 : arc4random()%3 + 3; //默認時間[3,5),拋到岸上1s //開啟上鉤定時器 [_fishtimer setfiredate:[nsdate distantpast]]; } else if ([[anim valueforkey:klineupanimationkey] isequaltostring:klineupanimationvalue]){ //上鉤動畫結束 self.fishhookstate = fishhookstateshake; //搖擺狀態 [_linepathlayer removefromsuperlayer]; [_fishhookview hooltimergoon]; //魚鉤計時器繼續 _catchedheight = 0; //移除釣上來的魚 [self removethecatchedfishes]; } } } |
魚模塊的代理方法
創建一個被釣到的魚,加在魚鉤上,這樣便可和魚鉤一起執行上鉤動畫了
1
2
3
4
5
6
7
8
9
10
|
#pragma mark - fishmodelimageviewdelegate 釣到魚后的代理 - ( 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; //每釣到一個y坐標往下移 //贏得錢數 self.winmoney += count; } |
2.初始化魚塘
簡單的創建魚背景并添加點擊手勢
3.初始化魚
通過for循環可以創建出多個某種魚
1
2
3
4
5
6
|
//小黃魚 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.資源移除
由于計時器不銷毀會造成循環引用,導致內存泄漏,所以必須手動移除他,還有動畫如果執行了代理,并且設置了結束后停留在結束位置,也會得不到釋放,所以都要手動釋放資源
1
2
3
4
5
6
7
8
9
10
11
|
- ( void )removefishviewresource{ //解決魚鉤上鉤動畫循環引用的問題 _linepathlayer = nil; //釣魚計時器關閉 [_fishtimer invalidate]; _fishtimer = nil; //釋放魚鉤的計時器 [self.fishhookview hooltimerinvalidate]; //發送通知釋放小魚資源 [[nsnotificationcenter defaultcenter] postnotificationname:notificationremovefishmodeltimer object:nil]; } |
總結
至此,本游戲已經完成了,寫的比較多,也比較亂,有什么不好的地方歡迎批評指正,希望對大伙有所幫助吧,本demo地址【https://github.com/ccalary/fishinggame】
原文鏈接:http://www.sohu.com/a/223595556_505825