微信小游戏实战--cocos creator实现wordle游戏(五)
到目前為止我們已經實現了整個游戲的界面以及“空格”和鍵盤按鈕的動畫,接下來完成游戲的基礎玩法流程。
完整代碼
一、初始化游戲
由于在游戲中“空格”的背景和Label會有相應的變化。所以在游戲開始前需要進行一些初始化設置。
1、隨機生成一個5個字母的單詞。
2、重置“空格”的背景和字體顏色:重置為在Cocos Creator編輯器中的初始設計值。
3、設置“空格”只讀:初始狀態下,只有第一排可以輸入。
4、重置鍵盤按鈕的背景顏色:重置為在Cocos Creator編輯器中的初始設計值。
5、設置當前選中的“空格”。
在gamemanager中實現以上功能,代碼如下:
import { words } from './words';//加載詞庫//根據詞庫隨機生成一個單詞setCurrentAnswer () {let sAnswer = words.getRandomword();gamemanager._currentAnswer = sAnswer;}getCurrentAnswer () {return gamemanager._currentAnswer;}//重置中間方塊區域resetMiddleBtn () {let homeNode = gamemanager.instance.homeNode;for (let i = 0; i < 6; i ++){let layoutNode = homeNode.getChildByPath('top/Layout_middle' + (i + 1).toString());for(let j = 0; j < layoutNode.children.length;j++){layoutNode.children[j].getChildByName('Sprite').getComponent(Sprite).spriteFrame = layoutNode.children[j].getChildByName('Sprite').getComponent(Sprite).spriteAtlas.getSpriteFrame('btn_border');layoutNode.children[j].getChildByName('Label').getComponent(Label).string = '';layoutNode.children[j].getChildByName('Label').getComponent(Label).color = new Color(0,0,0);} } }//設置中間方塊區域只讀setMiddleBtnReadOnly () {let homeNode = gamemanager.instance.homeNode;for (let i = 0; i < 6; i ++){let layoutNode = homeNode.getChildByPath('top/Layout_middle' + (i + 1).toString());if (i === 0) {gamemanager.instance.setBtnEnable(layoutNode,true);}else {gamemanager.instance.setBtnEnable(layoutNode,false); } } }setBtnEnable(layoutNode,isEnable){if(layoutNode) {for(let i = 0;i < layoutNode.children.length;i++){layoutNode.children[i].getComponent(Button).enabled = isEnable;}}} //重置鍵盤按鈕resetKeybordeBG () {let homeNode = gamemanager.instance.homeNode;for(let i = 0; i < 3; i++){let layoutNode = homeNode.getChildByPath('top/Layout_bottom' + (i + 1).toString());for(let j = 0; j < layoutNode.children.length; j++){layoutNode.children[j].getComponent(Sprite).spriteFrame = layoutNode.children[j].getComponent(Sprite).spriteAtlas.getSpriteFrame('keybord_bg');}}}//運行“空格”輸入動畫showBtnInputTween (currentBtnNode) {currentBtnNode.getComponent('btn_middle').tweenObjLabelInput.start();}//運行“空格”清除動畫showBtnClearTween (currentBtnNode) {currentBtnNode.getComponent('btn_middle').tweenObjClear.start();}詞庫是在github上找的原版游戲的詞庫。詞庫分為兩部分,正確的單詞大概有2700多個,允許輸入的單詞大概有1萬個。原版游戲的作者只將常用的英文單詞作為正確答案,考慮不通國家的玩家的語言不同又增加了近萬個單詞允許輸入。后續教程寫完以后會將原代碼以及所用到的素材提供下載,這里就不再貼出來了,太長不好顯示。
在home.ts腳本中增加initgame函數來統一管理游戲初始化過程:
start () {this.initgame();}//初始化游戲initgame() {let homeNode = gamemanager.instance.getHomeNode(); const layoutnode = homeNode.getChildByPath('top/Layout_middle1');//設置答案gamemanager.instance.setCurrentAnswer();//重置中間框格背景,字體顏色gamemanager.instance.resetMiddleBtn();//設置中間方格只讀gamemanager.instance.setMiddleBtnReadOnly();//重置鍵盤按鈕顏色gamemanager.instance.resetKeybordeBG(); //選中第一個方框gamemanager.instance.setCurrentLayoutNode(layoutnode); gamemanager.instance.setCurrentBtnNode(layoutnode.children[0]); layoutnode.children[0].getComponent(Button).clickEvents[0].emit(['click']); }?二、提交答案
1、獲取當前所在第幾排,然后將這一排5個字母組合成一個字符串。
2、進行合法性校驗:填寫的字母夠不夠5個,是否為有效單詞。
3、若未通過校驗則顯示提示框,提示框為prefab組件。
在home.ts中增加submitAnswer函數,代碼如下:
submitAnswer () {//提交答案let currentLayoutNode = gamemanager.instance.getCurrentLayoutNode();let err = '';let sAnswer = '';for (let i = 0;i < currentLayoutNode.children.length; i++) {sAnswer += currentLayoutNode.children[i].getChildByName('Label').getComponent(Label).string;}sAnswer = sAnswer.toLowerCase();if (sAnswer.length < 1) {err = '請填一個單詞';}else if (sAnswer.length < 5) {err = '單詞還沒有填完';}else if (words.allwords.indexOf(sAnswer) < 0){//校驗單詞err = '不是有效單詞';}if (err) {//彈出提示框Util.showDiolag('prefab/messageBox',homeNode.parent,(newNode) =>{if(newNode) {newNode.getChildByName('Label').getComponent(Label).string = err;} });}else { //處理答案提交動畫 this.processAnswer();}}?需要先創建Util.ts腳本并在home.ts中引用它,Utile.ts腳本中我將常用的功能進行了一些封裝。如下:
import { _decorator, Component, resources,Prefab,instantiate,Label } from 'cc'; const { ccclass, property } = _decorator;@ccclass('Util') export class Util extends Component {//判斷字符串是否為空static isEmpty (str): boolean {if (str === null ||str === '' ||str === undefined ||str.length === 0) {return true} else {return false}}//動態加載prefab組件static showDiolag(sPath,parentNode,callback){resources.load(sPath,Prefab,(err, prefeb) => {if(err) {console.log(err);}else {let newNode = instantiate(prefeb);if(callback) {callback(newNode);}parentNode.addChild(newNode); }}); }//執行線性等待static async sleep(ms: number) {return new Promise((resolve) => {setTimeout(() => {resolve('');}, ms)});} //獲取字符串最后一個字符static getLastChar(str) : string{if(Util.isEmpty(str)){return '';}else {return str.slice(str.length - 1,str.length);}}//http請求有道翻譯static translate(str,handler){let xhr = new XMLHttpRequest();xhr.timeout = 5000;//超時參數xhr.onreadystatechange = function (data) {if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)) {let response = xhr.responseText;let data = JSON.parse(response);if (handler) {//獲取翻譯結果let sResult = data['translateResult'][0][0]['tgt'];let sUrl = 'https://dict.youdao.com/dictvoice?type=0&audio=' + str; handler(sResult,sUrl);}//data['translateResult'][0][0]['tgt']翻譯結果//data.type 翻譯類型:EN2ZH_CN英譯漢 ZH_CN2EN漢譯英//有道英文語音接口//'https://dict.youdao.com/dictvoice?type=0&audio=' + sResult;//百度中文語音接口//https://fanyi.baidu.com/gettts?lan=zh&text=' + sResult + '&spd=5&source=web';} }//翻譯接口采用有道翻譯let url = 'https://fanyi.youdao.com/translate?&doctype=json&type=EN2ZH_CN&i=' + strxhr.withCredentials = true;xhr.open("GET", url , true);//跨域設置xhr.setRequestHeader('Access-Control-Allow-Origin', '*');xhr.setRequestHeader('Access-Control-Allow-Methods', 'GET, POST');xhr.setRequestHeader('Access-Control-Allow-Headers', 'x-requested-with,content-type,authorization');xhr.send();} }由于typescripte腳本是異步執行方式,在實踐中要順序顯示“方格”的翻轉動畫,就需要實現一個類似Java中的sleep函數功能,讓前一個“空格”開始翻轉后等待100ms再開始旋轉下一個“空格”。之所以要設計一個獲取字符串最后一個字符的功能是因為在Cocos creator編輯器中,Layout和btn_middle等組件我都是將名稱最后一位作為索引,然后在代碼中根據這個索引來判斷是位置?,F在回過頭來重新看這段代碼就會覺得沒有必要,可以根據父節點的.children屬性來遍歷子節點。?
玩家提交的答案通過合法性校驗后根據游戲規則判斷猜測結果并調用“方格”的旋轉動畫,在home.ts中增加processAnswer函數:
將隨機生成的答案轉換為sAnswer數組,在for循環中依次比對玩家輸入的每一個字母,并根據游戲規則改變“方格”的背景。為了方便玩家識別還有哪些字母可使用,增加了alterKeybordebg函數將比對結果同步更新到鍵盤按鈕上。當前這一排提交后就需要將這一排設置為只讀,讓玩家不能再輸入,然后將下一排設置為可以輸入。當玩家猜中單詞時則勝利,游戲結束。當第6排提交以后還未猜中單詞,再獲取下一排時就會獲取到null,則輸了,游戲結束。實現代碼如下:
async processAnswer (){let homeNode = gamemanager.instance.getHomeNode();let isPass = true;let layoutNode_middle = gamemanager.instance.getCurrentLayoutNode();let sAnswer = gamemanager.instance.getCurrentAnswer().split('');//先鎖定這一排gamemanager.instance.setBtnEnable(layoutNode_middle,false); for (let i = 0; i < layoutNode_middle.children.length; i++) {let char = layoutNode_middle.children[i].getChildByName('Label').getComponent(Label).string;char = char.toLowerCase();if (sAnswer.indexOf(char) < 0){//答案中沒有這個字母layoutNode_middle.children[i].getComponent('btn_middle').runRotation('btn_wrong');this.alterKeybordebg(char,'wrong');isPass = false;}else{if (sAnswer[i] != char) {//答案中有這個字母但是順序不對layoutNode_middle.children[i].getComponent('btn_middle').runRotation('btn_partright');this.alterKeybordebg(char,'partright');sAnswer[sAnswer.indexOf(char)] = null; isPass = false;}else{//答案中有這個字母,順序也對layoutNode_middle.children[i].getComponent('btn_middle').runRotation('btn_right');this.alterKeybordebg(char,'right');sAnswer[i] = null;}}await Util.sleep(100); //不執行線性等待,則所有方塊一起翻轉 }if(isPass) {//通過則顯示勝利彈窗和戰績,let sCorrectAnswer = gamemanager.instance.getCurrentAnswer();Util.showDiolag('prefab/resultBox',homeNode.parent,(newNode)=>{if(newNode){newNode.getChildByName('content').getChildByName('title').getComponent(Label).string = '恭喜你答對了!';newNode.getChildByName('content').getChildByName('message').getComponent(Label).string = '答案是:' + sCorrectAnswer; }}); this.readyToNext();}else {//獲取下一排的layoutNodelet char = Util.getLastChar(layoutNode_middle.name);let nextIndex = parseInt(char) + 1;const nextLayoutNode = homeNode.getChildByPath('top/Layout_middle' + nextIndex.toString());if(nextLayoutNode){//獲取到了則設置當前layout節點和btn節點const nextBtnNode = nextLayoutNode.getChildByName(nextLayoutNode.name + '_1');gamemanager.instance.setBtnEnable(nextLayoutNode,true);gamemanager.instance.setCurrentLayoutNode(nextLayoutNode);gamemanager.instance.setCurrentBtnNode(nextBtnNode);//模擬點擊事件nextBtnNode.getComponent(Button).clickEvents[0].emit(['click']);}else{//沒獲取到,則表示已經輸了,顯示正確答案let sCorrectAnswer = gamemanager.instance.getCurrentAnswer() ;Util.showDiolag('prefab/resultBox',homeNode.parent,(newNode)=>{if(newNode){newNode.getChildByName('content').getChildByName('title').getComponent(Label).string = '很遺憾你答錯了!';newNode.getChildByName('content').getChildByName('message').getComponent(Label).string = '答案是:' + sCorrectAnswer; }});this.readyToNext(); }}}alterKeybordebg(sKey,type){let homeNode = gamemanager.instance.getHomeNode(); let key = sKey.toUpperCase();for(let i = 0;i < 3; i++) {let layoutNode = homeNode.getChildByPath('top/Layout_bottom' + (i + 1).toString());for(let j = 0; j < layoutNode.children.length; j++) {if (layoutNode.children[j].getChildByName('Label').getComponent(Label).string === key) {if(type === 'wrong') {//錯誤要判斷之前是否已經更新過了,沒有更新過才更新if ((layoutNode.children[j].getComponent(Sprite).spriteFrame.name === 'keybord_bg')) {layoutNode.children[j].getComponent(Sprite).spriteFrame = layoutNode.children[j].getComponent(Sprite).spriteAtlas.getSpriteFrame('keybord_wrong'); }}else if (type === 'right') {//正確直接更新layoutNode.children[j].getComponent(Sprite).spriteFrame = layoutNode.children[j].getComponent(Sprite).spriteAtlas.getSpriteFrame('keybord_right');}else {//若存在,但是位置不正確的字母,需要判斷是否之前更新過,如果之前更新過則不需要再更新了if (layoutNode.children[j].getComponent(Sprite).spriteFrame.name === 'keybord_bg') {layoutNode.children[j].getComponent(Sprite).spriteFrame = layoutNode.children[j].getComponent(Sprite).spriteAtlas.getSpriteFrame('keybord_partright');}}}}}}上述代碼中調用的this.readyToNext()是將“ENTER”鍵的顯示內容變化成“NEXT”,此時玩家點擊后將開起第二局游戲。為了方便玩家識別,做了背景色的變化。代碼如下:
readyToNext() {//游戲結束時,將當前選中節點置空gamemanager.instance.setCurrentBtnNode(null);gamemanager.instance.setCurrentLayoutNode(null);this.setENTERBtnInfo('NEXT',new Color(17,235,130)); }最后在home.ts中創建onClick函數,此函數將綁定到ENTER鍵的點擊事件上。判斷“ENTER”鍵的顯示內容若為“ENTER”則調用submitAnswer進行答案提交,若為“NEXT”,則還原“ENTER”鍵的初始社會并調用initgame開始下一局游戲。代碼如下:
setENTERBtnInfo(smessage : string,color : Color){//還原ENTER鍵的背景和顯示內容let homeNode = gamemanager.instance.getHomeNode();let ENTERNode = homeNode.getChildByPath('top/Layout_bottom4/ENTER');ENTERNode.getChildByName('Label').getComponent(Label).string = smessage;ENTERNode.getComponent(Sprite).color = color;ENTERNode.angle = 0;}startNextGame() { //開始下一局游戲this.setENTERBtnInfo('ENTER',new Color(255,255,255)); this.initgame(); }onClick (){//ENTER鍵點擊事件let homeNode = gamemanager.instance.getHomeNode();let ENTERNode = homeNode.getChildByPath('top/Layout_bottom4/ENTER');if (ENTERNode.getChildByName('Label').getComponent(Label).string === 'NEXT') {this.startNextGame();}else {this.submitAnswer();}}事件綁定的操作前面已經介紹過,這里不再累述。最終“ENTER”鍵的設置如下:
?三、制作消息提示框和游戲結果信息提示框
在游戲中提示框是很常見的組件,需要制作為prefab在代碼中動態加載,實現組件的重復利用。動態加載的功能已經在Util.ts腳本中封裝好。prefab的制作方法在前面已經介紹過,這里不再累述,只是介紹一下提示框的UI和功能設計。
消息提示框設計如下:
?1、messageBox為一個空節點。
2、Mask為Sprite(單色):大小設置為寬720,高1280,。并且綁定一個button組件,UIOpacity組件。將UIOpacity設置為0(完全透明),button按鈕事件數量設置為0。
?這樣設計的目的是為了讓Mask成為一個透明遮罩,當提示框顯示出來時,用戶點擊Sprite以外的區局也就無法觸發“空格”上的點擊事件。
3、Sprite為Sprite。作為背景圖,添加一個UIOpacity組件,透明度設置為100。
4、Label。主要設置一下字體,顏色。Sprite和Label的大小根據自己喜好自行設定即可。
界面設計完成后,再創建一個messageBox.ts腳本文件綁定到messageBox這個空節點上。messageBox.ts實現的功能也很簡單,如下:
import { _decorator, Component, Node, tween, Sprite, UIOpacity } from 'cc'; const { ccclass, property } = _decorator;@ccclass('messageBox') export class messageBox extends Component {private tweenObj = null;onLoad () {let self = this;this.tweenObj = tween(this.node.getChildByName('Sprite').getComponent(UIOpacity)).to(0.3,{opacity : 255}).delay(1).call(function () {self.node.destroy();});}start () {this.tweenObj.start();} }在加載的時候創建一個動畫,在0.3秒內將Sprite的透明度變化到255,延遲一秒,調用self.node.destroy()將節點銷毀,此時提示框也就消失了。
? ? ? ? ? ? Util.showDiolag('prefab/messageBox',homeNode.parent,(newNode) =>{
? ? ? ? ? ? ? ? if(newNode) {
? ? ? ? ? ? ? ? ? ? newNode.getChildByName('Label').getComponent(Label).string = err;
? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ?
? ? ? ? ? ? });
第一個參數是prefab的路徑和prefab的名稱,prefab是在resources目錄下的文件夾,messageBox是prefab的名稱。homeNode是prefab將要加載到哪個節點下,這里的homeNode也就是home節點。通過回調函數給messageBox中的Label復制,顯示提示消息。最終實現的效果就是彈出一個提示框,提示框有一個透明的變化,由半透明變化到不透明,并且顯示1秒后就自動銷毀,不用玩家再手工關閉。
游戲結果提示框設計如下:
1、resultBox是空節點。寬720,高1280
2、mask和消息提示框prefab中的mask一樣。
3、content是Sprtie,背景為一張黃色的圖片。
4、title是Label,用于顯示游戲結果。
5、message是Label,用于顯示答案。
6、btn是按鈕,點擊后關閉該提示框。
新建resultBox.ts腳本將腳本掛載到resultBox空節點上。代碼如下:
import { _decorator, Component, Node } from 'cc'; const { ccclass, property } = _decorator;@ccclass('resultBox') export class resultBox extends Component {onClick() {this.node.destroy();} }然后將onClick函數綁定到btn按鈕的點擊事件上即可。調用方式:
? ? ? ? ? ? Util.showDiolag('prefab/resultBox',homeNode.parent,(newNode)=>{
? ? ? ? ? ? ? ? if(newNode){
? ? ? ? ? ? ? ? ? ? newNode.getChildByName('content').getChildByName('title').getComponent(Label).string = '恭喜你答對了!';
? ? ? ? ? ? ? ? ? ? newNode.getChildByName('content').getChildByName('message').getComponent(Label).string =?
? ? ? ? ? ? ? ? ? ? '答案是:' + sCorrectAnswer; ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
和messageBox一樣,就不再詳細描述了。
?四、音效播放功能實現:
作為一個游戲來說,音效是不可或缺的元素,沒有聲音的游戲顯得太蒼白了。在Cocos Creator3.x版本中提供了AudioSource組件來實現音效的播放這與2.x版本中不一樣,好在官方在demo示例中提供了完整使用案例。直接拿過來用即可。
新建audioManager.ts腳本,代碼如下:
import { _decorator, Component,AudioClip,AudioSource,assetManager,assert,warn, resources } from 'cc'; const { ccclass, property } = _decorator;@ccclass('audioManager') export class audioManager extends Component {private static _instance: audioManager;private static _audioSource?: AudioSource;//單例模式 static get instance () {if (this._instance) {return this._instance;}this._instance = new audioManager();return this._instance;}soundVolume : number = 1;//初始化函數,在gameRoot調用init (audioSource: AudioSource) {audioManager._audioSource = audioSource;}/*** 播放音效* @param {String} name */playSound(name: string){const audioSource = audioManager._audioSource!;assert(audioSource,'AudioSource not inited!');let path = 'sound/';resources.load(path + name,AudioClip,(err: any,audioClip: AudioClip) =>{if (err) {warn('load audioClip failed: ',err);}audioSource.playOneShot(audioClip,this.soundVolume);});}/*** 播放遠程音效* @param {String} url */playRemote(url: string){const audioSource = audioManager._audioSource!;assert(audioSource,'AudioSource not inited!'); console.log(url);assetManager.loadRemote(url,{ext : '.mp3'},(err, audioClip : AudioClip) => {if (err) {warn('load audioClip failed: ',err);}audioSource.playOneShot(audioClip,this.soundVolume); })}setSoundVolume(volume: number){this.soundVolume = volume;}openSound () {this.setSoundVolume(1);}closeSound () {this.setSoundVolume(0);}start () {// [3]}// update (deltaTime: number) {// // [4]// } }audioManager.ts我是在官方提供的示例中直接復制的,只保留了我需要的playSound()函數。在此基礎上新增了另外一個播放遠程音效的功能playRemote。音頻文件一般使用mp3格式,需要存放在resources文件夾下才能動態加載,為了便于管理,音頻文件都統一存放在resources下的sound文件夾中。
新建gameRoot.ts腳本,掛載到gameRoot節點下。gameRoot在之前已經介紹過,作為場景下的第一個節點,在代碼中注冊為常駐節點并掛載AudioSource組件。代碼如下:
import { _decorator, Component, AudioSource,assert,game } from 'cc'; import { audioManager } from './audioManager';const { ccclass, property } = _decorator;@ccclass('gameRoot') export class gameRoot extends Component {@property(AudioSource)private _audioSource: AudioSource = null;onLoad(): void{const audioSource = this.getComponent(AudioSource);assert(audioSource);this._audioSource = audioSource;//將gameRoot節點注冊為常駐節點game.addPersistRootNode(this.node);audioManager.instance.init(this._audioSource);} }gameRoot節點下掛載audioSource組件:
調用方式:
audioManager.instance.playSound('btn_click');
'btn_click'就是鍵盤按鈕點擊的音頻文件,通過指定音頻文件的名稱就可以進行播放,很方便。
這里要注意一下,在網上搜索音頻文件的時候要確認好是否有版權。不想付費的話就只能選沒有版權的音頻文件(知識付費的年代不好白嫖啊)。另外推薦一個免費的壓縮工具,這個工具是網頁版并且免費的,壓縮比例也比較高。這個是為了后續發布出來的程序包盡量小一點,微信小游戲的發布有限制,首包只能小于4M。每一KB都要斤斤計較。
到此游戲的基本玩法功能都實現完了,調試的時候發現少了一個功能(“DEL”清除鍵的功能還沒有實現)。清除功能也簡單,就是將當前選中的“空格”中的內容清空,并切換一下背景。新建btn_backspace.ts腳本文件,代碼如下:
import { _decorator, Component,Sprite, Node, Label, Button } from 'cc'; import { audioManager } from './audioManager'; import { gamemanager } from './gamemanager'; const { ccclass, property } = _decorator;@ccclass('btn_backspace') export class btn_backspace extends Component {onClick () {audioManager.instance.playSound('btn_click');let layoutNode = gamemanager.instance.getCurrentLayoutNode();let btnNode = gamemanager.instance.getCurrentBtnNode(); //游戲結束時CurrentLayoutNode被置空,再次點擊DEL按鈕就不要操作了if (!layoutNode) {return;} //如果當前選中的空格為空,則選最右邊的空格if (!btnNode) {btnNode = layoutNode.children[layoutNode.children.length - 1];gamemanager.instance.setCurrentBtnNode(btnNode);}this.clearBtnNode(btnNode);}clearBtnNode (btnNode) {btnNode.getChildByName('Sprite').getComponent(Sprite).spriteFrame = btnNode.getChildByName('Sprite').getComponent(Sprite).spriteAtlas.getSpriteFrame('btn_border');btnNode.getChildByName('Label').getComponent(Label).string = ''; gamemanager.instance.showBtnClearTween(btnNode); }}將 btn_backspace.ts掛載到“DEL”按鍵上,并綁定onClick時間:
至此 游戲的基本玩法功能就已經全部實現了,趕緊試玩一下吧。
五、注意事項:
?在VS CODE中修改了代碼以后,一定要切換到Cocos Creator編輯器中。此時,Cocos Creator編輯器的左下方會冒出一個小彈窗顯示出哪些腳本文件被修改了,這就代表Cocos Creator重新加載了這些被修改的腳本文件。然后你在VS CODE中按F5進行調試的時候才是最新的代碼,不切換一下的話你在VS CODE中修改了代碼直接F5運行的話,還是運行的之前的老代碼。這個坑我也是踩了好幾次。
總結
以上是生活随笔為你收集整理的微信小游戏实战--cocos creator实现wordle游戏(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用python分析《大侦探第七季之蔷薇下
- 下一篇: 计算机无法读入内存怎么办,Win10提示