首页 > 代码库 > 应用程序开发之模仿史上最牛游戏(四)

应用程序开发之模仿史上最牛游戏(四)

一、需要一个新的控制器(ReadyViewController)

-新建控制器,通过push方式到这个新控制器
-给控制器的view制定类(根据图片选择颜色)
-给线起名字
1??给每一个Stage添加手势监听
//2.4为stageView添加手势监听
        [stageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(itemClick:)]];
2??需要控制器来跳转下一个界面(Block的方式)
-把控制器的某一段代码,在StageView中调用
1.让StageView拥有一个Block属性(Block属性只能用copy)
@property (nonatomic, copy) void (^itemClickBlock)(StageInfo *stageInfo);
2.在控制器初始化时,给StageView的block赋值
//3.跳转到ReadyViewController的block方法
    stageView.itemClickBlock = ^(StageInfo *stageInfo) {
        [self performSegueWithIdentifier:@"ready" sender:stageInfo];
    };
3.在用户点击时StageView中调用block
#pragma mark -view点击的手势监听的方法
- (void)itemClick:(UIGestureRecognizer *)tap
{
    if (_itemClickBlock != nil) {
        
        Stage *stageView = (Stage*)tap.view;
        _itemClickBlock(stageView.stageInfo);
    }
}
4.让Ready控制器拥有一个StageModel属性
//关卡信息
@property (nonatomic, strong) StageInfo *stageInfo;
在调转之前调用的方法中传递
#pragma mark - 跳转到ReadyViewController的过程会执行的方法
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    ReadyViewController *ready = segue.destinationViewController;
    ready.stageInfo = (StageInfo*)sender;
}

注明:整个过程实现了将StageInfo从Stage中,一直传递到准备开始游戏的控制器中(ReadyViewController),整个传递过程是

Stage--->StageView--->StageViewController--->ReadyViewController


二、搭建界面
1??搭建界面
-游戏首先会进入到一个准备的界面
界面中的字体会动画的形式跳出来(玩过的都懂的),在动画执行完之后,这个界面消失。可见这个界面并不需要一个专门的控制器来管理,只需要一个View即可,但是我们需要封装好这个View
-第二个界面包含更多的元素
1.返回按钮
2.开始按钮
3.介绍的UIImageView
4.旁边的图标(根据StageInfo来设置)
5.简介的UILable(根据StageInfo显示不同的信息)
6.计分器(有较复杂的逻辑,所以我们专门写一个类来实现它的逻辑)
 
2??设置label
//2.加载关卡的label
    _stageTitle.text = [NSString stringWithFormat:@"Stage %d", _stageInfo.stageNo];
    _stageIntro.text = [_stageInfo.intro stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];
3??过度的View(单独创建一个View)
-设置背景图片
-拖几个label
-控制器把Model传递给View,让View根据Model设置几个label
     1.重写setStageInfo方法
     2.设置数据
-设置label显示时候的动画、声音
一种办法(UIView)
另一种办法(涂层动画)
1.初始化一个定时器
2.给定时器写一个方法来执行动画
3.移除UIView
#pragma mark - 重写setStageInfo对象
- (void)setStageInfo:(StageInfo *)stageInfo
{
    [self setFullView:@"select_bg.jpg"];
    
    _stageInfo = stageInfo;
    
    //0.播放声音
    [[SoundManager sharedSoundManager] playSound:kSoundReady];
    
    //1.设置准备界面的标题
    _stageReadyTitle.text = [NSString stringWithFormat:@"STAGE %d", stageInfo.stageNo];
    
    //2.设置几个label的文字
    NSArray *strings = [stageInfo.title componentsSeparatedByString:@"\\n"];
    int count = _labels.count;
    CGFloat timeCount = 0;
    for (int i = 0; i<count; i++) {
        UILabel *label = _labels[i];
        label.text = strings[i];
        label.hidden = YES;
        CGFloat delay = 0.3 * (i + 1);
        timeCount += delay;
        label.tag = i + 1;
        [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:@selector(showLabel:) userInfo:label repeats:NO];
    }
    //3.设置一个定时器,移除自身的View
    [NSTimer scheduledTimerWithTimeInterval:timeCount target:self selector:@selector(removeView) userInfo:nil repeats:NO];
}
#pragma mark 显示label的动画
- (void)showLabel:(NSTimer *)timer
{
    //1.取出label并且设置不隐藏
    UILabel *label = timer.userInfo;
    label.hidden = NO;
    
    //2.添加动画到涂层
    CAAnimationGroup * group = [self animationCroup];
    [label.layer addAnimation:group forKey:nil];
    
    //3.播放声音
    [[SoundManager sharedSoundManager] playSound:kDropTitle(label.tag)];
}
- (CAAnimationGroup *)animationCroup
{
    //1.伸缩动画
    CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"];
    scale.fromValue = @0;
    scale.toValue = @1;
    //2.位移动画
    CAKeyframeAnimation *trans = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
    trans.values = @[@0, @40, @20];
    //3.添加动画组
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = @[scale, trans];
    return group;
}

#pragma mark 移除View
- (void)removeView
{
    [self removeFromSuperview];
    _removeBlock();
}
4??记分板(业务逻辑较多,直接封装一个View)
-新建一个ReadyScoreView类
-拖一个UIView-UIImageView-6个label
-给labels添加数据
1.添加两个属性给stageInfo(因为这里我们要根据单位和格式来设置记分板中的label)
@property (nonatomic, copy) NSString *format;
@property (nonatomic, copy) NSString *unit;
2.显示数据
-根据积分来显示计分器的位置
1.拖一个
2.根据积分来确定它的位置
- (void)setStageInfo:(StageInfo *)stageInfo
{
    _stageInfo = stageInfo;
    
    //1.确定每一个label显示的值
    CGFloat margin = (stageInfo.max - stageInfo.min)/5;
    int count = _labels.count;
    for (int i = 0; i<count; i++) {
        UILabel *label = _labels[i];
        CGFloat score = stageInfo.max - i * margin;
        label.text = [NSString stringWithFormat:stageInfo.format, score];
    }
    
    //2.显示计分器的位置
    [self setScoreHintCenter:margin];
    
    if (!_stageInfo.stageRecordModel.rank) {
        _scoreHint.hidden = YES;
        return;
    }
    
    //3.执行动画
    self.transform = CGAffineTransformMakeTranslation(0, 200);
    [UIView animateWithDuration:0.3 animations:^{
        //平移动画(CGAffineTransformIdentity表示清空所有的属性)
        self.transform = CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
        CAKeyframeAnimation *blink = [CAKeyframeAnimation animationWithKeyPath:@"hidden"];
        blink.values = @[@0, @1, @0];
        blink.repeatCount = 3;
        [_scoreHint.layer addAnimation:blink forKey:nil];
    }];
}

#pragma mark 设置指示器的位置
- (void)setScoreHintCenter:(CGFloat)margin
{
    CGPoint center = _scoreHint.center;
    // 计算现在积分 和 最差成绩 的差距
    CGFloat delta = _stageInfo.stageRecordModel.score - _stageInfo.min;
    // 计算 每一分 占据 多少宽度
    CGFloat widthPerScore = center.x / (margin * 6);
    // 根据 积分差距  和 每分占据的宽度  来计算总宽度差距
    center.x -=  delta * widthPerScore;
    
    if (center.x <= 0) {
        center.x = 0;
    }
    
    _scoreHint.center = center;
}
-动画Block(在第一个界面的动画结束之后再显示第二个界面)
1.给Block赋值
    _stageReadyView.removeBlock = ^{
        _stageScoreView.stageInfo = _stageInfo;
    };
2.动画
#pragma mark 移除View
- (void)removeView
{
    [self removeFromSuperview];
    _removeBlock();
}
3.如果没有玩过,直接不显示计分器