手游《奥林劈图》的开发日记(一)

77 篇文章 0 订阅
19 篇文章 0 订阅

我最近将手游《奥林劈图》上线到苹果商店,了却了一桩三年的心愿,心情也由之前的燥动不安回归平静。现在我真的有时间和一颗平常心去拥抱机器学习和数据挖掘了。

几年前自己刚开始学习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
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

峻峰飞阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值