1. 当前所在位置:
  2. 首页
  3. 公告

记录:捕鱼达人开发笔记

2018-12-18 admin
玩游戏的大小孩开发整理笔记:平台cocos2d-x 2.2.6。联网版本,第一版本暂时单人游戏,开发从开始到上线用时一个月。 
遇到的难点:客户端 
1:鱼和子弹碰撞问题 
2:鱼游动倾斜问题 
3:UI动画 
服务器: 
1:鱼路线设计 
2:鱼群 
3:数据延迟问题 
4:数据同步 
整体设计:通过协议方式,传输数据,通过协议头,客户端或者服务器响应响应的功能。子弹客户端先于服务器,鱼服务器先于客户端。点击屏幕发子弹,服务器产生鱼。CS同时存在list记录子弹和鱼。防止假数据和数据同步。 
客户端: 
1碰撞问题:未使用cocos自带的物理引擎,用了OBB包围盒,添加了圆和其他形状的扩展。 
2鱼游动倾斜问题: 
设置了鱼类,通过鱼游动过程中会不挺的设置位置,于前一个位置比较设置倾斜量,重写了setPosition() 
代码如下: 
void UIFish::setPosition(const CCPoint& pos) 
CCPoint l_ccp = this->getPosition(); 
if (l_ccp.y == pos.y) 
if (pos.x > l_ccp.x) 
this->setRotation(0); 
else if (pos.x < l_ccp.x) 
this->setRotation(180); 
else if (l_ccp.y < pos.y) 
float rota = 0; 
if (l_ccp.x == pos.x) 
this->setRotation(-90); 
else if (l_ccp.x < pos.x) 
float k = (l_ccp.y - pos.y) / (l_ccp.x - pos.x); 
rota = 180 * atan(k) / M_PI; 
this->setRotation((-1)*rota); 
else 
float k = (l_ccp.y - pos.y) / (l_ccp.x - pos.x); 
rota = 180 * atan(k) / M_PI; 
this->setRotation((-1)*rota - 180); 
else 
float rota = 0; 
if (l_ccp.x == pos.x) 
this->setRotation(90); 
else if (l_ccp.x < pos.x) 
float k = (l_ccp.y - pos.y) / (l_ccp.x - pos.x); 
rota = 180 * atan(k) / 3.1415926; 
this->setRotation((-1)*rota); 
else 
float k = (l_ccp.y - pos.y) / (l_ccp.x - pos.x); 
rota = 180 * atan(k) / 3.1415926; 
this->setRotation(180 - rota); 
m_obb->setcenterpoint(pos.x, pos.y); 
m_obb->setRotation(getRotation()); 
CCSprite::setPosition(pos); 
3:UI动画 
那不是全靠美工么。 
服务器: 
本文开始于9月中 现在已经是11/11 过去。2.0版本已经上了 ,2.1开发已经开发大半勒。 
所以并不打算写上文中的东西了,打算在1.0中遇到的问题: 
被刷币两次。 
1:描述:我的捕鱼里面,一颗子弹打中两条鱼,那么每天鱼捕获的概率就会下降到原来的1/2。 
在这个基础上,刷币:子弹碰撞了2条,每次碰撞都会有碰撞信息(鱼信息,子弹信息),其中子弹信息里面有这颗子弹碰撞到了几条鱼。用户把这个值用模拟器改成了,那么概率恒变高。刷币成功。(ps:讨厌安卓用户 哎)。 
解决思路,服务器子弹list 没碰撞以前有crash_count值-1;一旦有第一条消息到时,给定值N,然后每次这颗子弹就在crash_count自减。那么当用户改了数据以后,第一次的子弹信息碰撞改成了1 ,第二次的消息就被抛弃了。
 
for (t_ite_bullet = m_Plist_Bullet_user.begin(); t_ite_bullet != m_Plist_Bullet_user.end(); t_ite_bullet++)
    {
        t_bullet_temp = *t_ite_bullet;
        if (t_bullet_temp->_tag == struct_data->_int_bullet_tag)
        {
            if (t_bullet_temp->_crash_count == -1)
                t_bullet_temp->_crash_count = struct_data->_int_count_crash;
            t_bullet_temp->_crash_count--;
            if (t_bullet_temp->_crash_count == -1)
            {
                t_bullet_exist = false;
                m_Plist_Bullet_user.erase(t_ite_bullet);
                t_bullet_temp->_crash_count =- 1;
                m_Plist_Bullet_wait.push_back(t_bullet_temp);
                show_output(74, struct_data->_int_count_crash, struct_data->_int_fish_type);
                break;
            }
            t_bullet_exist = true;
            break;
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2:描述:10倍的炮,当发送的时候,用户把值改成了1000倍。那么回报也多了100倍。
        问题:(这里所有的钱,以服务器为准,客户端显示)子弹是点击屏幕然后发射的,换炮有两种模式,A(点击换,客户端换,然后发送服务器)B(点击换,服务器响应,通知客户换)这里无论哪种都会造成数据的不一致,在一直发炮的情况下B玩家点了换这个时候发送的是10,但是服务器收到了换的通知,接着收到子弹消息,子弹变成了1000了。A会有假数据。这里解决是粗暴的:一旦子弹的权重和服务器炮台的权重不同,这颗子弹直接抛弃。
1
2
3:统计数据不一致,我有2套统计,数据一套放到服务器内存,一套每次结算的时候写入数据库(1~2)分钟写入一次,目的是多桌子,不定时分流,减小服务器压力。 
统计不一致原因:1,子弹还在飞,客户端强退。2救济金 3比较有意思,就是快速发射一串子弹碰撞同一条鱼,服务器收到了好多碰撞信息,在前几条,如第一条的时候,鱼已经被捕获了,活动的鱼列表中那条鱼已经消失了,那条鱼已经被清空(相当于鱼初始化了),放入了等待队列。后面 的子弹检查不到鱼,变成了假数据。解决方案:建立一个捕获队列,当前是10,当时真实的子弹的时候,上诉情况监测捕获队列。很好的统计了一部分子弹的消耗。
 
2.0版本增加:鱼阴影,炮台重做,能量炮,大鱼来的时候,小鱼害怕逃跑,自动发炮,子弹屏幕回弹,暂停(产品取消了,可能2.x上),背景切换,音乐切换模式改等。 
比较有意思的一些注意点,子弹碰撞屏幕,就是用数学的方式求斜率K,点斜-k,求运动路线。这里有种特殊情况,正好子弹的顶点,数学上要注意。 
小鱼害怕:那么需要不同的计时器老控制不同鱼的速度。
 
m_sched_small_fish = new CCScheduler();
        defaultScheduler->scheduleUpdateForTarget(m_sched_small_fish, 0, false);
        m_actionmanager_small_fish = new CCActionManager();
        m_sched_small_fish->scheduleUpdateForTarget(m_actionmanager_small_fish, 0, false);
 
        m_sched_big_fish = new CCScheduler();
        defaultScheduler->scheduleUpdateForTarget(m_sched_big_fish, 0, false);
        m_actionmanager_big_fish = new CCActionManager();
        m_sched_big_fish->scheduleUpdateForTarget(m_actionmanager_big_fish, 0, false);
        //对不同的鱼--设置不同的动作管理器
        t_fish->setActionManager(m_actionmanager_small_fish);
1
2
3
4
5
6
7
8
9
10
11
这个是我一开始没有找到的方法。是在看CCnode源码的时候发现的。ps:英语好太重要了,可惜太差,不然也不用找半天了
 
  CCActionManager *m_pActionManager;  ///< a pointer to ActionManager singleton, which is used to handle all the actions  CCActionManager *m_pActionManager;  
1
鱼影音用shader做的,最近也在看。好难,到时候重新写博客仅仅关于shader的相关。网上资料找的看的真累。 
背景切换,其实好low但是,没想到好方法。效果地图1 地图2,先显示地图1,然后潮汐过来地图2慢慢显示,当然2不是移动过来,而是显示。会有左半边地图1,潮汐,右半边地图2的这样显示。 
解决方法:开始是背景A(没看到),一个大的遮罩B(真实的背景),当要换的时候。B遮罩放大,慢慢挡住B的地图,露出A的地图。结束后AB地图背景设置,AB数据初始化。 
初始化背景
 
    int t_int_bg_id = rand() % 6;
        CCString* t_str_background = CCString::createWithFormat("buyudaren/UI/bg_%d.png", t_int_bg_id);
        CCString* t_str_bg = CCString::createWithFormat("buyudaren/UI/bg_%d.png", (t_int_bg_id+1)%6);
        m_int_sound_id = 0;
 
        m_img_background_new = UIImageView::create();
        m_img_background_new->loadTexture(t_str_background->getCString());
        m_img_background_new->setAnchorPoint(ccp(0.5,0));
        addChild(m_img_background_new);
        m_img_background_old = UIImageView::create();
        m_img_background_old->loadTexture(t_str_bg->getCString());
        m_img_background_old->setAnchorPoint(ccp(0.5, 0));
        m_int_bg_id = t_int_bg_id;
 
        CCSize the_back_size = m_img_background_new->getSize();
        float float_scale_width = the_size.width / the_back_size.width;
        float float_scale_height = the_size.height / the_back_size.height;
        float float_scale = float_scale_height > float_scale_width ? float_scale_height : float_scale_width;
        float float_ca = float_scale*the_back_size.width / 2 - the_size.width / 2;
 
        m_img_background_new->setScale(float_scale);
        m_img_background_new->setPosition(ccp(the_size.width / 2, 0));
        m_img_background_old->setScale(float_scale);
        m_img_background_old->setPosition(ccp(-the_size.width-40-float_ca, -the_size.height / 2));
        m_img_background_old->setAnchorPoint(ccp(0,0));
        stencil = CCSprite::create("buyudaren/UI/kongbai.png");
        CCSize the_stencil_size = stencil->getContentSize();
        stencil->setAnchorPoint(ccp(1,0.5));
 
        m_clip_wave = CCClippingNode::create();
        m_clip_wave->setInverted(true);
        m_clip_wave->setAnchorPoint(ccp(0, 0));
        m_clip_wave->setPosition(ccp(the_size.width+40, the_size.height / 2 ));
        m_clip_wave->setStencil(stencil);
        CCNode::addChild(m_clip_wave,1,10);
        m_clip_wave->addChild(m_img_background_old);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
移动:
 
        CCScaleTo* t_scale = CCScaleTo::create(3.0f, (the_size.width + 40) / 10);
        stencil->runAction(t_scale);
        CCMoveTo* t_move_wave = CCMoveTo::create((364+the_size.width)/((the_size.width+40)/3), ccp(-364, 0));
        CCSequence* l_seq_wave = CCSequence::create(t_move_wave, CCCallFunc::create(this, callfunc_selector(layer_game::action_wave_end)),NULL);
        m_sprite_wave->runAction(l_seq_wave);
1
2
3
4
5
这里提一句:换潮汐的时候,鱼应该是没有的,而服务器应该是在产生鱼的。即,客户段收到消息后统一放入消息池,队列的形式响应消息。当潮汐的时候应该阻塞消息分发。潮汐结束,继续开始消息分发。 
后话: 
捕鱼很关键的一点是动作,cocos提供了很好的动作管理器类:CCActionManager和动作CCAction,了解他们就能写出复杂的动作,比如气泡破裂等。正在整理下几节分享。sheder太难,刚刚摸到皮毛,有读者看到了如果能告诉资料啥的就太好了。2.0开发时读了烈火鸟网络科技公司的《cocos-2d高级开发教程–制作自己的《捕鱼达人》》程序上帮助不大,思路上帮助不错。
--------------------- 
 
捕鱼驾到