1 分析

公司要求做h5小游戏之前,想要做的一款类似成语小秀才的小游戏。学了一段时间ccc后回头填坑,尝试仿制一波,刚好发现论坛有套开源的ui素材。花了两天做的demo。做完后发现最难的是生成随机关卡,由于益智类小游戏动辄几百上千关,编写较高质量的关卡随机算法还是很有难度的。

  • 玩法:每个成语最多空缺两个位置,选择对应的词填入成语中,全对即可过关。

成语小秀才

界面分三部分,顶部关卡信息、成语区、选词区。

  • 成语区实际上是9*9的布局,共81的格子,只显示了有词的格子,开启调试模式可以显示所有的格子。
  • 选词区显示成语中空缺的词

调试模式

2 代码结构

2.1 关卡数据结构

正式上线的话,需要开发服务端用于返回关卡数据和保存用户信息。客户端通过http请求获取关卡数据,同时可以上传分数等信息。demo中没有用到服务端,直接本地挂载关卡数据,关卡数据保存在json文件中。

id    ---> 成语id,对应成语库中的成语grids ---> 保存成语中四个字的位置           id ---> 所处的格子位置           space ---> 该字是否为空缺{    "id": 4959,    "grids": [        {            "id": 18,            "space": false        },        {            "id": 27,            "space": true        },        {            "id": 36,            "space": false        },        {            "id": 45,            "space": true        }    ]}

2.2 词条数据对象

用一个类来描述成语词条的基本数据,它对应词库中的一条成语。

// IdiomData.tsexport class IdiomData {    public id: number = 0;          // 词条id    public chars: string = null;    // 完整成语,例如"一马当先"    public pinyin: string = null;   // 词条拼音    public note: string = null;     // 词条出处和释义}

2.3 词条对象

用一个类来描述关卡中出现的每一条成语对象。

  • 记录占用的格子
  • 保存词条数据对象
// Idiom.tsexport class Idiom {    public grids = [];      // 记录占用的格子    public data = null;     // 词条数据    public constructor(grids, idiomdataObj) {        this.data = idiomdataObj;        for (let i = 0; i             this.grids.push(grids[i]);        }    }}

2.4 格子对象

用一个类来描述每个格子的状态和行为。

  • 记录格子id
  • 该格子使用状态
  • 保存完整的成语数据
  • 格子上需要填写的字符
  • 保存被哪些词条对象使用
  • 格子是否为空缺状态
  • 格子填词是否成功,用来判断填词是否成功
  • 当前格子上的词id
// Grid.ts    public gridId: number = 0;                  // 格子id    public isUsed: boolean = false;             // 是否是被使用的格子    public data: string = null;                 // 完整的成语    public char: string = "";                   // 使用的成语字符,单个字    public idioms = [];                         // 反向保存idiom引用    这个是用来保存这个格子被哪些词条引用    public isSpaceGrid: boolean = false;        // 是否是 被 去字 状态    public isSelectMode: boolean = false;       // 是否是 填字 模式    public isSuccess: boolean = false;          // 标注该位置词 是否正确    public currentId: number = 0;               // 记录当前格子上 选词 的id,用来判断回退

    // 重置 格子数据,关卡切换复用    public resetGrid() {        ……    }    // 格子注册监听事件    public addListener(fn: Function, target) {        this.node.on(cc.Node.EventType.TOUCH_END, fn, target);    }    // 格子移除监听事件    public removeListener(fn: Function, target) {        this.node.off(cc.Node.EventType.TOUCH_END, fn, target);    }

2.5 关卡对象

用一个类来描述游戏关卡。关卡对象读取关卡json文件,返回词条对象数组。http请求获取关卡数据可以放在这里。

// Level.ts    public initLevelData() {        let idioms = this._levelData.json["idioms"];        let idiomArr = [];        for (let i = 0; i             let _id = idioms[i].id            let idiomdataObj = new IdiomData(this._jsonData.json[_id]);            // 词条对象 存词条数据,占位            let idiomObj: Idiom = new Idiom(idioms[i].grids, idiomdataObj);            idiomArr.push(idiomObj);        }        return idiomArr;    }

3 界面设计

3.1 节点结构

节点结构

3.2 结算界面

展示词条成语,提供下一关切换按钮。

节点结构

3.3 词条详情界面

展示词条拼音、释义、出处。

词条详情

游戏逻辑

4.1 成语区

  • 制作一个格子的预制体,一次性创建81个格子对象。

格子预制体
// game.ts    private init() {        // 9*9=81块    rows col        for (let index = 0; index 81; index++) {            let node = cc.instantiate(this.piece_prefab);            node.parent = this.qipan;            let tile: Grid = node.getComponent(Grid);            tile.gridId = index;            if (!this.isDebug) {                tile.hide();            }            this.tiles.push(tile);        }        …… ……    }
  • 遍历成语对象后,给指定格子位置添加对应的成语字符并绑定数据。从成语对象中拿到成语,将每个成语都分割成单独的字符存入数组中,将字符循环插入到格子中,如果该位置的格子为去字状态,就把该位置的字符隐藏,并记录空缺字符的id和字符用于初始化选词区。
        // 遍历成语对象         for (let i = 0; i             let chars = idiomArr[i].data.chars;            let grids = idiomArr[i].grids;            // 分割拿到单独的字符            let arr = chars.split("");            for (let j = 0; j                 let gid = grids[j].id;                let space = grids[j].space;                this.tiles[gid].bg.node.active = true;                this.tiles[gid].word.node.active = true;                // 设置 格子信息                this.tiles[gid].gridId = gid;                this.tiles[gid].char = arr[j];                this.tiles[gid].data = chars;                this.tiles[gid].idioms.push(idiomArr[i]);                // 判断是否为 去字 状态,如果是去字,那么就隐藏上面的word                if (space) {                    this.tiles[gid].isSpaceGrid = true;     // 空格子                    if (!this.tiles[gid].isSelectMode) {    // 判断 该空是否为选词                        this.tiles[gid].isSelectMode = true;                        this.tiles[gid].bg.spriteFrame = this.word_tile;                        // 添加点击事件                        this.tiles[gid].addListener(this.onSpaceGridClick, this);                        this.selectWords.push({"id": gid, "char": arr[j]});                    }                } else {                    this.tiles[gid].word.string = arr[j];                    this.tiles[gid].bg.spriteFrame = this.word_normal;                }            }        }
  • 空位置触摸事件。空位置需要实现两个功能:显示字符和回退选词区。
        // 标注空位置上已经有词  回退到选择区        if (grid.isUsed) {            let char = grid.word.string;            this.tiles[gridId].isUsed = false;            this.tiles[gridId].word.string = "";            this.tiles[gridId].isSuccess = false;            this.tiles[gridId].bg.spriteFrame = this.word_selected;            this.currentSelectGrid = gridId;            let currentid = grid.currentId;            for (let i = 0; i this.selectNode.length; i++) {                if (this.selectNode[i].gridId === currentid) {                    this.selectNode[i].node.getComponent(cc.Animation).play("show_word");                }            }        }

4.2 选词区

  • 复用格子预制体,遍历记录空缺字符的selectWordsArr数组里的数据。

选词区
    // 初始化选词区    private initSelectGroup() {        for (let s = 0; s this.selectWords.length; s++) {            let gid = this.selectWords[s].id;            let char = this.selectWords[s].char;

            let node = cc.instantiate(this.piece_prefab);            node.parent = this.selectGroup;            let tile: Grid = node.getComponent(Grid);            tile.gridId = gid;            tile.char = char;            tile.word.string = char;            tile.addListener(this.onSelectGridClick, this);

            this.selectNode.push(tile);        }    }
  • 为了可玩性,随机打乱selectWordsArr里的数据,将选词打乱。
    // 随机打乱selectWords数组    this.selectWords.sort(() => {        return Math.random() > 0.5 ? -1 : 1;    });
  • 选词区触摸回调,判断选词与空缺位置是否匹配,如果匹配将该格子的isSuccess设为true,表明该位置字符正确。这里判断字符正确的依据不能通过比较id的方式,因为有可能选词区出现若干个相同字符的格子,所以选择任意一个都要能达到字符正确的效果。
    // 选词 回调事件 ---> 判断与当前 选中位置 是否相符    private onSelectGridClick(e) {        …… ……        // 隐藏该选词         e.target.getComponent(cc.Animation).play("hide_word");        if (this.tiles[this.currentSelectGrid].isUsed) {            let char = this.tiles[this.currentSelectGrid].word.string;            let nowId = this.tiles[this.currentSelectGrid].currentId;            this.selectNode[nowId].node.active = true;            this.tiles[this.currentSelectGrid].word.string = grid.char;        } else {            this.tiles[this.currentSelectGrid].word.string = grid.char;            this.tiles[this.currentSelectGrid].isUsed = true;            this.tiles[this.currentSelectGrid].bg.spriteFrame = this.word_finished;            this.tiles[this.currentSelectGrid].currentId = gridId;        }        // 判断是否填词成功  通过比较字符的方式 gridId === this.currentSelectGrid        if (char === this.tiles[this.currentSelectGrid].char) {            // 记录该位置词语正确            this.tiles[this.currentSelectGrid].isSuccess = true;        }        this.judgeSuccess(this.currentSelectGrid);    }

4.3 填词逻辑

  • 每填一个词都要判断格子上绑定的成语是否填词成功。找出空位置,然后判断空位置上所有的isSuccess是否为true,满足条件则播放填词动画,查找下一个空缺位置。
    // 判断词条成功    private judgeSuccess(gridId: number) {        …… ……        if (this.tiles[gridId].idioms.length > 0) {            for (let i = 0; i this.tiles[gridId].idioms.length; i++) {                let idiom = this.tiles[gridId].idioms[i];                let flag = true;                spaceArr = [];                // 遍历去字 位置                for (let j in idiom.grids) {                    let id = idiom.grids[j].id;                    if (idiom.grids[j].space === true) {                        spaceArr.push(id);                    }                }                // 判断成功                for (let s = 0; s                     if (this.tiles[spaceArr[s]].isSuccess !== true) {                        flag = false;                    }                }            // 填词动画后,找下一个 空位置            this.findNextSpaceGrid();            …… ……    }

4.4 过关逻辑

  • 寻找下一个空位置。遍历格子对象,筛选isSelectMode为true同时isUsed为false,也就是未被占用的格子。满足条件的格子设置当前选中状态即可。

寻找空位置
        for (let i = 0; i this.tiles.length; i++) {            if (this.tiles[i].isSelectMode && this.tiles[i].isUsed === false) {                // 记录选中的格子位置                this.currentSelectGrid = i;                this.tiles[i].bg.spriteFrame = this.word_selected;                return;            }        }
  • 如果寻找下一个空位置失败,那么说明不存在下一个空格子,就要判断过关。
        // 如果没有下一个格子 就判断是否过关        let flag = true;        for (let sw = 0; sw this.selectWords.length; sw++) {            let id = this.selectWords[sw].id;            if (this.tiles[id].isSuccess !== true) {                flag = false;            }        }        if (flag) {            // 执行过关函数            cc.log("过关");            this.gameSuccess();        }

4.5 动画效果

  • 选词触摸动画,对照小秀才发现当选词点击时只需要把scale执行从1到0的动画就可以达到同样的效果。如果直接将active置false,layout的格子布局就会自动调整可见选词区的位置,导致每个选词的位置出现变化。

选词触摸动画
  • 填词动画。对比发现,填词正确动画拆分看,每个位置的动画都是一样的,将格子进行快速缩放并设置不同的延迟,看上去有跳动的效果。

填词动画
    // 填词成功动画    private fillGrid(grids, arr) {        for (let i = 0; i             let id = grids[i].id;            this.tiles[id].isSelectMode = false;            // 每个字延迟动画            setTimeout(() => {                this.tiles[id].getComponent(cc.Animation).play("word_success");            }, 100 * i);        }        for (let j = 0; j             this.tiles[arr[j]].removeListener(null, this);        }    }

4.6 词条成功逻辑

  • 判断词条中空位置格子是否都正确,如果正确就执行填词动画,查找下一个空位。如果没有空位置且填词全都正确,则说明过关,展示结算界面。

结算界面
  • 词条详情界面,显示拼音、释义、出处。

词条详情

5 演示

demo完整演示

6 总结

  • 项目开始前把结构划分清除,每个模块之间的联系都确定好,可以提高开发效率。
  • 第一次仿制游戏,得益于有完整的ui素材,论坛也有类似的帖子介绍。拿这类的小游戏练手对提高学习信心很有帮助。
  • 关于随机关卡,跟坛友交流过,随机生成的成语质量无法保证,也就是有可能生僻词混搭,能实现但体验会变差。还有另一种手动标注成语,人工干预的方式去生成关卡。随机关卡思路是有了,但是具体没实现,暂时用不上。什么时候有心情做了再发出来。
  • 使用cocos creator2.3.2版本,typescript语言

成语json_cocos creator实战(2)成语小秀才ts版相关推荐

  1. Autojs4.1.0实战教程---火火小视频极速版功能合集

    火火小视频极速版小助手源码链接:https://pan.baidu.com/s/1ZH1ab5wc3Xk4kfd54SpfQQ 提取码:h3in 火山极速版邀请码:279116054 合集地址:htt ...

  2. Cocos Creator 只谈实战系列—成语游戏篇

    相关文章导读: H5线体验链接: H5关卡编辑:http://game.ixuexie.com/idiomEditor H5成语游戏:http://game.ixuexie.com/idiomGame ...

  3. 微信小程序:智力考验看成语猜古诗句好玩解闷小游戏下载

    这是一款猜诗句的一款小程序,特别考脑力 里面拥有低,中,高三种难度 用户通过猜所提供的成语,然后猜出是哪句古诗 当然啦下方也是会有小小提示的,比如古诗作者名字 或者古诗的名字,或者第一个字是什么等等 ...

  4. Cocos Creator入门实战:桌球小游戏

    Cocos Creator入门实战:桌球小游戏 转载请保留原文链接:https://blog.csdn.net/zzx023/article/details/90035153 本篇主要是希望能够通过C ...

  5. Cocos Creator游戏引擎可以支持鼠标吗_Cocos Creator入门实战:桌球小游戏

    本文作者:BigBear 多年游戏行业研发经验 精通Unreal.CocosCreator游戏引擎 参与过多款手游.端游项目的研发 Cocos Creator入门实战:桌球小游戏 本篇主要是希望能够通 ...

  6. ​Cocos Creator入门实战:桌球小游戏

    本文作者:BigBear 多年游戏行业研发经验 精通Unreal.CocosCreator游戏引擎 参与过多款手游.端游项目的研发 Cocos Creator入门实战:桌球小游戏 本篇主要是希望能够通 ...

  7. 【小程序源码】看成语猜古诗句好玩解闷小游戏

    这是一款猜诗句的一款小程序,特别考脑力 里面拥有低,中,高三种难度 用户通过猜所提供的成语,然后猜出是哪句古诗 当然啦下方也是会有小小提示的,比如古诗作者名字 或者古诗的名字,或者第一个字是什么等等 ...

  8. 成语答题小程序手机版后台开发

    成语答题小程序手机版后台页面开发 成语答题小程序后台设置页面 成语答题小程序固定红包页面 成语答题小程序添加固定红包页面 成语答题小程序添加奖品页面 成语答题小程序删除内容提示页面 成语答题小程序添加 ...

  9. 视频教程- 桫哥-GOlang基础-Go语言实战:成语查询-Go语言

    桫哥-GOlang基础-Go语言实战:成语查询 多年互联网从业经验: 有丰富的的企业网站.手游.APP开发经验: 曾担任上海益盟软件技术股份有限公司项目经理及产品经理: 参与项目有益盟私募工厂.睿妙影 ...

最新文章

  1. 拯救老电影——详解爱奇艺ZoomAI视频增强技术的应用
  2. DIV与SPAN之间有什么区别
  3. 新版中青——青龙羊毛
  4. Docker可视化工具portainer的安装与使用
  5. Linux基础命令---cpio
  6. 深入理解并行编程-分割和同步设计(四)
  7. 自定义用户控件显示属性分类、描述、默认值
  8. 【mysql基础知识】解决java连接mysql时将localhost改为本机的ip地址后失败问题
  9. linux网络串口工具下载,串口调试工具手机版下载
  10. 服务器网口聚合操作文档,服务器网口聚合怎么操作
  11. GDI+学习及代码总结之------文本与字体
  12. 各种泵的图形符号_泵的图形符号
  13. 三极管分压共射放大电路
  14. Asp.net学习过程分解(学习路线)
  15. c++11 std::decay源码剖析
  16. 路径的单线杠双斜杠区别
  17. android 一分钟倒计时动画,Android利用属性动画自定义倒计时控件
  18. Android获取设备ID号
  19. 百词斩2021高频题汇总 | 备战春招,刷这30题就够了!
  20. 用计算机怎么打出X,x的平方怎么在电脑上打出来(常见数学符号打法

热门文章

  1. 彻底搞懂JavaScript执行机制
  2. ubuntu下搭建一个数据化处理的开发环境
  3. [摘录]第五章 与奋斗者分享利益
  4. 路由器交换机命令总结
  5. Navicat for Oracle实现连接Oracle
  6. 数学之美笔记(二十)
  7. 如何解决安卓SDK无法下载Package的问题
  8. NSDateFormatter and NSDateComponents
  9. HTTP文件下载原理(OTA 下载 断点续传)
  10. win10操作系统vscode如何配置c++开发环境