查看: 1446|回复: 0

[IOS开发教程] iOS 动画实战之钓鱼小游戏实例代码

发表于 2018-4-13 08:00:05

前言

最近写了一款钓鱼小游戏,自己平时也没做过游戏,本来以为这种游戏要用cocos2d什么的实现,后来发现其实动画就可以实现很棒的效果,先看看效果图。

思维导图

首先我们看下思维导图,本游戏主要分为4大块,其中鱼的实现最为复杂

思维导图

项目结构

准备工作

首先将需要的图准备好,这个鱼其实就是一组图片,图片大小固定,每一帧位置变化,所以看起来 是一个上下游动的鱼。

单张图片

鱼钩模块

摆动动画

鱼钩的摆动范围是[M_PI/4.0,-M_PI/4.0] (垂直向下为0度,顺时针为正),这里利用了计时器进行角度的更改,计时器用的CADisplayLink,它是一个和屏幕刷新率一致的定时器,如果没有卡顿,每秒刷新次数是60次,本Demo很多计时器用的都是CADisplayLink。下面是鱼钩的主要代码(重点:1、设置锚点后重置frame,2、更改角度,3、旋转)。 其中定义了一个block将角度angle回传到FishingView界面计算鱼钩落到池塘的位置。

  1. @property (nonatomic, strong) CADisplayLink *linkTimer;
  2. @property (nonatomic, assign) BOOL isReduce;//改变方向
  3. @property (nonatomic, assign) CGFloat angle;//摆动的角度
  4. - (void)initView{
  5. [self setAnchorPoint:CGPointMake(0.5, 0) forView:self];
  6. UIImageView *gouImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.frame.size.height - 35 , 30, 35)];
  7. gouImageView.image = [UIImage imageNamed:@"fish_catcher_tong"];
  8. [self addSubview:gouImageView];
  9. UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake((self.frame.size.width - 3)/2.0, 0, 3, self.frame.size.height - 35)];
  10. lineView.backgroundColor = HEXCOLOR(0x9e664a);
  11. [self addSubview:lineView];
  12. // 创建一个对象计时器
  13. _linkTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(hookMove)];
  14. //启动这个link
  15. [_linkTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
  16. }
  17. //设置锚点后重新设置frame
  18. - (void) setAnchorPoint:(CGPoint)anchorpoint forView:(UIView *)view{
  19. CGRect oldFrame = view.frame;
  20. view.layer.anchorPoint = anchorpoint;
  21. view.frame = oldFrame;
  22. }
  23. #pragma mark - 鱼钩摆动
  24. - (void)hookMove{
  25. if (self.isReduce){
  26. _angle-=1.8*cos(1.5*_angle)*0.01;//计算角度,利用cos模拟上升过程中减慢,下降加快
  27. if (_angle < -M_PI/180*45){
  28. self.isReduce = NO;
  29. }
  30. }else {
  31. _angle+=1.8*cos(1.5*_angle)*0.01;
  32. if (_angle > M_PI/180*45){
  33. self.isReduce = YES;
  34. }
  35. }
  36. if (self.angleBlock){
  37. self.angleBlock(_angle);
  38. }
  39. // DLog(@"鱼钩角度%f",_angle);
  40. //旋转动画
  41. self.transform = CGAffineTransformMakeRotation(_angle);
  42. }
复制代码

鱼模块

鱼模块是继承自UIImageView的一个类

鱼模块提供了三种初始化方式,可垂钓的鱼、不可垂钓的鱼(可以不用)、钓到的鱼三种鱼。

鱼的移动方式有两种,使用枚举定义,从左到右,从右到左

鱼的种类有六种,用枚举进行了定义

  1. typedef NS_ENUM(NSInteger, FishModelImageViewType){
  2. FishModelImageViewTypeXHY = 0, //小黄鱼
  3. FishModelImageViewTypeSBY = 1, //石斑鱼
  4. FishModelImageViewTypeHSY = 2, //红杉鱼
  5. FishModelImageViewTypeBWY = 3, //斑纹鱼
  6. FishModelImageViewTypeSHY = 4, //珊瑚鱼
  7. FishModelImageViewTypeSY = 5, //鲨鱼
  8. };
复制代码

提供了一个钓到鱼后的代理

  1. FishModelImageViewDelegate
  2. //鱼的种类-游动方向-赢取金额
  3. 方法 - (void)catchTheFishWithType:(FishModelImageViewType)type
  4. andDirection:(FishModelImageViewDirection)dir
  5. andWinCount:(int)count;
复制代码

1、动态的鱼

加载动态鱼的方法

  1. //初始化UIImageView
  2. UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 55, 55)];
  3. //如果图片的名字是有顺序的,例如xhy1,xhy2,xhy3...,可以取去掉序号的名字,然后会自动将所有的图片都加载进来,duration是动画时长
  4. imageView.image = [UIImage animatedImageNamed:@"xhy" duration:1];
  5. [self.view addSubview:imageView];
复制代码

初始化不同的鱼,不同的鱼大小不同,移动的速度不同,所以动画时长不一样

  1. //初始化小鱼 git动画时长
  2. - (void)initViewWithType:(FishModelImageViewType)type andDuration:(double)time{
  3. self.fishType = type;
  4. switch (type) {
  5. case FishModelImageViewTypeXHY://小黄鱼
  6. self.duration = 6.0;
  7. self.frame = CGRectMake(-100, 0, 35, 40); //鱼的大小要定义好
  8. self.image = [UIImage animatedImageNamed:@"xhy" duration:time];
  9. break;
  10. case FishModelImageViewTypeSBY://石斑鱼
  11. self.duration = 7.0;
  12. self.frame = CGRectMake(-100, 0, 50, 50);
  13. self.image = [UIImage animatedImageNamed:@"sby" duration:time];
  14. break;
  15. case FishModelImageViewTypeHSY://红杉鱼
  16. self.duration = 8.0;
  17. self.frame = CGRectMake(-100, 0, 50, 40);
  18. self.image = [UIImage animatedImageNamed:@"hsy" duration:time];
  19. break;
  20. case FishModelImageViewTypeBWY://斑纹鱼
  21. self.duration = 8.5;
  22. self.frame = CGRectMake(-100, 0, 65, 53);
  23. self.image = [UIImage animatedImageNamed:@"bwy" duration:time];
  24. break;
  25. case FishModelImageViewTypeSHY://珊瑚鱼
  26. self.duration = 9.0;
  27. self.frame = CGRectMake(-100, 0, 55, 55);
  28. self.image = [UIImage animatedImageNamed:@"shy" duration:time];
  29. break;
  30. case FishModelImageViewTypeSY://鲨鱼
  31. self.duration = 11.0;
  32. self.frame = CGRectMake(-200, 0, 145, 90);
  33. self.image = [UIImage animatedImageNamed:@"sy" duration:time];
  34. break;
  35. }
  36. }
复制代码

2、移动的鱼

提供的图片都是头朝左的(见上面的动图),所以从左往右游的话图片需要进行镜像反转

对于鱼是否可以垂钓是用通知进行传递信息的,可垂钓、不可垂钓两种状态

可垂钓:鱼钩沉到鱼塘时受到垂钓通知(将鱼钩底部的坐标传过来),现在鱼可以垂钓,当根据上钩概率等因素判断鱼上钩后,对鱼进行旋转,然后执行上钩动画。动画结束后执行代理。

  1. //初始化可以垂钓的鱼
  2. - (instancetype)initCanCatchFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir{
  3. if (self = [super init]){
  4. self.direction = dir;
  5. [self initViewWithType:type andDuration:1];
  6. if (dir == FishModelImageViewFromLeft){//从左往右,默认所有的鱼都是从右往左
  7. self.transform = CGAffineTransformMakeScale(-1, 1); //镜像
  8. }
  9. [self initFishView];
  10. }
  11. return self;
  12. }
  13. #pragma mark - 可以垂钓的鱼(计时器)
  14. - (void)initFishView{
  15. //接收可以垂钓的通知
  16. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationCanCatch:) name:NotificationFishHookStop object:nil];
  17. //接收不可垂钓的通知
  18. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationCannotCatch) name:NotificationFishHookMove object:nil];
  19. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeTimer) name:NotificationRemoveFishModelTimer object:nil];
  20. //创建计时器
  21. _linkTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(fishMove)];
  22. //启动这个link(加入到线程池)
  23. [_linkTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
  24. _offsetX = ScreenWidth;
  25. _offsetY = 100;
  26. _fishWidth = self.frame.size.width;
  27. //Y可变高度范围
  28. _randomRange = (int) (YuTangHeight - self.frame.size.height - OffSetYRange);
  29. self.speed = (ScreenWidth + _fishWidth)/self.duration;//游动速度
  30. self.changeX = self.speed/60.0;//计时器每秒60次
  31. DLog(@"鱼游动的速度:%f,每次位移:%f", self.speed,self.changeX);
  32. }
复制代码

鱼移动动画和上钩动画

  1. - (void)fishMove{
  2. if (self.direction == FishModelImageViewFromLeft){//从左至右
  3. if (_offsetX > ScreenWidth + _fishWidth){
  4. _offsetY = arc4random()%_randomRange + OffSetYRange;
  5. _offsetX = - _fishWidth - _offsetY;
  6. }
  7. _offsetX+=self.changeX;
  8. self.frame = [self resetFrameOrigin:CGPointMake(_offsetX, _offsetY)];
  9. if ([self fishCanBeCatchedWithOffsetX:_offsetX + _fishWidth]){
  10. NSLog(@"钓到从左到右的鱼了:%ld",(long)self.fishType);
  11. CGAffineTransform transform = CGAffineTransformIdentity;
  12. transform = CGAffineTransformScale(transform, -1, 1);//镜像
  13. transform = CGAffineTransformRotate(transform, M_PI_2);//旋转90度
  14. self.transform = transform;
  15. self.frame = [self resetFrameOrigin:CGPointMake(ScreenWidth*2, 0)];
  16. [self fishCatchedMoveUpWithOffsetX:_offsetX + _fishWidth];
  17. _offsetX = ScreenWidth + _fishWidth + 1;//重置起点
  18. _linkTimer.paused = YES;//计时器暂停
  19. }
  20. }else {//从右到左
  21. if (_offsetX < -_fishWidth){
  22. _offsetY = arc4random()%_randomRange + OffSetYRange;
  23. _offsetX = ScreenWidth + _offsetY;
  24. }
  25. _offsetX-=self.changeX;
  26. self.frame = [self resetFrameOrigin:CGPointMake(_offsetX, _offsetY)];
  27. if ([self fishCanBeCatchedWithOffsetX:_offsetX]){
  28. NSLog(@"钓到从右到左的鱼了:%ld",(long)self.fishType);
  29. self.transform = CGAffineTransformMakeRotation(M_PI_2);
  30. self.frame = [self resetFrameOrigin:CGPointMake(ScreenWidth*2, 0)];
  31. [self fishCatchedMoveUpWithOffsetX:_offsetX];
  32. _offsetX = -_fishWidth-1;//重置起点
  33. _linkTimer.paused = YES;//计时器暂停
  34. }
  35. }
  36. }
复制代码

鱼上钩的概率和赢得的金币个数

  1. //鱼是否可以被钓上来(根据概率计算)
  2. - (BOOL)fishCanBeCatchedWithOffsetX:(CGFloat)offsetX{
  3. if (!self.isCanCatch) return NO;
  4. if (fabs(offsetX - self.hookX) > self.changeX/2.0) return NO; //判断是否到达了可以垂钓的点
  5. int random = arc4random()%100; //[0,99]
  6. DLog(@"random:%d", random);
  7. switch (self.fishType) {
  8. case FishModelImageViewTypeXHY://小黄鱼 80% 金币2
  9. if (random < 80){
  10. self.moneyCount = 2;
  11. return YES;
  12. }
  13. break;
  14. case FishModelImageViewTypeSBY://石斑鱼 50% 金币5
  15. if (random < 50) {
  16. self.moneyCount = 5;
  17. return YES;
  18. }
  19. break;
  20. case FishModelImageViewTypeHSY://红杉鱼 30% 金币10
  21. if (random < 30) {
  22. self.moneyCount = 10;
  23. return YES;
  24. }
  25. break;
  26. case FishModelImageViewTypeBWY://斑纹鱼 15% 金币20
  27. if (random < 15) {
  28. self.moneyCount = 20;
  29. return YES;
  30. }
  31. break;
  32. case FishModelImageViewTypeSHY://珊瑚鱼 5% 金币50
  33. if (random < 5) {
  34. self.moneyCount = 50;
  35. return YES;
  36. }
  37. break;
  38. case FishModelImageViewTypeSY://鲨鱼 1% 金币100
  39. if (random < 1) {
  40. self.moneyCount = 100;
  41. return YES;
  42. }
  43. break;
  44. }
  45. self.moneyCount = 0;
  46. return NO;
  47. }
复制代码

3.被钓到的鱼

初始化被钓到的鱼方法

  1. //初始化钓到的小鱼
  2. - (instancetype)initCatchedFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir{
  3. if (self = [super init]){
  4. self.direction = dir;
  5. [self initViewWithType:type andDuration:0.5];
  6. //重制x,y坐标, 30为鱼钩的宽度,85为鱼钩的长度
  7. self.x = (30 - self.width)/2.0;
  8. self.y = 85 - 6;
  9. if (dir == FishModelImageViewFromLeft){//从左往右,默认所有的鱼都是从右往左
  10. CGAffineTransform transform = CGAffineTransformIdentity;
  11. transform = CGAffineTransformScale(transform, -1, 1);//镜像
  12. transform = CGAffineTransformRotate(transform, M_PI_2);//旋转90度
  13. self.transform = transform;
  14. }else {
  15. self.transform = CGAffineTransformMakeRotation(M_PI_2);
  16. }
  17. }
  18. return self;
  19. }
复制代码

当鱼被抓到后,执行上钩动画

  1. //鱼被抓到后往上游
  2. - (void)fishCatchedMoveUpWithOffsetX:(CGFloat) offsetX{
  3. //钩沉到鱼塘的高度为45
  4. //位移动画
  5. CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"position"];
  6. ani.duration = 0.7;
  7. if (self.fishType == FishModelImageViewTypeSY){//鲨鱼由于太长,所以不进行上游动画了
  8. ani.fromValue = [NSValue valueWithCGPoint:CGPointMake(offsetX,45 + _fishWidth/2.0)];
  9. ani.toValue = [NSValue valueWithCGPoint:CGPointMake(_hookX, 45 + _fishWidth/2.0)];
  10. }else {
  11. ani.fromValue = [NSValue valueWithCGPoint:CGPointMake(offsetX, (_offsetY < 60) ? 45 + _fishWidth/2.0 : _offsetY)];//离钩子近的话则不进行动画
  12. ani.toValue = [NSValue valueWithCGPoint:CGPointMake(_hookX, 45 + _fishWidth/2.0)];
  13. }
  14. ani.delegate = self;
  15. //设置这两句动画结束会停止在结束位置
  16. [ani setValue:kFishCatchedMoveUpValue forKey:kFishCatchedMoveUpKey];
  17. [self.layer addAnimation:ani forKey:kFishCatchedMoveUpKey];
  18. }
复制代码

鱼上游动画结束后将翻转的鱼复位,然后执行代理将钓到的鱼通过代理传递出去

  1. #pragma mark - CAAnimationDelegate
  2. - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
  3. if (flag){
  4. if ([[anim valueForKey:kFishCatchedMoveUpKey] isEqualToString:kFishCatchedMoveUpValue]){//鱼上游
  5. if (self.direction == FishModelImageViewFromLeft){
  6. CGAffineTransform transform = CGAffineTransformIdentity;
  7. transform = CGAffineTransformScale(transform, -1, 1);//镜像
  8. transform = CGAffineTransformRotate(transform, 0);//旋转90度
  9. self.transform = transform;
  10. }else {
  11. self.transform = CGAffineTransformMakeRotation(0);
  12. }
  13. if ([self.delegate respondsToSelector:@selector(catchTheFishWithType:andDirection:andWinCount:)]){
  14. [self.delegate catchTheFishWithType:self.fishType andDirection:self.direction andWinCount:self.moneyCount];
  15. }
  16. }
  17. }
  18. }
复制代码

钓鱼View

这是实现界面了,本来是写在VC里的,后来发现也能提取出来,所有就提取出来了,在调用时非常简单,像正常View一样初始化后添加到主View上即可,在viewDidDisappear中讲资源释放掉即可。

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. _fishView = [[FishingView alloc] initWithFrame:self.view.bounds];
  4. [self.view addSubview:_fishView];
  5. }
  6. - (void)viewDidDisappear:(BOOL)animated{
  7. [super viewWillDisappear:animated];
  8. [_fishView removeFishViewResource];
  9. }
复制代码

1.初始化鱼钩

初始化鱼钩

讲鱼钩摆动的角度通过代理传到本界面

  1. #pragma mark - 鱼钩
  2. - (void)initHookView{
  3. _fishHookView = [[FishHookView alloc] initWithFrame:CGRectMake((ScreenWidth - 30)/2.0, 5, 30, 85)];
  4. __weak typeof (self) weakSelf = self;
  5. _fishHookView.angleBlock = ^(CGFloat angle) {
  6. weakSelf.angle = angle;
  7. };
  8. [self addSubview:_fishHookView];
  9. UIImageView *yuGanImageView = [[UIImageView alloc] initWithFrame:CGRectMake(ScreenWidth/2.0 - 2, 0, ScreenWidth/2.0, 50)];
  10. yuGanImageView.image = [UIImage imageNamed:@"fish_gan_tong"];
  11. [self addSubview:yuGanImageView];
  12. }
复制代码

下钩动画:鱼塘增加了点击手势,点击后执行钓鱼动作,暂停鱼钩摆动计时器,下钩动画结束后发送通知高速鱼模块可以上钩了,并将鱼钩的底部中心坐标传递过去,鱼线用CAShapeLayer绘制,并执行strokeEnd动画

  1. //钓鱼动作
  2. - (void)fishBtnAction{
  3. if (self.fishHookState != FishHookStateShake) return; //不是摇摆状态不可出杆
  4. [self.fishHookView hookTimerPause];//暂停鱼钩的计时器
  5. double degree = _angle*180/M_PI;//度数
  6. double rate = tan(_angle);//比列
  7. DLog(@"degree:%f---rate:%f",degree,rate);
  8. //计算出来线终点x的位置 , 钩到水里的深度不变,即y是固定的
  9. _lineOffsetX = ScreenWidth/2.0 - (FishLineHeigth)*rate;
  10. //钩子底部xy值
  11. _hookBottomX = ScreenWidth/2.0 - (FishLineHeigth + FishHookHeight)*rate;
  12. _hookBottomY = FishLineHeigth + FishHookHeight;
  13. //动画时间
  14. double aniDuration = [self hookOutOfRiver] ? 0.5 : 1;
  15. //绘制路径
  16. UIBezierPath *path = [UIBezierPath bezierPath];
  17. [path moveToPoint:CGPointMake(ScreenWidth/2.0 ,5)];
  18. [path addLineToPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];
  19. //图形设置
  20. _linePathLayer = [CAShapeLayer layer];
  21. _linePathLayer.frame = self.bounds;
  22. _linePathLayer.path = path.CGPath;
  23. _linePathLayer.strokeColor = [HEXCOLOR(0x9e664a) CGColor];
  24. _linePathLayer.fillColor = nil;
  25. _linePathLayer.lineWidth = 3.0f;
  26. _linePathLayer.lineJoin = kCALineJoinBevel;
  27. [self.layer addSublayer:_linePathLayer];
  28. //下钩动画
  29. CAKeyframeAnimation *ani = [CAKeyframeAnimation animationWithKeyPath:@"strokeEnd"];
  30. ani.duration = aniDuration;
  31. ani.values = @[@0,@0.8,@1];
  32. ani.keyTimes = @[@0,@0.6,@1];
  33. ani.delegate = self;
  34. [ani setValue:kLineDownAnimationValue forKey:kLineDownAnimationKey];
  35. [_linePathLayer addAnimation:ani forKey:kLineDownAnimationKey];
  36. //位移动画
  37. _hookAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
  38. //移动路径
  39. CGFloat tempOffsetX = ScreenWidth/2.0 - (FishLineHeigth*0.8)*rate;
  40. NSValue *p1 = [NSValue valueWithCGPoint:CGPointMake(ScreenWidth/2.0 ,5)];
  41. NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(tempOffsetX, FishLineHeigth*0.8)];
  42. NSValue *p3 = [NSValue valueWithCGPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];
  43. _hookAnimation.duration = aniDuration;
  44. _hookAnimation.values = @[p1,p2,p3];
  45. _hookAnimation.keyTimes = @[@0,@0.7,@1];//动画分段时间
  46. //设置这两句动画结束会停止在结束位置
  47. _hookAnimation.removedOnCompletion = NO;
  48. _hookAnimation.fillMode=kCAFillModeForwards;
  49. [_fishHookView.layer addAnimation:_hookAnimation forKey:@"goukey"];
  50. }
复制代码

钓鱼动作:下钩动画结束后计时器打开,执行此方法;倒计时为最后一秒时鱼不可上钩(鱼上钩动画0.7s,要留上钩动画的时间);计时器为0时发送不可垂钓通知告诉鱼模块不可上钩了,并执行上钩动画。

  1. //钩子停在底部
  2. - (void)hookStop:(NSTimer *)timer{
  3. _stopDuration-=1;
  4. //最后一秒不可上钩
  5. if (_stopDuration == 1){
  6. //发送不可垂钓的通知
  7. self.fishHookState = FishHookStateUp;
  8. [[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookMove object:nil];
  9. }
  10. if (_stopDuration <= 0){
  11. //关闭计时器
  12. [timer setFireDate:[NSDate distantFuture]];
  13. UIBezierPath *path = [UIBezierPath bezierPath];
  14. [path moveToPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];
  15. [path addLineToPoint:CGPointMake(ScreenWidth/2.0 ,5)];
  16. _linePathLayer.path = path.CGPath;
  17. //动画时间
  18. double aniDuration = [self hookOutOfRiver] ? 0.5 : 1;
  19. //上钩
  20. CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
  21. ani.duration = aniDuration;
  22. ani.fromValue = [NSNumber numberWithFloat:0];
  23. ani.toValue = [NSNumber numberWithFloat:1];
  24. ani.delegate = self;
  25. ani.removedOnCompletion = NO;
  26. ani.fillMode=kCAFillModeForwards;
  27. [ani setValue:kLineUpAnimationValue forKey:kLineUpAnimationKey];
  28. [_linePathLayer addAnimation:ani forKey:kLineUpAnimationKey];
  29. [_fishHookView.layer removeAllAnimations];
  30. NSValue *p1 = [NSValue valueWithCGPoint:CGPointMake(ScreenWidth/2.0 ,5)];
  31. NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(_lineOffsetX, FishLineHeigth)];
  32. _hookAnimation.duration = aniDuration;
  33. _hookAnimation.values = @[p2,p1];
  34. _hookAnimation.keyTimes = @[@0,@1];
  35. [_fishHookView.layer addAnimation:_hookAnimation forKey:@"goukey"];
  36. }
  37. }
复制代码

金币动画&加分动画

下钩动画开始,总金币减少10个

上钩动画开始,发送不可垂钓通知,鱼钩状态为上钩状态

如果有捉到鱼(根据鱼模块代理是否执行判断是否捉到),执行金币动画和加分动画

下钩动画结束,发送可以垂钓的通知给鱼模块,并将鱼钩坐标传递过去,开启上钩的计时器

上钩动画结束,更改鱼钩状态,移除一些View,鱼钩继续摆动

  1. #pragma mark - CAAnimationDelegate 动画代理
  2. //动画开始
  3. - (void)animationDidStart:(CAAnimation *)anim{
  4. //下钩动画开始
  5. if ([[anim valueForKey:kLineDownAnimationKey] isEqualToString:kLineDownAnimationValue]){
  6. self.fishHookState = FishHookStateDown;//下钩状态
  7. //钱数
  8. self.moneyLabel.text = [NSString stringWithFormat:@"%d", _totalMoney-=10];
  9. self.winMoney = 0;
  10. }else if ([[anim valueForKey:kLineUpAnimationKey] isEqualToString:kLineUpAnimationValue]){//上钩动画开始
  11. self.fishHookState = FishHookStateUp;//上钩状态
  12. [[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookMove object:nil];
  13. }
  14. if (self.isCatched){//钓到鱼后落金币
  15. HHShootButton *button = [[HHShootButton alloc] initWithFrame:CGRectMake(_lineOffsetX, 0, 10, 10) andEndPoint:CGPointMake(10, 200)];
  16. button.setting.iconImage = [UIImage imageNamed:@"coin"];
  17. button.setting.animationType = ShootButtonAnimationTypeLine;
  18. [self.bgImageView addSubview:button];
  19. [self bringSubviewToFront:button];
  20. [button startAnimation];
  21. HHWinMoneyLabel *winLabel = [[HHWinMoneyLabel alloc] initWithFrame:CGRectMake(_lineOffsetX - 100/2, ScreenFullHeight - FishSeaHeight, 100, 30)];
  22. winLabel.text = [NSString stringWithFormat:@"+%d",_winMoney];
  23. [self addSubview:winLabel];
  24. self.isCatched = !self.isCatched;
  25. //金币总数
  26. self.moneyLabel.text = [NSString stringWithFormat:@"%d", _totalMoney+=self.winMoney];
  27. }
  28. }
  29. //动画结束
  30. - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
  31. if (flag){
  32. if ([[anim valueForKey:kLineDownAnimationKey] isEqualToString:kLineDownAnimationValue]){//下钩动画结束
  33. self.fishHookState = FishHookStateStop;//垂钓状态
  34. //钩的位置
  35. NSDictionary *dic = @{@"offsetX":[NSString stringWithFormat:@"%.2f",_hookBottomX],@"offsetY":[NSString stringWithFormat:@"%.2f",_hookBottomY]};
  36. //发送可以垂钓的通知,钩的位置传过去
  37. [[NSNotificationCenter defaultCenter] postNotificationName:NotificationFishHookStop object:nil userInfo:dic];
  38. _stopDuration = [self hookOutOfRiver] ? 1 : arc4random()%3 + 3; //默认时间[3,5),抛到岸上1s
  39. //开启上钩定时器
  40. [_fishTimer setFireDate:[NSDate distantPast]];
  41. }else if ([[anim valueForKey:kLineUpAnimationKey] isEqualToString:kLineUpAnimationValue]){//上钩动画结束
  42. self.fishHookState = FishHookStateShake;//摇摆状态
  43. [_linePathLayer removeFromSuperlayer];
  44. [_fishHookView hoolTimerGoOn];//鱼钩计时器继续
  45. _catchedHeight = 0;
  46. //移除钓上来的鱼
  47. [self removeTheCatchedFishes];
  48. }
  49. }
  50. }
复制代码

鱼模块的代理方法

创建一个被钓到的鱼,加在鱼钩上,这样便可和鱼钩一起执行上钩动画了

  1. #pragma mark - FishModelImageViewDelegate 钓到鱼后的代理
  2. - (void)catchTheFishWithType:(FishModelImageViewType)type andDirection:(FishModelImageViewDirection)dir andWinCount:(int)count{
  3. self.isCatched = YES;
  4. FishModelImageView *fishImageView = [[FishModelImageView alloc] initCatchedFishWithType:type andDirection:dir];
  5. [self.fishHookView addSubview:fishImageView];
  6. fishImageView.y = fishImageView.y + _catchedHeight;
  7. _catchedHeight += 8;//每钓到一个y坐标往下移
  8. //赢得钱数
  9. self.winMoney += count;
  10. }
复制代码

2.初始化鱼塘

简单的创建鱼背景并添加点击手势

3.初始化鱼

通过for循环可以创建出多个某种鱼

  1. //小黄鱼
  2. for (int i = 0; i < 8; i++){
  3. FishModelImageView *model1 = [[FishModelImageView alloc] initCanCatchFishWithType:FishModelImageViewTypeXHY andDirection: (i%2 == 0) ? FishModelImageViewFromRight : FishModelImageViewFromLeft];
  4. model1.delegate = self;
  5. [self.bgImageView addSubview:model1];
  6. }
复制代码

4.资源移除

由于计时器不销毁会造成循环引用,导致内存泄漏,所以必须手动移除他,还有动画如果执行了代理,并且设置了结束后停留在结束位置,也会得不到释放,所以都要手动释放资源

  1. - (void)removeFishViewResource{
  2. //解决鱼钩上钩动画循环引用的问题
  3. _linePathLayer = nil;
  4. //钓鱼计时器关闭
  5. [_fishTimer invalidate];
  6. _fishTimer = nil;
  7. //释放鱼钩的计时器
  8. [self.fishHookView hoolTimerInvalidate];
  9. //发送通知释放小鱼资源
  10. [[NSNotificationCenter defaultCenter] postNotificationName:NotificationRemoveFishModelTimer object:nil];
  11. }
复制代码

总结

至此,本游戏已经完成了,写的比较多,也比较乱,有什么不好的地方欢迎批评指正,希望对大伙有所帮助吧,本demo地址【https://github.com/Ccalary/FishingGame】



回复

使用道具 举报