手遊《奧林劈圖》的開發日記(一)

我最近將手遊《奧林劈圖》上線到蘋果商店,了卻了一樁三年的心願,心情也由之前的燥動不安迴歸平靜。現在我真的有時間和一顆平常心去擁抱機器學習和數據挖掘了。

幾年前自己剛開始學習cocos2dx的時候,腦子經常冒出各種各樣的奇怪的遊戲創意,害怕下一分鐘可能會忘卻,就習慣了把它們記錄在有道筆記上。至今翻看那些筆記,可以零星的回憶起這個遊戲創作和開發的軌跡和新路歷程。

[2016年4月19日]

任務目標設計(舊)

添加3根木棒,獲得:

2個L2  (該設計太抽象)

1個Z1 (該設計太抽象)

(其它圖形數量不限)

 

除去3根棍棍兒,獲得:

3個O4 (該設計太抽象)

用給定的棍棍兒, 將圖形分爲4個大小和形狀相同的圖形。

放置所有積木到灰色圖形內,即可過關。

 

任務id:

任務目標:添加%d根木棒,獲得:

* %d個%s

* %d個%s

(其它形狀數量不限)

 

任務驗證函數

任務完成狀態提示:打勾

TaskPopup和TaskStatus的UI根據StageTask的參數配置生成。

複雜困難的任務分步讓玩家完成。

[2016年4月20日]

StageLayer (經典模式)

|----背景層

  |----StatusBar

  |----scrollview

         |----stageN (sp1.csb, sp2.csb, sp3.csb e.g.)

|----task (type=0, pieces=3,award:{wall=3,coin=100})

         |----mapboard (W=100, H=100)

          |----dimension (size = 700x1100 e.g.)

|----fields

|----walls

|----blocks

|----TaskPopup (跟據stageN的配置信息生成)

|----ResultPopup (獎勵根據stageN的配置信息生成)

|----HintPopup (createNode by stageN.csb)

 

任務目標:

通過添加牆,將圖形分成若干個子圖形,然後移動子圖形填充到下方的陰影圖形中。

 

任務完成:

您消耗了6個牆完成本次任務,目前世界最好成績爲使用5個牆。

您的過關獎勵爲: 牆: 6 金幣:100

[返回] [確定]

 

過關獎勵:

恭喜你獲得: 牆: 6 金幣:100

(此處與服務器通信,wall +6, coin +100, usewall 6, 如果好於預設牆數發送:牆的佈局數據)

牆佈局: {stage=sp5, num=6, pos: [{x:1,y:2,d=1},{x:2,y:3,d=0}]}

 

 

[2016年4月22日]

[難]:

面積大

融合度高

規定劈開的圖形數目,大小或形狀

規定使用圍牆數目

[易]:

面積小

離散

原圖形面積大於目標圖形面積 (原圖形允許有剩餘)

 

基本上,可添加圍牆數目決定答案的難度

[2016年4月23日]

1. 經典模式 (暗黑夜空)

2. PVP模式設計,每天10:00 (或整點)發佈一個新副本,30分鐘內完成,牆數少者獲勝。牆數相同時間早者獲勝。前10名有獎品。

bool g2Grid::hasPart()
bool g2Grid::getField()

g2Body -> [covered grids]集合 ->遍歷
Body::isLanded()

g2ShapeModel, g2ShapeClip ==> g2Shape (嵌套結構)

bool g2ShapeClip::isFieldLaid()
{
    int xnum = xNum();
    int ynum = yNum();
    for (int i = 0; i < xnum; i++)
    {
        for (int j = 0; j < ynum; j++)
        {
            auto pt = m_origin + GridPoint(i, j);
            auto grid = getGrid(pt);
            if (!grid || !grid->getField())
                return false;
        }
    }
    return true;
}

 

[2016年4月25日]

1. 環狀圖形允許

2. CShape : g2Mapboard

{

std::<CShape*> m_children;

}

Sharesdk集成

Sqlite加密

主界面設計

關卡選擇界面

關卡背景

iOS內購集成

[2016年4月26日]

一個Zone由關卡選擇器和若干關卡組成。

關卡選擇器中包含若干Button,每個Button是一個關卡的入口。

StagePicker

|--------Button1 {version:1, source:"xxx.csb"}

|--------Button2 {version:2, source:"xxx.csb"}

|--------Button3 {version:2, source:"xxx.csb"}

 

StageLayer1 {wall:3, award:{coin:100,wall:4}}

StageLayer2 {wall:3, award:{coin:100,wall:4}}

StageLayer3 {wall:3, award:{coin:100,wall:4}}

 

 

[2016年4月26日]

服務器與客戶端同步設計

通用格式:

send: req={op:xxx, opdata}&cct=yyy

resp: ret=0&rsp={op:xxx, respdata, cmd:{jsondata}}&cct=yyy

定時請求server端的scounter

通信協議:

1. 第三方鑑權成功後,開始登陸游戲服務器

send: msg={op:login, account:{uid3:124234135,type:QQ,name:氣昏頭}}&cct=0

resp: ret=0&rsp={accountuid: xxx, roleuid:AABB112233}}&cct=0

2. 選擇一個從服務器返回的角色

send: msg={op:selectrole, roleuid:AABB112233}&cct=xxx

resp: ret=0&rsp={roleuid:AABB112233,cct=xxx,sct:yyy}&cct=xxx

3. 請求慢同步 when ccounter (client) < ccounter(server) (服務器數據覆蓋客戶端)

send: msg={op:slowsync, roleuid:AABB112233}&cct=0

resp: ret=0&rsp={roledata:{...},sct:xxx}&cct=0

角色用戶數據:

{

role:{uid=xxx,name=yyy,coin=500,wall=12,zone1Prog=7,accountUid=zzz,ccounter=11,scounter=9},

zone1:{s1:{..},s2:{..},s3:{..},s4:{..},s5:{..},s6:{..},s7:{..}}

}

 

4. 合併本地帳號 (when ccounter=0 on server side) (客戶端數據覆蓋服務器)

send: msg={op:merge, roledata:{...}}&cct=xxx

resp: ret=0&rsp={roleuid:AABB112233,cct=xxx,sct:yyy,cmd:{op:slowsync}&cct=xxx

 

5. 花費金幣500購買當前關卡hint

send: msg={op:buyhint, roleuid:AABB112233, cost:{coin:500}}&cct=xxx

resp: ret=0&&rsp={op:buyhint, coin: 1500, sct:yyy}&cct=xxx

 

6. 使用5個圍牆條,通過關卡 (zone=zone1 sn=57)

send: msg={op:pass, roleuid:AABB112233, stage:{zone:zone1,group:0,sn: s27}, cost:{wall:5}, award:{coin:100, wall:6}, misc:{time=300}}&cct=xxx

resp: ret=0&rsp={op:pass, coin: 1500, wall: 11, sct:yyy}&cct=xxx

 

7. 請求獲得服務器的新事項

send: msg={op:getupdate,roleuid:AABB112233,sct:25}&cct=xxx (獲取服務器中scounter爲25的更新事項)

resp: ret=0&rsp={jsondata},sct:xxx}&cct=xxx

 

說明:

1. ccounter_on_client >= ccounter_on_server

99.99%的時間ccounter_on_client應該大於counter_on_server, 如果相等意味最後一次操作的返回數據未到達客戶端。

 

2. 當scounter_on_client <= scounter_on_server, 服務器有更新的事件需要客戶端獲得。

 

設計目標:

1. 沒有連接服務器的條件下,遊戲可以正常運行。當連接服務器後,客戶端實現與服務器的數據同步。

2. 多客戶端交替玩遊戲不會有嚴重問題。

3. 客戶端數據永遠與服務器端相同或更新(離線玩),當客戶端數據舊的時候需要與服務器同步。

 

1. 增量數據同步:

json queue:

0 {type:"login", data: }

1 {type:"stage", data:{id=sp2, usedwall=8, layout:[], wall=5,coin=100}

2 {type:"useitem", data: }

 

1. 全數據同步

用戶數據包括:

1. 過關數(例如:通過23關,第24關解鎖但未過)

2. 通過的關卡的信息: 使用的牆數,時間

3. 賬戶擁有的牆數和金幣數量。

4. 牆佈局在離線狀態丟棄。

 

同步錨: 通關序號(如23),counter0(每次數據通訊標識), counter1(每次數據通訊標識)

 

[2016年4月27日]

寵物系統

某一關通過後,贈送玩家一種圖形:

恭喜你獲得寵物: 馬蹄型 (圖)

 

又某一關通過後,贈送玩家一種圖形:

恭喜你獲得寵物: 直角型 (圖)

 

玩家攜帶一個寵物參戰,每一關如果劈出寵物圖形,則獎勵增加xx%。

通關後寵物獲得經驗,當經驗滿足升級條件,可以手動升級,升級需要花費一定的金錢。

寵物級別越高,如果劈出與寵物相同圖形,通關獲得的金錢獎勵和圍牆獎勵越多。

您確定更換參戰的寵物嗎? 更換後,原寵物的連勝次數被清除。 連勝越多,通關後獲得的經驗越多。

 

[2016年4月29日]

提示設計 (HintPopup)

HintPopup (經典模式)

|----背景層

  |----scrollview

         |----stageN (sp1.csb, sp2.csb, sp3.csb e.g.)

|----task (type=0, pieces=3,award:{wall=3,coin=100})

         |----mapboard (W=100, H=100)

          |----dimension (size = 700x1100 e.g.)

|----fields

|----walls

|----blocks

|----hints (visible)

 

花錢買過提示,會記錄在案,以後可以自由查看。

[2016年5月6日]

過關檢查

觸發事件: block moved

檢查:

1) dstShape檢查

每一個方格都已被block覆蓋。

2) srcShape檢查

劈分的每一個clip,都已經生成block,每一個block都至少覆蓋一個目標方格。

3) Limit檢查

調用limit::isAllOk(),沒一個檢查道具均檢查通過。

[2016年5月9日]

 

數據庫設計

服務器:

account

[uid] [uid3] [type]

 

role

[uid] [name] [coin] [wall] [zone1Prog] [accountUid]

 

zone1_by_role

[roleUid] [groupId] [s1] [s2] ... [s40]

字段s(n): json format e.g. {w:5, playtime:300, version:1}

 

zone1_by_stage (主要用於遊戲統計)

[uid] [version] [source] [playerNum] [playTimes] [wallNum] [spentTime] [hintTimes] [minWallNum] [maxWallNum] [bestSolution] [bestRoleUid] [bestCreateTime]

s1 1 split1 57 6 241 8 4 9 {jsondata}

 

客戶端: (Logined Account -> Active Role)

account

[uid] [uid3] [type]

 

role

[uid] [name] [coin] [wall] [Zone1Prog] [accountUid] [ccounter] [scounter]

 

zone1

[roleUid] [groupId] [s1] [s2] ... [s40]

字段s(n): json format e.g. {w:5, playtime:300, version:1}

 

opmsg

[_id] [msg] [md5] [roleUid]

 

[2016年5月9日]

用戶數據管理

class Role 是所有用戶數據入口類

Role -> Udb -> Local Database

Role::init 加載表role和zone1中的數據。

 

提供對本地用戶數據的getter和setter

setCoin() getCoin()

setWall() getWall()

setCCounter() getCCounter()

setSCounter() getSCounter()

pushOpMsg() popOpMsg()

Udb嚴禁Remote訪問,保證數據在客戶端和服務器的一致性和可維護性。

[2016年5月12日]

 

帳號登錄及角色初始化過程

1. 帳號和角色必須在服務器創建。 (本地角色除外)

2. xxxManager is not also to manager xxx but also to give life to xxx.

3. 使用帳號數據的前提與聯網無關,而是:本地數據庫中的帳號和角色數據完整。所以需要先檢查這兩個。

4. 帳號表以服務器爲準,角色表需要比較ccounter.

5. 只有用戶選擇“遊客模式”纔會啓用本地角色

6. 登錄失敗或者選擇角色失敗,仍然使用帳號角色,前提是地數據庫完整。

bool Role::checkUserDataIntegrity()

AccountManager::onAuthenticated()  -> (uid3, type)
{
    find(uid3, type) -> (accountUid);  //首先本地搜索帳號:
    find(accountUid) -> (roleUid)  //搜索角色
    (-->onAccountSelected)
    
    AccountManager::login() //如果本地無此帳號或角色,啓動登錄
    (-->onAccountSelected)    
}

AccountManager::onAccountSelected(accountUid, roleUid)
{
    account = Account::create();
    Account::init() //讀數據庫初始化account

    RoleManager::selectRole(roleUid) 
}

RoleManager::selectRole(roleUid)
{
    RoleManager::find(accountUid) -> (roleUid)  //首先本地搜索角色:
    (-->onSelected)

    //沒有找到, 查詢服務器
    (-->onSelected)
}

RoleManager::onSelected(roleUid)
{
    role = Role::create();
    //讀數據庫初始化role
}

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章