Iphone用戶端程式員半年工作總結

來源:互聯網
上載者:User

  來公司四個半月了,從對用戶端遊戲編程的小白慢慢的也能寫一些東西了,當然了這裡最感謝的人就是九天了,對於九天其它的好我就不說了,就是感覺九天為了團隊,為了項目,他在很用心的做每一件事情。就如武俠小說裡的人物,有的練習功夫強身健體,有的為了取得江湖地位,有的為了報仇雪恨,當然有很少一部分當做興趣愛好研究,比如說周伯通,他就是個武癡,對什麼功夫都感興趣。可是這些人裡沒有一項符合九天的,我認為九天喜歡將事情做的比較完美已經成為一種習慣,無論是代碼方面還是說管理方面,處處為大家考慮,為公司考慮。到是他自己的事情考慮的很少,這裡我就不舉例子了,大家都懂得,:-D。要寫總結,就忍不住感謝下九天,來公司這麼長時間了,還沒感謝過他。

 

    剛開始接觸用戶端遊戲,我只是本能的處於接受任務,完成任務。由於沒有認真仔細的去思考這些任務,犯了很多低級的錯誤。其實這就是一種不負責任的表現方式,只是把任務當做任務完成了,但並沒想這個東西是幹什麼用的,怎麼用?用術語說的話,要實現什麼功能,這個功能在什麼地方要用?這裡舉個我第一次接收的一個小任務的例子:實現一個函數,這個函數實現的功能是設定不同語言,來實現多語言載入。當時我也沒仔細想,就將這個函數寫成了一個全域函數,放在config檔案裡。config檔案是用來儲存工程設定還有一些通用的全域變數的東西,我寫在這裡,一個是設定不方便,在一個也不符合config檔案的性質。別看這是一個小小的函數,通過這個函數,程式裡實現了不同國家語言的分離,只通過修改材質的尾碼名就可以方便的切換不同國家的材質。

 

 

const string ConfigManager::GetResourceTextureName( const char* res_name ) { 

  std::string full_name(res_name); 

 

  switch (language_) { 

    case LAN_EN: 

      full_name += "_en.png"; 

      break; 

    case LAN_CN: 

      full_name += "_cn.png"; 

      break; 

    case LAN_KR: 

      full_name += "_kr.png"; 

      break; 

    case LAN_JP: 

      full_name += "_jp.png"; 

      break; 

    default: 

      CCAssert(0,"wrong language"); 

      break; 

  } 

  return full_name; 

故事代碼1

   第二個比較典型的任務是金幣儀錶,如下:

 

     

 

   圖片1

 

    這個剛開始認為比較簡單的一個任務,就是說根據當前玩家的分數移動每個位元的數字材質,表現出一個數字。分析下需求大概有以下幾點要求:

 

   1、玩家資料載入時算好每個素材的位置

 

   2、監測到玩家資料有變化時材質根據數值變化,播放動作

 

   3、數字變化動作就如滾輪效果,0——9,9——0實現平滑過度

 

   4、數字不能同是播放所有位元的效果

 

    剛開始做的時候美術給出的是一個橫版的材質,要做出豎版的效果,這可如何是好,經過和九天的討論得出通過裁剪出每個小的數字材質,然後去控制小的數字材質,這期間遇到了很多問題。剛開始對cocos2d-x動作那套不是太懂,基本就會用個MoveTo(),對ccnode那套也不是很熟,對這些概念都不是很理解。所以每次要表現數字變化時,都先產生一個材質數組,然後算出每個小的材質到目標位置的相對的距離。在動畫播放過程中,假如有新的動畫,則先將數字變化存放在數組裡,當然了一個是先進先出的模型。通過一周的折騰準系統是實現了,但是效果不是很好,尤其0——9,9——0的突變沒有處理好。並且還用的是單間模式,豈不知金幣儀錶只是ui層的一個功能模組,根本沒有必要用單間,所以這個也是對功能沒有想清楚。後來經過一周的折騰,發現這種方法太扭曲了,問題很多,經過和lyc,九天的商討改變了材質,由橫條改為豎條,就是這麼簡單的一改,問題一下簡單化了很多,首先我們都知道圓柱面展開後是一個矩形,這樣通過豎條的矩形就能很好的實現圓柱的功能,只要簡單的在矩形9後面再補個0,就可以了。因為9之後就是0,0之前就是9,為了讓動作的連貫性,動作播放完,立馬調用setposition將材質設定一個位置。就簡單的實現了滾動效果。功能實現了,效果還的慢慢調,遊戲不就是要個感覺嘛,通過對動作的時間調整,最後實現了這個功能。回想起來這中間又很多值得學習的地方:

 

    1、在做任務的時候一定要想清楚功能,心裡基本要明白通過什麼方式去實現這個功能

 

    2、將一些常用的算數歸納為數學公式,大大方便運算。

 

    3、多和自己的同事交流,三人行必有我師,咱們這個團隊都是很樸實的技術青年,對每個人的問題都很熱心的。

 

    4、只有通過多做,對這個東西熟悉了,理解了,才能做出如工藝品的東西

 

    sky項目的加密解密程式還是比較有意思的,這個程式是通過一套叫tea的演算法,將程式的設定檔加密成為2進位檔案,在程式啟動的時候再進行解密。首先來看下密碼編譯演算法:

 

   

 

 

void TEA::encrypt(const ulong *in, ulong *out) { 

 

    ulong *k = (ulong*)_key; 

    register ulong y = in[0]; 

    register ulong z = in[1]; 

    register ulong a = k[0]; 

    register ulong b = k[1]; 

    register ulong c = k[2]; 

    register ulong d = k[3]; 

    register ulong delta = 0x9E3779B9; /* (sqrt(5)-1)/2*2^32 */ 

    register int round = _round; 

    register ulong sum = 0; 

 

    while (round--) {   /* basic cycle start */ 

        sum += delta; 

        y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); 

        z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); 

    }   /* end cycle */ 

    out[0] = y; 

    out[1] = z; 

故事代碼2

    在這過程中首先的看懂這個加密函數,由於網上的例子是直接加密解密一個很短的字串,我就天真的認為密碼編譯演算法是傳入一個char*,然後通過加密傳出一個char*,這樣就加密好了。導致花了很多時間在尋找為什麼程式加密不對。再一個字串都是以‘/0’結尾的,由於對概念理解的不是很清楚,程式總是出現一些比較奇怪的問題。

 

 

const string TEA::SaveFile(const char* content) 

  int file_size = strlen(content); 

  byte *plain = new byte[file_size + 1]; 

  CharToByte((char*)content, plain, file_size); 

  byte *new_plain = new byte[file_size*8 + 1]; 

  int k = 0; 

  for (int i = 0; i < file_size; ++i) 

  { 

    encrypt(plain + i, new_plain + k); 

    k += 8; 

  } 

  char *out = new char[file_size*8 + 1]; 

  ByteToChar(new_plain, out, file_size * 8); 

  string crypt_str(out, file_size * 8); 

  delete []plain; 

  delete []new_plain; 

  delete []out; 

  return crypt_str; 

 

故事代碼3

最後通過做這個功能模組得到三點:

 

    1、申請記憶體的時候,要多申請一位,用來儲存,結尾符號

 

    2、讀取字串的時候都是以‘/0’結尾的,所以說對與2進位的資料流用strlen就不對了。

 

    3、strlen函數其實就是遍曆數組每個字元,只到只到'/0'的時候返回遍曆次數。

 

    4、要有勇氣和渴望多看看代碼,明白了遠離問題也就不難了。

 

    這裡大概說說程式裡遇到一些問題,以及解決方案,增強自己的自備知識。

 

 

1、遊戲程式的運行機理

 

    遊戲程式的main函數是一個while函數,只到玩家退出程式,while迴圈結束

 

 

int main() 

    while(1) 

    { 

         if(g_run) 

         { 

              Tick(ts); 

              DrawScene(); 

          } 

         else 

         { 

            break; 

          } 

     } 

    return 0; 

 

偽碼1

    遊戲程式和應用軟體最大的區別就是,應用軟體一直在等待著使用者的輸入,來相應使用者的請求,而遊戲程式就不同了,隨著程式時間的配置,遊戲通過drawscene不斷的去渲染新的畫面,說的通俗點就是輸出一張張材質。材質本質上就是點的屬性集合。比如一個頂點顏色屬性顏色函數是通過color(uchar red, uchar green, uchar blue, uchar alpfa)。材質的像素就是來描述頂點數量的變數。像素越高表明材質越細膩。而遊戲畫面就是通過每秒播放材質的數量來描述的。這就是幀的概念,玩過flash的都懂得,如何通過設定沒幀材質來實現簡單的動畫。說這麼多就是要表達,遊戲程式就是通過在合適的時間輸出不同的材質。注意,我這裡說的是遊戲程式,而不是遊戲。因為遊戲是技術,美術,創意,音樂的集合體。就拿獵鳥裡寵物館裡一個彈出對話方塊來說,這個lyc實現的,說個真心話,這一點我非常佩服lyc,他做遊戲不是簡簡單單的去完成任務,而是通過一種審美的意識去看待每個模組的實現。雖說程式不能對整個遊戲的整體表現有太大的決定權,但在細節的處理上加上自己的理解和愛,局部還是會非常出彩的。

 

void PetInfoDialog::Show() { 

  this->setScale(0.0f); 

   

  CCScaleTo* scale = new CCScaleTo; 

  scale->initWithDuration(0.5f,1.0f); 

 

  CCEaseBackOut* ease = new CCEaseBackOut; 

  ease->initWithAction(scale); 

  scale->release(); 

 

  runAction(ease); 

  ease->release(); 

 

void PetInfoDialog::Hide() { 

  CCScaleTo* scale = new CCScaleTo; 

  scale->initWithDuration(0.5f,0.0f); 

 

  CCEaseBackIn* ease = new CCEaseBackIn; 

  ease->initWithAction(scale); 

  scale->release(); 

 

  CCCallFunc* call_func = new CCCallFunc; 

  if(!call_func->initWithTarget(this, callfunc_selector(PetInfoDialog::DestoryCallback) ) ) { 

    CCLog("call fun selector wrong"); 

    return; 

  } 

 

  CCSequence* seq = new CCSequence; 

  seq->initOneTwo(ease, call_func); 

  ease->release(); 

  call_func->release(); 

 

  runAction(seq); 

  seq->release(); 

範例程式碼1

 

2、記憶體管理

 

    學過c/c++的人都知道指標和記憶體管理是最難理解的,這裡我就不詳細說明記憶體管理的話題,只是說說在cocos2d-x裡如何管理好自己的記憶體。

 

cocos2d-x裡的精靈都繼承自CCobject類,先看看CCobjec自己申請和釋放記憶體的原理。

 

 

CCObject::CCObject(void) 

    static unsigned int uObjectCount = 0; 

 

    m_uID = ++uObjectCount; 

 

    // when the object is created, the refrence count of it is 1 

    m_uReference = 1; 

    m_bManaged = false; 

 

 

void CCObject::release(void) 

    assert(m_uReference > 0); 

    --m_uReference; 

 

    if (m_uReference == 0) 

    { 

        delete this; 

    } 

 

範例程式碼2

    再來看看父類,我說的這個父類不是繼承關係的父類,而是說裝配層次的關係。父類調用addchild發生了什麼。

 

void CCNode::addChild(CCNode *child, int zOrder, int tag) 

{    

    CCAssert( child != NULL, "Argument must be non-nil"); 

    CCAssert( child->m_pParent == NULL, "child already added. It can't be added again"); 

 

    if( ! m_pChildren ) 

    { 

        this->childrenAlloc(); 

    } 

 

    this->insertChild(child, zOrder); 

 

    child->m_nTag = tag; 

 

    child->setParent(this); 

 

    if( m_bIsRunning ) 

    { 

        child->onEnter(); 

        child->onEnterTransitionDidFinish(); 

    } 

 

 

void CCNode::insertChild(CCNode* child, int z) 

    unsigned int index = 0; 

    CCNode* a = (CCNode*) m_pChildren->lastObject(); 

    if (!a || a->getZOrder() <= z) 

    { 

        m_pChildren->addObject(child); 

    } 

    else 

    { 

        CCObject* pObject; 

        CCARRAY_FOREACH(m_pChildren, pObject) 

        { 

            CCNode* pNode = (CCNode*) pObject; 

            if ( pNode && (pNode->m_nZOrder > z )) 

            { 

                m_pChildren->insertObject(child, index); 

                break; 

            } 

            index++; 

        } 

    } 

 

    child->setZOrder(z); 

 

範例程式碼3

     通過原始碼,得到結論如下:

    1、精靈建立時,它的reference加1,relese的時候reference減1。

    2、將精靈安裝到父類的時候,父類會調用addobject,這樣父類就會使精靈reference數加1,這就意為著精靈的生命週期又它的父類管理了。

    3、如果不能一一對應的使reference的值加1和減1的話就會造成記憶體流失。

    4、從程式裡看出,每個sprite只能添加到一個父類上。

3、狀態機器

 

void BackgroundAncientLayer::Update(cocos2d::ccTime ts) 

  if ((torch_num_ < kTorchCount) && (torch_time_ += ts) > 1.0f) 

  { 

    SetTorchGui(); 

    torch_time_ = 0; 

  } 

  switch(state_) 

  { 

  case STATE_START: 

    SetState(STATE_CLOUD_OUT); 

    break; 

  case STATE_CLOUD_OUT: 

#ifdef TARGET_IPHONE 

   PlayCloudAciton(ccp(1522.0f * kIphoneScaleFactor, 210.0f * kIphoneScaleFactor),  

     ccp(-420.0f * kIphoneScaleFactor, 850.0f * kIphoneScaleFactor), 80.0f, 15.0f);   

#else 

   PlayCloudAciton(ccp(1707.0f, 0), ccp(-568.0f, 895.0f), 80.0f, 15.0f);    

#endif 

   SetState(STATE_END); 

  case STATE_END: 

    break; 

  default: 

    break; 

  } 

 

範例程式碼4

    通過狀態來標記程式啟動並執行不同階段,有以下幾點好處:

    1、思路比較清晰,在本例子中,很清晰的表明了要乾的事情,這個例子比較簡單,遇到複雜的狀態很大程度上可以降低程式的出錯率。

    2、可以很容易的跳躍到不同的狀態上。

4、簡單的設計模式

 

摘自 武龍飛的空中樓閣

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.