天天看點

CocosCreator隻談實戰系列1——成語遊戲編輯器篇CocosCreator隻談實戰系列1——成語遊戲編輯器篇

CocosCreator隻談實戰系列1——成語遊戲編輯器篇

CocosCreator隻談實戰系列1——成語遊戲編輯器篇CocosCreator隻談實戰系列1——成語遊戲編輯器篇
CocosCreator隻談實戰系列1——成語遊戲編輯器篇CocosCreator隻談實戰系列1——成語遊戲編輯器篇

前言

作者從18年4月開始試水微信小遊戲,後面又用休閑小遊戲項目嘗試過國内安卓,頭條小遊戲,facebook等平台。

也是從18年4月第一次使用 cocos creator, 感覺creator的開發體驗不錯,特别是從Unity3D 轉到creator很平滑,

無需看太多說明文檔基本就能上手即用,同時,creator也能滿足休閑遊戲快速産出原型和核心玩法的這一要求。

接下來的一段時間,作者打算将手上的一些項目做成creator系列文章,這些項目每一個核心玩法都有所不同,也使用到了creator引擎的許多方面,希望對creator學習路上的朋友有所幫助。

本篇是系列第一篇,所選項目是今年大火的“成語"類,這個項目打算分兩篇介紹,本篇先說關卡編輯器是如何實作的,下一篇再說遊戲本體實作。 有看官可能得問了,為什麼要先說編輯器?俗話說得好啊 ”工欲善其事必先利其編輯器“, 各位,對于成語這種動則幾千關卡的項目,如果沒有一個可以用起來很友善的編輯器,開發效率就變得很低下了。。 而就實際資料來說:

  1. 這個關卡編輯器使用了一周進行開發
  2. 一個策劃人員一周可輕松制作 300 關卡

好了,廢話不多說,下面進入正題。

一,需求分析與設計

”謀定而後動, 知止而有得“

寫代碼最好的狀态是,當開始敲下第一行代碼的時候,子產品怎麼劃分,子產品間怎麼牽線搭橋,全盤皆成竹于胸

CocosCreator隻談實戰系列1——成語遊戲編輯器篇CocosCreator隻談實戰系列1——成語遊戲編輯器篇

我們先來看看成語關卡編輯器的需求點吧:

  1. 我們應該制作一個編輯區,編輯器是9 X 9的格子布局,共81個格子
  2. 編輯成語的方式,應該是随心所欲的在格子上刷出成語,想怎麼刷就怎麼刷,這樣生成關卡才快,你

把編輯器傳遞給你的策劃同僚,他要面臨的是生成幾千個關卡。。

  1. 換成語和删成語。 除了自由刷成語這個基本操作,應該支援對某個成語進行選中,把它換成其它更合适的

成語,或者直接删掉重新編輯。

  1. 去字功能。 這也是編輯器比較重要的一個功能,因為在遊戲中玩家看到的成語都不是完整的需要填空,而去字

功能就是用來編輯那些字顯示為空格需要讓玩家填空。

​ 一個關卡少說有7,8個成語,如果一個一個的去點就太累了,這裡我們實作了一個‘自動一鍵去字’功能,一鍵去字,如果效果不好,再手動微調即可。

  1. 其它:關卡儲存/關卡加載/成語詞庫配置讀取

需求整理出來了,下一步就是簡單設計和規劃代碼結構:

  1. *詞條基本資料*

我們希望用一個類來描述成語詞條的基本資料,請記住,它對應的僅僅

是成語詞庫裡的一條資料,而不是成語對象。但是很明顯,最終它會被一個成語對象所引用。

詞條基本資料所需要的資料結構很簡單:

//file idiomData.js
export default class IdiomData
{
    constructor(id, chars, pinyin, note)
    {
        this.id=id;                //資料id
        this.chars=chars;       //儲存成語的chars,例如"一馬當先"
        this.pinyin=pinyin;     //成語的注音
        this.note=note;         //成語的出處和釋義
    }
    //...
}
           

​ 2. *成語對象*

​ 我們希望用一個類來描述關卡中編輯出的每一條成語對象

​ 成語對象的成員也很簡單:

  • 記錄自己占用了哪些格子(因為後續的操作例如換詞/删詞都會使用這個資料)
  • 引用成語詞條資料
  • 記錄編輯時,自身的方向(就兩個方向:橫 or 豎)
//file Idiom.js
  export const IdiomGridDir={
      Unknow:0,
      Horizontal:1,  // 橫
      Vertical:2,     //豎
  };
  export default class Idiom
  {
      constructor(grids)
      {
          //占用的格子
          this.grids=[];
          //引用的詞條資料
          this.data=null;
          //方向 橫or豎
          this.girdDir=IdiomGridDir.Unknow;
          for(let i=0;i<grids.length;i  )
          {
              this.pushGrid(grids[i]);
          }
      }
      //...
  }
           
  1. *格子對象*
我們希望用一個類來描述編輯區每個格子的狀态與行為:
           
  • 該格子是否被使用了
  • 該格子上面的成語字元(如果沒被使用,就是” ”)
  • 該格子對應的成語對象(格子上需要記錄成語對象,并且要以數組形式記錄,因為存在兩個成語

交叉字格子)

  • 該格子是否是被共享的格子
  • 該格子是否是被‘去字’的狀态

重點就是以上屬性,當然肯定有一些顯示相關屬性就不一一列出了

//file Grid.js
  cc.Class({
      extends: cc.Component,
      properties: {
          //格子ID
          gridId:{
              default:0,
              visible:false
          },
          
          //是否是被使用的格子
          isUsed:{
              default:false,
              visible:false
          },
      },
  
      // LIFE-CYCLE CALLBACKS:
      onLoad () {
          //其它屬性 ...        
          
          //引用的詞條資料
          this.data=null;
          //使用的成語字元
          this.char="";
          //格子反向儲存idiom引用
          this.idioms=[];
          //是否是共享格
          this.isShareGrid=false;
          //是否是被’去字‘狀态
          this.isSpaceGrid=false;
      },
           
  1. *關卡對象*
我們希望用一個類來描述遊戲關卡對象:
           

關卡對象組織了格子和成語對象,并且負責對刷成語和換成語/删成語/去字/儲存加載等

是以關卡類會包含更多的資料與行為,事實上大部分代碼也集中在關卡類中

關卡對象主要的資料成員為:

  • 一個9X9長度的數組,儲存格子資訊
  • 一個用于存放已經完成編輯的成語對象數組
  • 一個存放編輯過程中選中格子的數組(便于做一些計算,比如确定刷格子的方向,再比如

刷格子的數量是否已經越界了等)

onLoad () {
        this.totalGridsNum=this.gridLineNum * this.gridLineNum;
        //格子數組 最大長度9x9=81
        this.grids=[];
        //儲存已經編輯的成語對象的數組
        this.idioms=[];
        //緩存編輯過程中滑鼠已選中的格子
        this.selectedGrids=[];
           

關卡對象start() 函數

  • 建立9X9的編輯區背景格子
  • 注冊滑鼠事件,處理格子刷取邏輯
  • 注冊鍵盤事件(主要處理CTRL 左鍵,做精确去字選取處理)
start () {
        //建立9X9 編輯區背景格子
        this.createBgGrids();
        //注冊滑鼠事件
        this.registTouchEvent();
        //注冊鍵盤事件
        this.registKeyEvent();
    },
           

關卡對象主要函數如下,原則上我們将每個編輯功能封裝成一個函數,讓他們各司其職:

registTouchEvent:function ()    //處理touch事件
registKeyEvent:function()       //處理鍵盤事件
selectIdiom:function(idiom)        //選中一個成語對象
selectGrid:function(grid)        //選中一個格子
changeSelectedIdiom:function()  //将選中的成語換成其它成語
deleteSelectedIdiom:function()    //删除選中的成語 
autoRemoveChar:function()        //自動給成語去字
saveLevel:function()            //儲存關卡
loadLevel:function()            //加載關卡
           

小結一下,其實成語關卡編輯器核心的類就是上述4個,通過簡單的需求分析,我們作了一個比較清晰的劃分,讓它們各自負責各自的工作。這裡想多提一下,有經驗的老鳥看到這裡肯定會發現一個問題,嚴格說類似 registTouchEvent,registKeyEvent, saveLevel,loadLevel 這樣的行為,不屬于Level對象需要負責的工作,更合理的做法,應該是抽象出一個Editor類,來負責處理事件,儲存加載關卡,串聯編輯流程。确實在大一些的項目,筆者更推薦這樣的設計,本次介紹的項目由于結合開發工期原因,将一些編輯器負責的行為添加到了關卡對象中。

二,編輯流程實作

接下來再簡單介紹下編輯流程實作,由于項目本身偏簡單,就隻抓重點講啦。

*1. 刷詞*

刷詞主要是在 TouchEvent事件中處理,其實如果隻考慮在空白格子刷詞,是非常容易處理的,

這種情況下,程式上隻需要判斷格子選取情況,再從成語庫中取詞填充格子就行了,而格子選取符合要求

的條件無非是:必須是連續選中的4格,格子不能有拐彎,選取範圍不要超過編輯有效範圍。

刷詞稍微複雜一些的情況是某些標明的格子已經被使用了,就出現了多詞共享格的情況,這時候必須把這些字和它

的位置作為附加條件,帶到成語詞庫中進行搜尋。

先看一下刷詞邏輯,在touch_end中判斷是否符合刷詞條件:

this.node.on(cc.Node.EventType.TOUCH_END,(event)=>{
            let point=event.touch.getLocation();
            // 轉到編輯格子區域的 local position
            let localPoint=this.node.convertToNodeSpace(point);
            localPoint.y=this.node.height - localPoint.y;                        
    
            // 做一些是否超出編輯範圍等判斷 。。。
    
            //選取的格子長度為4
            if(this.selectedGrids.length===4)
            {
                //先按格子ID排個序,因為格子可能是從右到左或者從下到上刷的
                this.selectedGrids.sort((a,b)=>{

                    return a.gridId - b.gridId;
                });
                
                //排序OK後,4個格子ID取出來
                let idx0=this.selectedGrids[0].gridId;
                let idx1=this.selectedGrids[1].gridId;
                let idx2=this.selectedGrids[2].gridId;
                let idx3=this.selectedGrids[3].gridId;
                
                //滿足這個條件,說明是橫向連續4格
                if(idx1===idx0 1 && idx2===idx1 1 && idx3===idx2 1)
                {
                    //在選取範圍中擷取被共用的char
                    let shareChars=this.getShareCharsInSelection();
                    //成語庫中查找
                    let idiomData=WordsLib.instance.findIdiomData(shareChars);
                    if(idiomData!==null)
                    {
                        //找到了,生成新idiom對象
                        let idiom=new Idiom(this.selectedGrids);
                        //新成語儲存關卡中
                        this.idioms.push(idiom);
                        this.updateIdiomNumLabel();
                        //更新成語對象資料
                        idiom.setIdiomData(idiomData);
                        //更新格子狀态
                        idiom.updateGrids();
                    }
                    else
                    {
                        //沒有合适的成語可填,做一些編輯狀态的清理工作                        
                    }
                }
                //滿足這個條件,說明是縱向連續4格
                else if(idx1===idx0 this.gridLineNum && idx2==idx1 this.gridLineNum && idx3===idx2 this.gridLineNum)
                {
                    //内部邏輯和上面橫向是一樣的。。。
                }
            }
    
            
           

*2. 自動去字*

CocosCreator隻談實戰系列1——成語遊戲編輯器篇CocosCreator隻談實戰系列1——成語遊戲編輯器篇

實作自動去字主要是為編輯時提供一個快捷功能,一個成語最多自動去掉兩個字

寫了一個removeChar函數來處理

removeChar()
    {
        let ret=this.getSpaceGrids();
        //已經有兩個空格字的話就不繼續處理了
        if(ret.length===2)
            return;

        let removeIdx=[0,1,2,3];
        if(ret.length===1)
        {
            //在剩餘的3格裡面再去掉一個字
            removeIdx.splice(ret[0],1);
            let randIdx=Util.randRangeNumber(0,removeIdx.length-1);
            let gid=removeIdx[randIdx];
            this.grids[gid].forceSetSpace();
        }
        else if(ret.length===0)
        {
            //這是需要去掉兩個字的情況
            let randIdx=Util.randRangeNumber(0,removeIdx.length-1);
            this.grids[randIdx].forceSetSpace();
            removeIdx.splice(randIdx, 1);

            randIdx=Util.randRangeNumber(0,removeIdx.length-1);
            let gid=removeIdx[randIdx];
            this.grids[gid].forceSetSpace();
        }
    }
           

以上是關于刷詞和去字的實作,至于說其它如選詞/換詞/删詞/儲存加載,其實實作都很簡單,成語項目整體來說

很容易實作,這些功能就不啰嗦了。

順便作一下下個項目的預告吧,下個項目準備介紹一個豎版跑酷類遊戲——《峭壁逃亡》

該遊戲參與了頭條小遊戲平台的内測,相關新聞連接配接 http://dy.163.com/v2/article/detail/DT5A07F10546236I.html

這個 項目将重點給大家介紹 跑酷類遊戲無限關卡的生成方法,以及基于dragonBone的角色控制, 敬請期待。

CocosCreator隻談實戰系列1——成語遊戲編輯器篇CocosCreator隻談實戰系列1——成語遊戲編輯器篇
CocosCreator隻談實戰系列1——成語遊戲編輯器篇CocosCreator隻談實戰系列1——成語遊戲編輯器篇

筆者簡介:

肖堯,從事遊戲 前端/後端/3D引擎開發多年

前盛大錦天項目主程,前成都網龍研發負責人,進階架構師

現任休閑遊戲公司H5技術總監,未來将持續專注于基于H5的泛娛樂/教育/傳媒/工具等産品的研究與開發。

微信/QQ : 1611471

繼續閱讀