- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章iOS 动画实战之钓鱼小游戏实例代码由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前言 。
最近写了一款钓鱼小游戏,自己平时也没做过游戏,本来以为这种游戏要用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 。
最后此篇关于iOS 动画实战之钓鱼小游戏实例代码的文章就讲到这里了,如果你想了解更多关于iOS 动画实战之钓鱼小游戏实例代码的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
问题情景 混淆群内的小伙伴遇到这么个问题,Mailivery 这个网站登录后,明明提交的表单(邮箱和密码也正确)、请求头等等都没问题,为啥一直重定向到登录页面呢?唉,该出手时就出手啊,我也看看咋回事
实战-行业攻防应急响应 简介: 服务器场景操作系统 Ubuntu 服务器账号密码:root/security123 分析流量包在/home/security/security.pcap 相
背景 最近公司将我们之前使用的链路工具切换为了 OpenTelemetry. 我们的技术栈是: OTLP C
一 同一类的方法都用 synchronized 修饰 1 代码 package concurrent; import java.util.concurrent.TimeUnit; public c
一 简单例子 1 代码 package concurrent.threadlocal; /** * ThreadLocal测试 * * @author cakin */ public class T
1. 问题背景 问题发生在快递分拣的流程中,我尽可能将业务背景简化,让大家只关注并发问题本身。 分拣业务针对每个快递包裹都会生成一个任务,我们称它为 task。task 中有两个字段需要
实战环境 elastic search 8.5.0 + kibna 8.5.0 + springboot 3.0.2 + spring data elasticsearch 5.0.2 +
Win10下yolov8 tensorrt模型加速部署【实战】 TensorRT-Alpha 基于tensorrt+cuda c++实现模型end2end的gpu加速,支持win10、
yolov8 tensorrt模型加速部署【实战】 TensorRT-Alpha 基于tensorrt+cuda c++实现模型end2end的gpu加速,支持win10、linux,
目录如下: 为什么需要自定义授权类型? 前面介绍OAuth2.0的基础知识点时介绍过支持的4种授权类型,分别如下: 授权码模式 简化模式 客户端模式 密码模式
今天这篇文章介绍一下如何在修改密码、修改权限、注销等场景下使JWT失效。 文章的目录如下: 解决方案 JWT最大的一个优势在于它是无状态的,自身包含了认证鉴权所需要的所有信息,服务器端
前言 大家好,我是捡田螺的小男孩。(求个星标置顶) 我们日常做分页需求时,一般会用limit实现,但是当偏移量特别大的时候,查询效率就变得低下。本文将分四个方案,讨论如何优化MySQL百万数
前言 大家好,我是捡田螺的小男孩。 平时我们写代码呢,多数情况都是流水线式写代码,基本就可以实现业务逻辑了。如何在写代码中找到乐趣呢,我觉得,最好的方式就是:使用设计模式优化自己
我们先讲一些arm汇编的基础知识。(我们以armv7为例,最新iphone5s上的64位暂不讨论) 基础知识部分: 首先你介绍一下寄存器: r0-r3:用于函数参数及返回值的传递 r4-r6
一 同一类的静态方法都用 synchronized 修饰 1 代码 package concurrent; import java.util.concurrent.TimeUnit; public
DRF快速写五个接口,比你用手也快··· 实战-DRF快速写接口 开发环境 Python3.6 Pycharm专业版2021.2.3 Sqlite3 Django 2.2 djangorestfram
一 添加依赖 org.apache.thrift libthrift 0.11.0 二 编写 IDL 通过 IDL(.thrift 文件)定义数据结构、异常和接口等数据,供各种编程语言使用 nam
我正在阅读 Redis in action e-book关于semaphores的章节.这是使用redis实现信号量的python代码 def acquire_semaphore(conn, semn
自定义控件在WPF开发中是很常见的,有时候某些控件需要契合业务或者美化统一样式,这时候就需要对控件做出一些改造。 目录 按钮设置圆角
师父布置的任务,让我写一个服务练练手,搞清楚socket的原理和过程后跑了一个小demo,很有成就感,代码内容也比较清晰易懂,很有教育启发意义。 代码 ?
我是一名优秀的程序员,十分优秀!