• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            戰魂小筑

            討論群:309800774 知乎關注:http://zhihu.com/people/sunicdavy 開源項目:https://github.com/davyxu

               :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
              257 隨筆 :: 0 文章 :: 506 評論 :: 0 Trackbacks

            #

            測試用例

            我們對Golang的結構體變量賦值, 以及單參數函數調用進行反射和native操作的測試

             

            package main

             

            import (

            "reflect"

            "testing"

            )

             

            type data struct {

            Hp int

            }

             

            const AssignTimes = 100000000

             

            func TestNativeAssign(t *testing.T) {

             

            v := data{Hp: 2}

             

            for i := 0; i < AssignTimes; i++ {

            v.Hp = 3

            }

             

            }

             

            func TestReflectAssign(t *testing.T) {

             

            v := data{Hp: 2}

             

            vv := reflect.ValueOf(&v).Elem()

             

            f := vv.FieldByName("Hp")

             

            for i := 0; i < AssignTimes; i++ {

             

            f.SetInt(3)

            }

             

            }

             

            func TestReflectFindFieldAndAssign(t *testing.T) {

             

            v := data{Hp: 2}

             

            vv := reflect.ValueOf(&v).Elem()

             

            for i := 0; i < AssignTimes; i++ {

             

            vv.FieldByName("Hp").SetInt(3)

            }

             

            }

             

            func foo(v int) {

             

            }

             

            const CallTimes = 100000000

             

            func TestNativeCall(t *testing.T) {

            for i := 0; i < CallTimes; i++ {

             

            foo(i)

            }

            }

             

            func TestReflectCall(t *testing.T) {

             

            v := reflect.ValueOf(foo)

             

            for i := 0; i < CallTimes; i++ {

             

            v.Call([]reflect.Value{reflect.ValueOf(2)})

            }

            }

            性能測試數據

            === RUN TestNativeAssign
            — PASS: TestNativeAssign (0.03s)
            === RUN TestReflectAssign
            — PASS: TestReflectAssign (0.41s)
            === RUN TestReflectFindFieldAndAssign
            — PASS: TestReflectFindFieldAndAssign (9.86s)
            === RUN TestNativeCall
            — PASS: TestNativeCall (0.03s)
            === RUN TestReflectCall
            — PASS: TestReflectCall (21.46s)

            測試評測

            • 在結構體變量賦值測試用例中, 我們發現TestReflectFindFieldAndAssign賦值格外的耗時. 分析性能點在FieldByName這個函數上, 我們查了下底層如何實現的:

            // FieldByName returns the struct field with the given name

            // and a boolean to indicate if the field was found.

            func (t *structType) FieldByName(name string) (f StructField, present bool) {

            // Quick check for top-level name, or struct without anonymous fields.

            hasAnon := false

            if name != "" {

            for i := range t.fields {

            tf := &t.fields[i]

            if tf.name == nil {

            hasAnon = true

            continue

            }

            if *tf.name == name {

            return t.Field(i), true

            }

            }

            }

            if !hasAnon {

            return

            }

            return t.FieldByNameFunc(func(s string) bool { return s == name })

            }

            各位看官必須吐槽用for來遍歷獲取數據, 但冷靜下來分析. 這樣做無可厚非.
            試想如果reflect包在我們使用ValueOf時使用map緩沖好一個結構體所有字段的訪問數據后, 肯定訪問指定字段速度會很快
            但是, 以空間換速度的需求其實最多滿足了1%的需求.
            同樣的例子是圖形API里訪問Shader變量的方法, 總是默認使用字符串獲取, 速度很慢. 當你想快速訪問時, 請提前按需緩存字段
            那么, Golang使用的也是這樣的思路. 雖然暴力了一點, 但是能夠讓程序跑對, 性能優化的東西放在之后來做, 緩沖下就可以解決

            • 在調用測試用例中, 毫無懸念的, 調用速度很慢
              因此, 我們在平時使用反射時, 盡量偏向于反射變量緩沖存在下的變量賦值或者獲取
              而調用的需求盡量減少, 如果有goroutine存在的情況下, 則不必太多擔心.
            posted @ 2016-08-12 15:26 戰魂小筑 閱讀(3048) | 評論 (0)編輯 收藏

            這是一個Unity3D元老級bug, 表現就是:  角色在屏幕邊緣放特效,  離開屏幕持續一段時間后再回到屏幕后, 發現特效重新播放. 顯然這是錯誤的效果

            解決方法:

            在ParticleSystem組件上勾選SubEmitter, 不要問為什么, 做就好

            相關官方鏈接

            http://answers.unity3d.com/questions/218369/shuriken-particle-system-not-rendering-particles-w.html

             

            天煞的, 4.X程序無法訪問粒子系統的參數, 所以只能辛苦美術兄弟們了

            posted @ 2016-08-01 15:23 戰魂小筑 閱讀(5592) | 評論 (0)編輯 收藏

            熱更新的內容可以是美術資源, 可以是代碼, 但相對來說, 美術資源的更新不會受到約束, 代碼實際上是重災區, 本文介紹的主要是代碼熱更新

            熱更新對于開發者來說是一件麻煩事, 特別對于看重效率,便捷性和結構的程序員來說, 熱更新就是運營人員的不懂技術的表現
            然而, 對于上線才是剛剛開始的網絡游戲, 特別是手游來說. 熱更新是極為重要的基礎功能

            為什么要熱更新

            客戶端

            適應上線需求

            對于手游客戶端來說, 受到蘋果審核的約束, 一次審核提交需要10~20天不等的等待時間, 而這段時間, 開發進度依然會推進很多.

            一旦手游上線, 第一個版本在玩家瘋狂行為下, 出點問題是必然的, 所以”上線更”就成了家常便飯. 如果你要說, 必須大包, 無法熱更, 那么10~20

            多天后, 游戲估計就沒啥人了, 更別說渠道, 發行投入巨大資金進行推廣之下讓玩家迎來的一堆bug的版本以及所謂程序員的傲慢和清高.

            熱調試, 熱開發, 熱發布

            除了線上問題之外, 由于Unity3D為了適應64位應用需求, 將C#編譯出的IL代碼利用il2cpp第三方庫編譯成為c++. 效率提升了倒是好,

            但工程編譯和發布時間變得相當感人, 沒個1~2個小時完全搞不定. 即便加裝ssd, 為了修改一個bug, 也不知道要等多少根煙的時間…

            只要核心功能不變化的情況下, 完全可以讓熱更新成為開發期間的好工具, lua代碼修改后, 馬上可以在手機上看效果, 沒有編譯, 發布的時間損耗, 其實反而提升了開發效率

            服務器

            對于服務器來說, 常見游戲類型的玩家一般在半夜的在線人數會急速下降. 但是對于比較熱門的MMO, 以溝通為基礎的游戲, 半夜也會有很多人在線

            因此傳統的停服更新對于玩家的熱情秒殺很大的. 想想看,屁股先鋒公測停15天各位是什么感受? 所以為了玩家體驗, 同時保證服務器穩定的前提下

            修復一些輕微bug, 用熱更新再合適不過了. 所以老服務器程序員, 千萬不能以服務器穩定為借口而忽略了玩家體驗.

            技術是用來解決問題的, 不是用來裝X的

            怎么熱更新

            以下是Unity3D的幾種熱更新方式

            基于C#, 使用動態加載Assembly反射更新代碼

            這種方式在安卓上完全可行, 對現有架構無需大的修改, 一樣使用C#和Unity3D的方式進行開發

            但在iOS上受到限制, 因此對于全平臺首發的游戲, 或者雙平臺都要上的游戲, 已經慢慢的不使用這種方法進行熱更新了

            基于Lua, 將Lua代碼視為資源, 動態加載并運行

            云風團隊早期研究出的UniLua是基于C#編寫的Lua虛擬機來運行, 而且只支持字節碼解釋, 因此無法做動態功能, 效率奇低

            后期, ulua的出現, 徹底將Lua作為比較正統的更新方式存在. ulua基于Tolua庫進行封裝, 添加了一些便捷封裝, 代碼打包和基本的框架

            ToLua本身是一個基于C版Lua上擴充的庫, 以靜態鏈接庫方式與Unity3D代碼鏈接. 因此, 可以說ToLua是跑在C層上, 速度不亞于C++和Lua的組合

            基于Lua的代碼更新方式, 無論跨任何平臺都可以以同一套代碼和工作流進行, 因此避免很多麻煩, 成為現在主流的開發方式

            游戲邏輯全都用Lua寫么?

            做過網頁和手機App的童鞋都發現, js, 一個bug超多, 設計奇怪的語言居然成為主流界面開發語言, 為啥?

            動態特性適合制作ui

            另外一個反例就是: 使用C++開發界面, 例如Qt, MFC之類, 雖然設計嚴謹, 但是最終擋不住各種奇葩的修改需求

            因此, 界面非常推薦使用動態語言來開發, 游戲界就是用Lua

            而游戲核心, 根據各自游戲類型來定, 總的一點, 效率瓶頸點, Update之類的, 盡量使用C#或者C++來實現

            寫在最后

            當前中國大環境下的玩家和各種氪金理由與純的不能再純的游戲人的基本愿望是沖突的

            然而國外游戲的各種設計和機制, 暴雪戰網更新不及時, 版本不對沒提示, 這些基本錯誤在中國的網游都不會出現的

            技術上無法趕英超美的我們, 在體驗上已經輸出了我們的價值觀, 老外們都在學

            對于程序員來說, 只是多貼近玩家, 多了解外面的世界而已

            posted @ 2016-07-06 11:03 戰魂小筑 閱讀(6072) | 評論 (6)編輯 收藏

            蘋果要求在2016年6月1日后新的app必須支持ipv6網絡, 技術發展靠蘋果果然沒錯, 但開發者還是要開始忙起來了
            這里介紹下Unity3D的適配的一些經驗

            基本注意點

            • ios ipv6適配無需修改服務器, 也就是說, 如果你的服務器依然是ipv4的也是可以使用的
            • 蘋果的適配方案是將ipv4的地址轉換為ipv6, 到了路由層再轉回去繼續利用ipv4網絡傳輸

            測試網絡環境搭建

            轉載請注明:http://www.shnenglu.com/sunicdavy戰魂小筑

            網上有很多翻譯了蘋果官方的搭建ipv6測試網絡環境的文章, 例如:
            http://www.cocoachina.com/ios/20160525/16431.html
            注意以下幾點

            • 無需路由器支持ipv6, 但貓(modem)必須要支持ipv6. 因為現在大多數都是光貓
              以下截圖是光貓管理端
              3440e3f9-12de-435b-85ab-a7a3be8b384b[6]
              光貓里的ipv6支持默認是關閉的, 所以需要手動打開, 按默認值配置即可

            • 請確認mac os系統必須是osx 10.11以后的版本才可以打開NAT64

            • 正確連接mac的ios設備應是如下截圖示意
              91f54476-4b5d-4585-a364-0da2139774c1[6]

            • 默認連接上wifi時看連接信息時, 一般只會有紅色DNS地址或者根本不顯示
            • 只有在第一次訪問網絡, 例如打開瀏覽器進入任意網站時, 才會顯示上面的幾條信息
            • 如果只有DNS沒有IP地址和子網掩碼, 一般是光貓沒有打開ipv6的DHCP, 沒有分配IP
            • 還有一種測試ipv6 DHCP是否正常工作的方法: 關閉NAT64時可以上網, 但打開NAT64無法上網

            轉載請注明:http://www.shnenglu.com/sunicdavy戰魂小筑

            Unity3D的Socket適配

            WWW類本身已經支持了IPV6, 無需處理, 這里講解使用C#原生Socket的處理

            • 測試用的設備的iOS版本必須是9.3以上的
            • Socket構造時, AddressFamily 設置為InterNetworkV6時只支持ipv6網絡, 傳入InterNetwork時只支持ipv4網絡
            • 4.7.2和5.4.3的當前版本在mono層并未支持ipv6代碼適配的核心函數getaddrinfo, 因此需要通過oc層做轉換, 以下是代碼
              這段代碼將getaddrinfo的地址轉換成一個完整字符串, 格式是:
              ipv4|ipv4地址|ipv6|ipv6地址|

            P.S. copyStr這種用法參考了http://www.codeinsect.net/blog/2016/05/26/unity-ipv6-socket-%E6%94%AF%E6%8C%81%EF%BC%8C%E5%B7%B2%E6%B5%8B%E8%AF%95%E9%80%9A%E8%BF%87/
            會造成內存泄露, 如果有更好的方法歡迎反饋

            轉載請注明:http://www.shnenglu.com/sunicdavy戰魂小筑

            iosaddrinfo.mm

               1:  #include <sys/socket.h>
               2:  #include <netdb.h>
               3:  #include <arpa/inet.h>
               4:  #include <err.h>
               5:  #define OUTSTR_SIZE 4096
               6:  extern "C"
               7:  {
               8:      const char* copyStr( const char* str )
               9:      {
              10:          char* s = (char*)malloc(strlen(str) + 1);
              11:          strcpy(s, str);
              12:          return s;
              13:      }
              14:      const char* IOSGetAddressInfo(const char *host )
              15:      {
              16:          if( NULL == host )
              17:              return copyStr("ERROR_HOSTNULL");
              18:          char outstr[OUTSTR_SIZE];
              19:          struct addrinfo hints, *res, *res0;
              20:          memset(&hints, 0, sizeof(hints));
              21:          hints.ai_family = PF_UNSPEC;
              22:          hints.ai_socktype = SOCK_STREAM;
              23:          hints.ai_flags = AI_DEFAULT;
              24:          printf("getaddrinfo: %s\n", host);
              25:          int error = getaddrinfo(host, "http", &hints, &res0);
              26:          if (error != 0 )
              27:          {
              28:              printf("getaddrinfo: %s\n", gai_strerror(error));
              29:              return copyStr("ERROR_GETADDR");
              30:          }
              31:          memset( outstr, 0, sizeof(char)*OUTSTR_SIZE );
              32:          struct sockaddr_in6* addr6;
              33:          struct sockaddr_in* addr;
              34:          const char* solvedaddr;
              35:          char ipbuf[32];
              36:          for (res = res0; res; res = res->ai_next)
              37:          {
              38:              if (res->ai_family == AF_INET6)
              39:              {
              40:                  addr6 =( struct sockaddr_in6*)res->ai_addr;
              41:                  solvedaddr = inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, sizeof(ipbuf));
              42:                  strcat ( outstr, "ipv6|");
              43:                  strcat ( outstr, solvedaddr);
              44:              }
              45:              else
              46:              {
              47:                  addr =( struct sockaddr_in*)res->ai_addr;
              48:                  solvedaddr = inet_ntop(AF_INET, &addr->sin_addr, ipbuf, sizeof(ipbuf));
              49:                  strcat ( outstr, "ipv4|");
              50:                  strcat ( outstr, solvedaddr);
              51:              }
              52:              strcat ( outstr, "|");
              53:          }
              54:          return copyStr(outstr);
              55:      }
              56:  }
            轉載請注明:http://www.shnenglu.com/sunicdavy戰魂小筑

            iosaddrinfo.h

               1:  #pragma once
               2:  extern "C"{
               3:      const char* IOSGetAddressInfo(const char *host );
               4:  }
            • C#層的處理假設多個地址中都是統一的地址類型,要么全是v4要么全是v6
              返回給定的host內多個IP地址, 可以供處理復雜的北網通,南電信問題

               1:  using System;
               2:  using System.Net;
               3:  using System.Net.Sockets;
               4:  using System.Runtime.InteropServices;
               5:  using UnityEngine;
               6:  using System.Collections;
               7:  using System.Collections.Generic;
               8:  public class IOSIPV6
               9:  {
              10:      [DllImport("__Internal")]
              11:      private static extern string IOSGetAddressInfo(string host );  
              12:      public static IPAddress[] ResolveIOSAddress(string host, out AddressFamily af)
              13:      {
              14:          af = AddressFamily.InterNetwork;
              15:          var outstr = IOSGetAddressInfo(host);
              16:          Debug.Log("IOSGetAddressInfo: " + outstr);
              17:          if (outstr.StartsWith ("ERROR")) 
              18:          {
              19:              return null;
              20:          }
              21:          var addressliststr = outstr.Split('|');
              22:          var addrlist = new List<IPAddress>();
              23:          foreach (string s in addressliststr)
              24:          {
              25:              if (String.IsNullOrEmpty(s.Trim()))
              26:                  continue;
              27:              switch( s )
              28:              {
              29:                  case "ipv6":
              30:                      {                        
              31:                          af = AddressFamily.InterNetworkV6;
              32:                      }
              33:                      break;
              34:                  case "ipv4":
              35:                      {
              36:                          af = AddressFamily.InterNetwork;
              37:                      }
              38:                      break;
              39:                  default:
              40:                      {
              41:                          addrlist.Add(IPAddress.Parse(s));
              42:                      }
              43:                      break;
              44:              }
              45:          }
              46:          return addrlist.ToArray();
              47:      }
              48:  }
            轉載請注明:http://www.shnenglu.com/sunicdavy戰魂小筑

            參考鏈接

            官方文檔
            https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html#//apple_ref/doc/uid/TP40010220-CH213-SW1

            某人的解決方案
            http://www.codeinsect.net/blog/2016/05/26/unity-ipv6-socket-%E6%94%AF%E6%8C%81%EF%BC%8C%E5%B7%B2%E6%B5%8B%E8%AF%95%E9%80%9A%E8%BF%87/
            注意, 此方案中的方法可用, 但是地址并不能解決南北互通的問題

            posted @ 2016-06-16 14:18 戰魂小筑 閱讀(5364) | 評論 (0)編輯 收藏

            最近整合ulua到項目里進行熱更新, protoc-gen-lua這古老的東西重新讓我繼續發博客, 因為坑

            生成好的協議報錯找不到protobuf

            在每個protoc-gen-lua生成的lua文件里, 都有一行

            local protobuf = require "protobuf"

            本身按照官方出的沒問題, 但是在ulua的目錄里, 總是報protobuf找不到的錯誤. 前后對比了下我生成的lua和ulua官方生成的代碼里

            發現居然他修改了地址改為了

            local protobuf = require "protobuf/protobuf"

            好吧, 只有修改生成器代碼protoc-gen-lua\plugin\protoc-gen-lua中第412行改為

            lua('local protobuf = require "protobuf/protobuf"\n')
             

            生成消息無法找到Descriptor反射查信息

            在LuaFramework\ToLua\Lua\protobuf\protobuf.lua的939行添加
            message_meta._member.Descriptor = descriptor
            在消息里就可以通過msg.Descriptor獲得此消息的反射信息

            由于proto文件定義的內容過多導致的lua local超過限制的警告

            image

            這個錯誤真是讓我哭笑不得, protoc-gen-lua的可用性再一次被懷疑

            我們的協議好歹分成了接近100個, 每個里面消息和數據是混合的, 更別說有些童鞋喜歡把一個項目的協議全寫在一個文件里, 那生成的local數量簡直是酸爽

             

             

             

            后記

            搜索protoc-gen-lua時, 無意間又搜到3年前自己的博文http://www.shnenglu.com/sunicdavy/archive/2013/04/24/199693.html

            記得那個時候準備在服務器使用lua, 還好沒這么干, 轉了go, 否則后果不堪設想

            lua上使用pb其實并不容易, 云風的pbc寫的不錯, 但怕有坑, sproto直接不兼容現有項目, 風險大于易用性所以果斷棄用

            因此, 看來有必要自己寫一個支持良好的lua pb庫

            posted @ 2016-05-31 11:26 戰魂小筑 閱讀(4978) | 評論 (0)編輯 收藏

            一個手游的圖形技術關鍵性指標是: 內存占用, DrawCall和包大小. 

            這三個參數是訓練有素的程序和UI美術都需要關注的重要問題

            接下來我們來講解下UI美術怎么對待這三個問題

            內存占用

            手機的內存不會明顯區分內存和顯存, 大部分都是共享訪問的. 這里說的內存, 一般可以通過一些工具直接看到

            , 比如說XCode等

            圖形上對內存影響最大的就是紋理, 而紋理上最關鍵的問題就是紋理的大小, 也就是紋理面積

            我曾經見過一些訓練不是那么有素的UI美術, 用鼠標選中幾個png文件, 點擊屬性告訴我: 諾, 你都看到了, 我這邊的圖片

            才占幾kb, 為啥你總是說內存占用大

            紋理的內存占用, 只決定于紋理的面積以及發色數, 紋理面積就是長乘寬(像素), 發色數就是一般常說的: 16位色, 32位色

            之所以把內存占用放在首位, 是因為, 大多數的手機一旦超過限定內存就會開始清理后臺掛起的程序, 實在清理不了只有殺掉最占內存的程序

            這就肯定殺到了你寫的游戲之上

            轉載請注明http://www.shnenglu.com/sunicdavy,戰魂小筑, 否則木有小JJ

            DrawCall(DC)

            美術來理解這個概念可以這么說:  繪制一張圖片需要耗費1次DC, 假設界面上有10個圖標, 那就需要耗費10個DC

            而一般手游的DC需要限定在150個之內, 如何降低DC呢, 就需要通過Atlas技術來合并圖片

            將多張圖片打到一張紋理上的技術被叫做Atlas, 俗稱大圖或者圖集, 被打之前的圖也就叫小圖

            大圖上的每個圖元素叫做精靈

            每個精靈被繪制無數次最終也只會耗費1個DC

            但我們不能把所有游戲用到的圖片都打成圖集, 這并不劃算

            我們會根據圖的使用頻率, 用途來按需打圖集

            比如說:

            1. 進游戲只看一次的宣傳圖, 為了方便制作和加載迅速, 做一張整圖動態加載會比較好

            2. 反復查看的圖標, 因為數量相對固定,數量不會膨脹, 我們就做成圖集

            3. 但是類似于刀塔傳奇中50+英雄, 普通玩家看不到那么多英雄但又被打成圖集是不劃算的, 所以損失一點DC按小圖繪制及加載是正確方法

            4. 一般時候, 我們將尺寸小的圖片打成圖集, 配合大尺寸圖片同時加載

            轉載請注明http://www.shnenglu.com/sunicdavy,戰魂小筑, 否則木有小JJ

            包大小

            包大小對于游戲來說, 會影響的是玩家首次下載的時間, 如果連游戲都不下載, 做的再漂亮的游戲也是沒用的

            降低包大小的方法很多, 例如:

            1. 分包機制. 先玩小包, 根據需要下載大包, 多見于MMORPG

            2. 良好的資源管理方法及習慣

            3. 剔除冗余資源

            4. 盡量使用3D渲染代替2D紋理圖片

            以上3個概念是游戲美術, 程序必須了解的重要概念

            但一個合格的美術, 除了事后優化, 還需要做的是事前優化

            事前優化包括: 在游戲立項后, UI美術需要了解基本的游戲功能設計方案

            出一套基本的對話框, 提示框, 圖標裝飾等的圖素, 這些資源往往只有不到512見方的資源

            利用這些圖片可以拼湊出70%的界面及美化效果

            在這之后的UI內容, 只是特效,動畫的設計.

            轉載請注明http://www.shnenglu.com/sunicdavy,戰魂小筑, 否則木有小JJ

             

            一些道理:

            1. 進游戲因為內存超標就崩潰, 再漂亮的圖片也是沒用的.

            2. 游戲是多門藝術的綜合, 游戲美術的不僅要畫的好, 還要能做出優化的好的資源

            3. 手游和端游的美術資源標準有本質區別, 資源做出來是給人看的, 不是屏幕. 因此高低分辨率的搭配, 尤為重要.

            4. 還是那句話, 多看看別人做的游戲, 多問問別人怎么做的

            posted @ 2016-04-28 13:55 戰魂小筑 閱讀(4557) | 評論 (1)編輯 收藏

            最近在項目中進行資源優化. 我們的項目一直以來都是以傳統的電子表格配置為中心的資源驅動加載方法, 拿角色攜帶的特效要播放出來這個case來具體點說就是:

            1. 技能部分的特效可以遍歷動作表播放的所有特效id, 提前預載

            2. buff類特效是動態確定的,無法分析. 需要通過角色表添加資源id在加載角色時加載特效

            這種做法的缺點:

            當角色特效效果調整時, 美術和策劃需要調整特效id表. 多出來不用的特效也加載是感覺不出來的, 分析也是很困難的

            所以這種以傳統的電子表格配置為中心的方式在Unity3D里, 內存, 包優化會是個大問題.

             

            那么, 什么是Unity3D的開發核心思想?

            除了組件思想外, 就是Prefab, 貫徹整個編輯器及引擎自始至終

             

            處理角色攜帶特效加載后播放的這個case, 用Prefab為中心的資源管理來做的話, 大概就是這樣:

            1. 程序編寫一個角色特效列表腳本, 把List暴露出來可以在編輯器里使用

            2. 美術在做技能時, 把要用到的特效拖拽到List中

            3. 特效無需再編制全局ID編碼

            4. 策劃根據這個角色掛接的特效索引, 在配置表里添加播放指令

            這樣做的優點:

            角色引用到的資源才會被打到最終游戲包內, 不使用的資源是不會被加載的

             

            類似的, 在UI特效里, 也應該是將要播放的特效掛接到對象中, 而不是動態通過代碼去加載

            在Unity3D中, Prefab將圖片,Shader, 特效, 腳本等一切平等看待, 只要有引用, 一次性加載.

            同時, 也可以通過靜態工具分析Prefab.

            如果是通過代碼加載的效果, 則只能讓程序員做優化, 這種過程無法讓Unity3D官方后期提供的工具進行優化

             

            所以, 推薦使用Prefab為中心的資源管理模式

            posted @ 2016-03-24 16:33 戰魂小筑 閱讀(2051) | 評論 (0)編輯 收藏

            本文編寫時, Google 官方的 protobuf 版本是3.0.0beta

            下面介紹下proto3的一些細節變化

            Proto3的語法變化

            語法標記

            這個版本的protoc的protobuf編譯器已經可以支持proto2語法和proto3的語法

            如果你的proto文件沒有添加syntax說明的話, 用這個版本的編譯器會報錯, 提示你默認proto2支持, 請添加語法標記

            syntax = "proto2";

             

            optional不需要了

            只保留repeated標記數組類型, optional和required都被去掉了

            實際使用證明, required的設計確實是蛋疼, C++的調試版會彈出assert,release版和optional也沒啥區別

            map支持

            map編寫格式為

            map<key_type, value_type> map_field = N;
            例如:
            map<string, Project> projects = 3;
            代碼生成確認支持map, 這對于很多語言來說又可以偷懶了

            字段default標記不能使用了

            位于proto2語法的字段number后的[default=XX]

            這個東西不能用了, 理由是:

            對于同一段序列化后的數據, 如果序列化端的default和反序列化端的default描述不一樣會導致最終結果完全不一致

            即: 同一個數據兩個結果, 這是不可預測的結果, 因此干掉這個特性

            不過本人覺得, 對于游戲來說, 這個功能本身可以壓縮很多數據,雖然會有隱患

             

            枚舉默認值一定是0

            proto2里的默認值是枚舉的第一個value對應的值, 不一定為0

            proto3在你定義value時, 強制要求第一個值必須為0

            這個修改為避免隱患還是有幫助的

            泛型描述支持

            any類型, 可以代表任何類型, 可以先讀進來, 再進行解析, 沒具體用, 步子跨大了怕扯到蛋

            支持json序列化

            這個極好, json再次被同化了

            增加了多種語言支持

            js, objc, ruby, C#等等

            然而, C#版本的基礎runtime庫是用C# 6.0的語法寫的,這對于Unity mono祖傳2.0來說, 確實扯到蛋了,沒法用

            Protobuf現在使用CMAKE做配置系統

            編譯起來稍微麻煩, 還要下個被墻掉的cmake…

             

             

            第三方庫里對于proto3的變化

            Golang的官方protobuf支持: https://github.com/golang/protobuf

            生成代碼中的結構體字段類型變化

            對于proto2的文件, 生成的go代碼中的結構體依然使用類型指針作為默認存儲, 兼容老的系統

            對于proto3的文件, 生成的go代碼中的結構體直接使用字段作為默認存儲, 不再使用GetXXX來作為字段值訪問, 賦值時也無需使用proto.類型() 函數進行指針類型字段值創建.

            這個調整很是方便, 但丟失了optional判斷功能, 對應C++里就是hasXXX的功能, 不過好歹這個邏輯現在用的不多了

            這個修改大概也是配合json序列化來做的, go默認的json序列化時, 無法使用proto2生成的結構體的, 因為都是指針,無法賦值..

             

            新版protoc-gen-go的插件會生成descriptor的壓縮數據

            新插件會給每次生成的文件添加這樣一段代碼

            var fileDescriptor0 = []byte{
                // 220 bytes of a gzipped FileDescriptorProto
                0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x8f, 0xcd, 0x4e, 0xc5, 0x20,
                0x10, 0x85, 0x53, 0xbd, 0x35, 0x32, 0xb7, 0xdd, 0x4c, 0x5c, 0xb0, 0x70, 0x71, 0xd3, 0xb8, 0x70,
                0x75, 0x17, 0xfa, 0x04, 0xc6, 0xd8, 0xb8, 0x50, 0x63, 0xa8, 0x2f, 0x80, 0xed, 0x44, 0x89, 0x28,
                0x04, 0xc6, 0xbf, 0x47, 0xf1, 0x6d, 0x95, 0x49, 0x8d, 0x4d, 0x5c, 0x01, 0xdf, 0x39, 0x7c, 0x30,
                0x00, 0x1c, 0x82, 0xdf, 0xc6, 0x14, 0x38, 0xe0, 0xaa, 0xec, 0xbb, 0x37, 0x68, 0x2e, 0x3e, 0x62,
                0x48, 0x7c, 0x49, 0x76, 0xa2, 0x84, 0x47, 0xd0, 0xde, 0x96, 0xf8, 0xee, 0x33, 0xd2, 0x8d, 0x7d,
                0x26, 0x5d, 0x6d, 0xaa, 0x63, 0x65, 0xda, 0xb8, 0x84, 0xd8, 0x41, 0x63, 0xc2, 0x7b, 0xef, 0xc8,
                0x4f, 0x52, 0xda, 0x91, 0x52, 0x93, 0x16, 0x0c, 0x0f, 0x41, 0x89, 0xa9, 0x77, 0x9e, 0xf4, 0xae,
                0x14, 0x54, 0xfc, 0x05, 0xdd, 0x57, 0x05, 0x4a, 0xba, 0xd7, 0xc4, 0x16, 0xb7, 0x80, 0x03, 0x27,
                0xf7, 0xf2, 0x70, 0x72, 0xe5, 0x32, 0x0f, 0xd1, 0x3b, 0xa6, 0x34, 0x5b, 0x31, 0xff, 0x4b, 0x70,
                0x03, 0x6b, 0x43, 0x91, 0x2c, 0x9f, 0x3f, 0xd2, 0xf8, 0x24, 0xf6, 0x7d, 0xb3, 0x4e, 0x7f, 0x08,
                0x0f, 0xa0, 0x3e, 0xf3, 0xce, 0x66, 0xbd, 0x12, 0x49, 0x6d, 0xcb, 0xa1, 0x4c, 0x37, 0xbf, 0xf3,
                0xb3, 0xbc, 0x8e, 0xac, 0x6b, 0xb9, 0xd9, 0xe6, 0x25, 0xbc, 0xdf, 0x93, 0x6f, 0x9e, 0x7e, 0x07,
                0x00, 0x00, 0xff, 0xff, 0x0c, 0x9f, 0x10, 0xa8, 0x2e, 0x01, 0x00, 0x00,
            }

            對于meta信息的提取還是很方便的

            然而

            對于多個文件的生成, 這樣做非常的麻煩, 因為這個字段會重復導致編譯錯誤

            很多人在論壇里吐槽, 官方給出的解決方法是, 使用protoc一次性傳入一個package下的所有的proto直接生成一個go

            而不是現在的一個proto一個go

            生成代碼會自動注冊到全局, 并可以方便的查詢

            以前這個代碼需要自己來做, 現在官方提供了支持, 很是方便

            然而, 為什么不支持遍歷… 殘念啊, 又要自己動手了

            posted @ 2016-01-25 14:23 戰魂小筑 閱讀(27164) | 評論 (0)編輯 收藏

            項目地址:

            https://github.com/davyxu/tabtoy

            posted @ 2016-01-25 14:00 戰魂小筑 閱讀(5494) | 評論 (2)編輯 收藏

            最近碰到一個蹊蹺的設備相關問題。我們的游戲使用的是Unity3D 4.X 真機測試環境都是ios8越獄,從iPhone6,iPad3到iPhone5s都有。所有包在我們本機測試都是OK的,結果包發出去, 在iTouch5,iPhone6s這些2015年新出的設備上一律卡進度條

            隨即,我們進行了分析。期初推斷是arm64引起的問題,嘗試調整為il2cpp同時啟用armv7和arm64的通用包,問題沒有解決。

            繼續分析:因為游戲正常啟動, 只是初次加載卡進度條, 那么可以排除是arm64位問題導致的,因為如果是不兼容包, 在安裝時直接會報出架構錯誤,無法正常安裝。

            給游戲內部加入了一個HTTP日志系統, 給服務器報錯。跟蹤了一次, 結果發現了一些奇怪日志

            image

            在檢測下載之前的加載沒有出現任何問題

            但是下載錯誤報了兩次, 第一個錯誤在我們本機也會報,但可以忽略。 但第二個錯誤只有iTouch5,iPhone6s會出現

            報錯后, 所有日志都出現了兩次。

            對比了下代碼,發現了一些邏輯漏洞。但同時需要注意的是, 這個bug的問題的核心就是在這一個錯誤描述上

            The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.

            查過文章發現,ios9開始默認要求所有的app的HTTP訪問必須使用HTTPS加密協議保證安全

            所以結合前面的測試環境, 證明這個問題確定被修復

            posted @ 2016-01-07 14:56 戰魂小筑 閱讀(1564) | 評論 (3)編輯 收藏

            僅列出標題
            共26頁: 1 2 3 4 5 6 7 8 9 Last 
            久久久久久久久久免免费精品 | 中文无码久久精品| 亚洲AⅤ优女AV综合久久久| 亚洲欧美日韩精品久久亚洲区 | 99久久人人爽亚洲精品美女| 久久久久亚洲爆乳少妇无| 色诱久久久久综合网ywww| 99久久精品国产一区二区三区| 日日狠狠久久偷偷色综合免费| 午夜不卡久久精品无码免费| 国产精品热久久无码av| 久久成人国产精品| 伊人久久五月天| 天天综合久久久网| 精品久久久久久国产| 国产午夜精品久久久久九九电影| 午夜天堂av天堂久久久| 区久久AAA片69亚洲| 久久夜色撩人精品国产| 久久免费国产精品一区二区| 日日噜噜夜夜狠狠久久丁香五月| 香蕉aa三级久久毛片| 国产精品成人99久久久久91gav| 九九精品99久久久香蕉| 无码人妻久久一区二区三区 | 中文字幕久久久久人妻| 亚洲日韩欧美一区久久久久我| 国产精品无码久久久久| 国产精品午夜久久| 久久综合久久久| 久久久综合九色合综国产| 成人久久久观看免费毛片| 久久久久亚洲av无码专区| 亚洲va久久久噜噜噜久久天堂| 伊人久久大香线蕉精品不卡| 伊人久久大香线蕉综合5g| 久久99热这里只有精品66| 久久AV高潮AV无码AV| 一本色道久久综合亚洲精品| 亚洲天堂久久久| 久久久精品人妻一区二区三区四 |