天涯明月刀ol|天涯明月刀迅雷下载
  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高級開發教程–制作自己的《捕魚達人》》程序上幫助不大,思路上幫助不錯。
--------------------- 
 
捕魚駕到 天涯明月刀ol