iOS 小游戏项目——你话我猜升级版

级别: ★☆☆☆☆
标签:「iOS」「小游戏项目」「你话我猜」
作者: MrLiuQ
审校: QiShare团队

前言:最近公司部门在组织团建,需要准备两个团建小游戏:
分别是“数字速算升级版”和“你话我猜升级版”。

小编琢磨了一下,发现这个两个小项目很适合iOS入门学习,故这篇文章诞生了。
本篇将介绍iOS 小游戏项目——你话我猜升级版。
希望通过这篇文章,帮助对iOS感兴趣的同学快速入门iOS。

我们先来看看效果图:

效果图

一、项目需求:

  1. UI层面:
    比较基础,上面三个Label显示数据(分别是:错误数、倒计时、正确数),中间的一个大Label显示所猜词条,下面三个Button分别对应(答错、开始/复位、答对)。

图解:


UI
  1. 逻辑层面:

    • 点击对/错按钮,对应的Label的数字要+1
    • 做一个计时器,游戏时长定为300秒,时间到0时,结束游戏,对/错按钮禁止点击。
    • 点击开始、对/错按钮,中间的词条都需要更新。
    • 点击开始按钮,出现一个弹窗,点击确定,开始倒计时。
  2. 词库搭建:

    • 词库难度分为5个等级,等级越高,抽到的概率越小。
    • 词库去重处理,抽到的词条不再显示。
    • 概率算法。

二、实现思路:

1. UI层面:

  • 方式一:storyboard(拖控件、加约束)。
  • 方式二:纯代码。

项目中,我选择的storyboard。独立开发时,使用storyboard比较高效。

@property (weak, nonatomic) IBOutlet UILabel *wordLabel;//!< 猜题Label
@property (weak, nonatomic) IBOutlet UILabel *secondsLabel;//!< 计时Label
@property (weak, nonatomic) IBOutlet UILabel *correctLabel;//!< 成功计数Label
@property (weak, nonatomic) IBOutlet UILabel *wrongLabel;//!< 失败计数Label
@property (weak, nonatomic) IBOutlet UIButton *correctButton;//!< 成功按钮
@property (weak, nonatomic) IBOutlet UIButton *wrongButton;//!< 失败按钮
@property (weak, nonatomic) IBOutlet UIButton *startButton;//!< 开始按钮

2. 业务逻辑:

  • 所要保存的属性:
@property (nonatomic, assign) NSUInteger seconds;//!< 剩余时间
@property (nonatomic, assign) NSInteger correctCount;//!< 答对题数
@property (nonatomic, assign) NSInteger wrongCount;//!< 答错题数

@property (nonatomic, strong) QiGuessWords *guessWords;//!< 词条(题目)
@property (nonatomic, strong) NSTimer *timer;//!< 计时器
  • 开始按钮业务逻辑:
//! 开始按钮点击事件
- (IBAction)startButtonClicked:(UIButton *)sender {
    
    NSString *message = [NSString stringWithFormat:@"确定要 %@ 吗?", sender.currentTitle];
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
    UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:sender.currentTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        sender.selected = !sender.selected;
    
        self.correctButton.enabled = !self.correctButton.enabled;
        self.wrongButton.enabled = !self.wrongButton.enabled;
        
        if (sender.selected) {
            self.wordLabel.text = self.guessWords.randomWord;
            [self startTimer];
        } else {
            [self resetElements];
        }
    }];
    [alertController addAction:cancelAction];
    [alertController addAction:confirmAction];
    
    [self.navigationController presentViewController:alertController animated:YES completion:nil];
}
  • 成功/失败按钮业务逻辑:
//! 成功按钮点击事件
- (IBAction)correctButtonClicked:(id)sender {
    
    _correctLabel.text = [NSString stringWithFormat:@"%li",(long)++_correctCount];
    _wordLabel.text = _guessWords.randomWord;
}

//! 失败按钮点击事件
- (IBAction)wrongButtonClicked:(id)sender {
    
    _wrongLabel.text = [NSString stringWithFormat:@"%li",(long)++_wrongCount];
    _wordLabel.text = _guessWords.randomWord;
}
  • 定时器相关代码:
#pragma mark - Private functions

- (void)startTimer {
    
    [self stopTimer];
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(countDown) userInfo:nil repeats:YES];
}

- (void)stopTimer {
    
    [_timer invalidate];
    _timer = nil;
}

- (void)countDown {

    _secondsLabel.text = [NSString stringWithFormat:@"%li", (long)--_seconds];
    
    if (_seconds <= 0) {
        [self stopTimer];
        _correctButton.enabled = NO;
        _wrongButton.enabled = NO;
    }
    else if (_seconds < 30) {
        _secondsLabel.textColor = [UIColor redColor];
    }
}
  • 重置元素逻辑:
- (void)resetElements {
    
    _wordLabel.text = @"";
    
    _seconds = 300;
    _wrongCount = 0;
    _correctCount = 0;
    _secondsLabel.text = [NSString stringWithFormat:@"%li", (long)_seconds];
    _correctLabel.text = [NSString stringWithFormat:@"%li", (long)_correctCount];
    _wrongLabel.text = [NSString stringWithFormat:@"%li", (long)_wrongCount];
    _correctButton.enabled = NO;
    _wrongButton.enabled = NO;
    _startButton.enabled = YES;
    
    [self stopTimer];
}

三、难点:词库工具

词库的难点在于:去重、分级、按概率抽题。

QiGuessWords.h中,

  1. 先定义一个枚举:表示题的难度系数。
typedef NS_ENUM(NSUInteger, QiGuessWordsType) {
    QiGuessWordsTypePrimary,
    QiGuessWordsTypeMiddle,
    QiGuessWordsTypeSenior,
    QiGuessWordsTypeComplex,
    QiGuessWordsTypeCustom
};
  1. 暴露一个属性,直接出随机词条。并暴露了一个方法,直接返回一个指定“难度”、“数量”的随机的词条数组。
@property (nonatomic, copy) NSString *randomWord;

- (NSArray<NSString *> *)randomWordsWithType:(QiGuessWordsType)type count:(NSUInteger)count;

QiGuessWords.m中,

  1. init方法:
- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        
        NSString *primaryWords = @"螃蟹,口红...";
        NSString *middleWords = @"班主任,放风筝...";
        NSString *seniorWords = @"落井下石,七上八下...";
        NSString *complexWords = @"低头思故乡,处处闻啼鸟...";
        NSString *customWords = @"TCP,360杀毒...";
        
        _primaryWords = [primaryWords componentsSeparatedByString:@","].mutableCopy;
        _middleWords = [middleWords componentsSeparatedByString:@","].mutableCopy;
        _seniorWords = [seniorWords componentsSeparatedByString:@","].mutableCopy;
        _complexWords = [complexWords componentsSeparatedByString:@","].mutableCopy;
        _customWords = [customWords componentsSeparatedByString:@","].mutableCopy;
        
        _allWords = @[_primaryWords, _middleWords, _seniorWords, _complexWords, _customWords];
    }
    
    return self;
}
  1. Getter方法:
    注意这里三元表达式的运用。

思想:系统算出一个0~9的随机数,

随机数 词条类型 概率
0,1 primaryWords(初级) 20%
2,3 middleWords(中等) 20%
4,5 seniorWords(高级) 20%
6 complexWords(复杂) 10%
7,8,9 customWords(自定义) 30%
#pragma mark - Getters

- (NSString *)randomWord {
    
    NSUInteger r = arc4random() % 10;
    NSUInteger i = r < 2? 0: r < 4? 1: r < 6? 2: r < 7? 3: 4;
    
    NSMutableArray<NSString *> *words = _allWords[i];
    
    if (words.count == 0) {
        return self.randomWord;
    } //!< 所有数据取完后会造成死循环
    
    NSUInteger index = arc4random() % words.count;
    NSString *randomWord = words[index];
    [words removeObject:randomWord];
    
    return randomWord;
}

最后,游戏工程源码:游戏源码


了解更多iOS及相关新技术,请关注我们的公众号:

关注我们的途径有:
QiShare(简书)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公众号)

推荐文章:
iOS 文件操作简介
iOS 关键帧动画
iOS 编写高质量Objective-C代码(七)
奇舞周刊

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容