成语json_cocos creator实战(2)成语小秀才ts版
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 演示
6 总结
- 项目开始前把结构划分清除,每个模块之间的联系都确定好,可以提高开发效率。
- 第一次仿制游戏,得益于有完整的ui素材,论坛也有类似的帖子介绍。拿这类的小游戏练手对提高学习信心很有帮助。
- 关于随机关卡,跟坛友交流过,随机生成的成语质量无法保证,也就是有可能生僻词混搭,能实现但体验会变差。还有另一种手动标注成语,人工干预的方式去生成关卡。随机关卡思路是有了,但是具体没实现,暂时用不上。什么时候有心情做了再发出来。
- 使用cocos creator2.3.2版本,typescript语言
成语json_cocos creator实战(2)成语小秀才ts版相关推荐
- Autojs4.1.0实战教程---火火小视频极速版功能合集
火火小视频极速版小助手源码链接:https://pan.baidu.com/s/1ZH1ab5wc3Xk4kfd54SpfQQ 提取码:h3in 火山极速版邀请码:279116054 合集地址:htt ...
- Cocos Creator 只谈实战系列—成语游戏篇
相关文章导读: H5线体验链接: H5关卡编辑:http://game.ixuexie.com/idiomEditor H5成语游戏:http://game.ixuexie.com/idiomGame ...
- 微信小程序:智力考验看成语猜古诗句好玩解闷小游戏下载
这是一款猜诗句的一款小程序,特别考脑力 里面拥有低,中,高三种难度 用户通过猜所提供的成语,然后猜出是哪句古诗 当然啦下方也是会有小小提示的,比如古诗作者名字 或者古诗的名字,或者第一个字是什么等等 ...
- Cocos Creator入门实战:桌球小游戏
Cocos Creator入门实战:桌球小游戏 转载请保留原文链接:https://blog.csdn.net/zzx023/article/details/90035153 本篇主要是希望能够通过C ...
- Cocos Creator游戏引擎可以支持鼠标吗_Cocos Creator入门实战:桌球小游戏
本文作者:BigBear 多年游戏行业研发经验 精通Unreal.CocosCreator游戏引擎 参与过多款手游.端游项目的研发 Cocos Creator入门实战:桌球小游戏 本篇主要是希望能够通 ...
- Cocos Creator入门实战:桌球小游戏
本文作者:BigBear 多年游戏行业研发经验 精通Unreal.CocosCreator游戏引擎 参与过多款手游.端游项目的研发 Cocos Creator入门实战:桌球小游戏 本篇主要是希望能够通 ...
- 【小程序源码】看成语猜古诗句好玩解闷小游戏
这是一款猜诗句的一款小程序,特别考脑力 里面拥有低,中,高三种难度 用户通过猜所提供的成语,然后猜出是哪句古诗 当然啦下方也是会有小小提示的,比如古诗作者名字 或者古诗的名字,或者第一个字是什么等等 ...
- 成语答题小程序手机版后台开发
成语答题小程序手机版后台页面开发 成语答题小程序后台设置页面 成语答题小程序固定红包页面 成语答题小程序添加固定红包页面 成语答题小程序添加奖品页面 成语答题小程序删除内容提示页面 成语答题小程序添加 ...
- 视频教程- 桫哥-GOlang基础-Go语言实战:成语查询-Go语言
桫哥-GOlang基础-Go语言实战:成语查询 多年互联网从业经验: 有丰富的的企业网站.手游.APP开发经验: 曾担任上海益盟软件技术股份有限公司项目经理及产品经理: 参与项目有益盟私募工厂.睿妙影 ...
最新文章
- 拯救老电影——详解爱奇艺ZoomAI视频增强技术的应用
- DIV与SPAN之间有什么区别
- 新版中青——青龙羊毛
- Docker可视化工具portainer的安装与使用
- Linux基础命令---cpio
- 深入理解并行编程-分割和同步设计(四)
- 自定义用户控件显示属性分类、描述、默认值
- 【mysql基础知识】解决java连接mysql时将localhost改为本机的ip地址后失败问题
- linux网络串口工具下载,串口调试工具手机版下载
- 服务器网口聚合操作文档,服务器网口聚合怎么操作
- GDI+学习及代码总结之------文本与字体
- 各种泵的图形符号_泵的图形符号
- 三极管分压共射放大电路
- Asp.net学习过程分解(学习路线)
- c++11 std::decay源码剖析
- 路径的单线杠双斜杠区别
- android 一分钟倒计时动画,Android利用属性动画自定义倒计时控件
- Android获取设备ID号
- 百词斩2021高频题汇总 | 备战春招,刷这30题就够了!
- 用计算机怎么打出X,x的平方怎么在电脑上打出来(常见数学符号打法