http://www.manew.com/4136.html

游戏内的角色,能够像纸娃娃换装那样子让玩家可以为自己的角色改变外观,一直是相当受欢迎的功能;一般而言,我们建好的 3D 模型,如果要将其中一个部位换成另外一个形状,最直接的就是将该物件部位的 Mesh 替换掉,那么外观就改变了,但这种方法如果运用在需要做动作的模型上,将发现被置换掉的部位不会正常动作,更糟的状况可能连模型显示的位置及方向都是错误的,所以,直接变更 Mesh 的方法只适用于静态模型物件,为此,我们必须找出更深入的方法来做换装的功能,幸好,此部份 Unity 官方已经有提供相关范例可以参考。

Unity 官方提供的人物换装范例可以从官网下载 Character Customization,或是开启 Unity 编辑器的 Window > Asset Store 在 Complete Projects > Tutorials 找到 Character Customization 下载并汇入到自己的专案中。这个范例提供相当完整的示范,而且考虑到实作于游戏中时,不可能一次把全部的资源都载入,所以将模型、材质、纹理等资源都包成 Asset bundle,只在要使用到时才载入需要的部份;也因为如此,对於不了解
Asset bundle 的情况下,要透过这个范例来直接学习换装也相对变得困难;另外,范例中也对资源规范了特定形式的命名规则,主要也是为了建立 Asset bundle 内容资料及从 Asset bundle 取出资源而设计,在不了解这些规则之前,想要透过此范例学习换装,有一定程度的难度;当然,如果愿意使用与范例中 characters 目录中两个人物的模型、材质、纹理完全相同的命名规则及档案配置方式,几乎可以直接套用到自己的游戏中,而不太需要了解内部的运作方式。

在 Unity 开启 Asset store,在Asset store 中找出 Character Customization 范例。

虽然,官方这个范例能够直接套用,但制作游戏常会有不同的客制化需求,如果不了解相关原理和流程的话,可能就无法自由灵活的运用,所以,以下将利用这个范例并排除掉 Asset bundle 的部份,直接在场景中完成纸娃娃换装的方法。

首先,先来看看模型的结构,从 Projects 视窗将 CharacterCustomization > characters > Female > Female 拉到场景中,在 Hierarchy 视窗将物件展开来,会发现几个名称相同并使用数字区别的物件,它们分别代表人物各部位的模型,由此可知,整个人物模型档包含多个相同部位的模型,而 Famale_Hips 则是整个人物的骨架结构,人物的动作则是设置在顶层物件(Female)的 Animation,所以这个模型档是个模型资源,而不是实际上要放到在场景中的目标物件。

每个部位有多个模型物件

了解模型档内容後,接下来先建立一个名为 TestChar 的 C# 程式档用来控制换装,为了方便测试,在 Projects 视窗将 CharacterCustomization > characters > Female > Female@walk 的 Animation Wrap Mode 改为 Loop,并在程式档的 Start() 内加入 animation.Play(“walk”),如此在执行状态将会使人物不断的做走路的动作。

选择 Female@walk

Animation Wrap Mode 选择 Loop

Unity 官方这个范例,说穿了就是将模型档做为来源模型资源,然後再依照需求将各部位重新组合成一个新的目标模型,所以我们直接将人物模型 Female 拉两个到场景中,分别为它们命名为 Source 及 Target,依照以下步骤做些准备动作:

从 Projects 视窗将 CharacterCustomization > characters > Female > Per Texture Materials 依照名称把适当的材质球(Material) 拉给 Source 的每个部位(不包含 Female_Hips 及其子物件)。

Source 物件是做为来源资源使用,实际在场景中不需要运作,所以直接点选 Source 物件并将 Inspector 视窗中 Source 名称栏位前的方框取消勾选来将它关闭。

Source 前面的方框取消勾选,取消勾选后会弹出对话视窗询问是否希望关闭全部的子物件,点击 Deactivate Chidren。把 Target 物件中除了 Famale_Hips 以外的子物件全部删除。把 TestChar 程式档拉给 Target 物件。Source 中的各部位名称应该都要有编号(例如 face-1 ),如果没有的话,加上编号。

完成以上的准备动作,接下来就要开始来写程式了,程式主要工作是先将 Source 中每个物件的 SkinnedMeshRenderer 资料取出储存在 data 变数中,data 的内容则是依照部位分类,接下来在 Target 加入 SkinnedMeshRenderer  ,然后每个部位取出一个指定的资料,利用 CombineInstance class 及 Mesh.CombineMeshes() 将各部位模型合并,同时也重新排列材质,然後依照取出的 SkinnedMeshRenderer 的 bone 的名称,找到与
Target 的 Female_Hips 子物件内名称相对应的物件重建骨架列表,最后将这些重新组合建立的资料给 Target 的 SkinnedMeshRenderer,如此就可完成换装的动作,以下为程式说明:

  1.  
  2.  
  3.  
  4.  
  5. 001
  6. //来源模型资源的物件
  7. 002
  8.  
  9. 003
  10.   public Transform source;
  11. 004
  12.  
  13. 005
  14.   //目标物件
  15. 006
  16.  
  17. 007
  18.   public Transform target;
  19. 008
  20.  
  21. 009
  22.   //模型资源资料
  23. 010
  24.  
  25. 011
  26.   private Dictionary<string , Dictionary<string,SkinnedMeshRenderer>> data = new Dictionary<string, Dictionary<string,SkinnedMeshRenderer>>();
  27. 012
  28.  
  29. 013
  30.   void Start () {
  31. 014
  32.  
  33. 015
  34.   //从来源模型资源取出各部位的 SkinnedMeshRenderer
  35. 016
  36.  
  37. 017
  38.   SkinnedMeshRenderer[] parts = source.GetComponentsInChildren<SkinnedMeshRenderer>(true);
  39. 018
  40.  
  41. 019
  42.   foreach(SkinnedMeshRenderer part in parts){
  43. 020
  44.  
  45. 021
  46.   //利用 ? 字元分隔档名做为资料结构的 key,档名为 部位?编号 储存为 [部位][编号]=SkinnedMeshRenderer资料
  47. 022
  48.  
  49. 023
  50.   string[] partName = part.name.Split('?');
  51. 024
  52.  
  53. 025
  54.   // 在 data 加入资料
  55. 026
  56.  
  57. 027
  58.   if(!data.ContainsKey(partName[0])) data.Add(partName[0] , new Dictionary<string,SkinnedMeshRenderer>());
  59. 028
  60.  
  61. 029
  62.   data[partName[0]].Add(partName[1],part);
  63. 030
  64.  
  65. 031
  66.   }
  67. 032
  68.  
  69. 033
  70.   //目标物件加入 SkinnedMeshRenderer
  71. 034
  72.  
  73. 035
  74.   SkinnedMeshRenderer targetSmr = target.gameObject.AddComponent<SkinnedMeshRenderer>();
  75. 036
  76.  
  77. 037
  78.   //从目标物件取得骨架资料 (Female_Hips 的全部物件)
  79. 038
  80.  
  81. 039
  82.   Transform[] hips = target.GetComponentsInChildren<Transform>();
  83. 040
  84.  
  85. 041
  86.   /** 开始 重组模型 */
  87. 042
  88.  
  89. 043
  90.   //初始化资料列表
  91. 044
  92.  
  93. 045
  94.   List<CombineInstance> combineInstances = new List<CombineInstance>();
  95. 046
  96.  
  97. 047
  98.   List<Material> materials = new List<Material>();
  99. 048
  100.  
  101. 049
  102.   List<Transform> bones = new List<Transform>();
  103. 050
  104.  
  105. 051
  106.   foreach(KeyValuePair<string , Dictionary<string,SkinnedMeshRenderer>> _part in data){
  107. 052
  108.  
  109. 053
  110.   //从资料中取得各部位指定编号的 SkinnedMeshRenderer
  111. 054
  112.  
  113. 055
  114.   SkinnedMeshRenderer smr = new SkinnedMeshRenderer();
  115. 056
  116.  
  117. 057
  118.   switch(_part.Key){
  119. 058
  120.  
  121. 059
  122.   case eyes”:
  123. 060
  124.  
  125. 061
  126.   smr = _part.Value[“1”];
  127. 062
  128.  
  129. 063
  130.   break;
  131. 064
  132.  
  133. 065
  134.   case face”:
  135. 066
  136.  
  137. 067
  138.   smr = _part.Value[“1”];
  139. 068
  140.  
  141. 069
  142.   break;
  143. 070
  144.  
  145. 071
  146.   case hair”:
  147. 072
  148.  
  149. 073
  150.   smr = _part.Value[“1”];
  151. 074
  152.  
  153. 075
  154.   break;
  155. 076
  156.  
  157. 077
  158.   case pants”:
  159. 078
  160.  
  161. 079
  162.   smr = _part.Value[“1”];
  163. 080
  164.  
  165. 081
  166.   break;
  167. 082
  168.  
  169. 083
  170.   case shoes”:
  171. 084
  172.  
  173. 085
  174.   smr = _part.Value[“1”];
  175. 086
  176.  
  177. 087
  178.   break;
  179. 088
  180.  
  181. 089
  182.   case top”:
  183. 090
  184.  
  185. 091
  186.   smr = _part.Value[“1”];
  187. 092
  188.  
  189. 093
  190.   break;
  191. 094
  192.  
  193. 095
  194.   }
  195. 096
  196.  
  197. 097
  198.   //准备要组合的 Mesh
  199. 098
  200.  
  201. 099
  202.   CombineInstance ci = new CombineInstance();
  203. 100
  204.  
  205. 101
  206.   ci.mesh = smr.sharedMesh;
  207. 102
  208.  
  209. 103
  210.   combineInstances.Add(ci);
  211. 104
  212.  
  213. 105
  214.   //排列新的材质列表
  215. 106
  216.  
  217. 107
  218.   materials.AddRange(smr.materials);
  219. 108
  220.  
  221. 109
  222.   //取得相对应名称的骨架物件来建立新的骨架列表
  223. 110
  224.  
  225. 111
  226.   foreach(Transform bone in smr.bones){
  227. 112
  228.  
  229. 113
  230.   foreach(Transform hip in hips){
  231. 114
  232.  
  233. 115
  234.   if(hip.name != bone.name) continue;
  235. 116
  236.  
  237. 117
  238.   bones.Add(hip);
  239. 118
  240.  
  241. 119
  242.   break;
  243. 120
  244.  
  245. 121
  246.   }
  247. 122
  248.  
  249. 123
  250.   }
  251. 124
  252.  
  253. 125
  254.   }
  255. 126
  256.  
  257. 127
  258.   //合并 Mesh 并写入至 Target 的 SkinnedMeshRenderer
  259. 128
  260.  
  261. 129
  262.   targetSmr.sharedMesh = new Mesh();
  263. 130
  264.  
  265. 131
  266.   targetSmr.sharedMesh.CombineMeshes(combineInstances.ToArray() , false , false);
  267. 132
  268.  
  269. 133
  270.   // Target 的 SkinnedMeshRenderer 写入新骨架列表
  271. 134
  272.  
  273. 135
  274.   targetSmr.bones = bones.ToArray();
  275. 136
  276.  
  277. 137
  278.   // Target 的 SkinnedMeshRenderer 写入新材质列表
  279. 138
  280.  
  281. 139
  282.   targetSmr.materials = materials.ToArray();
  283. 140
  284.  
  285. 141
  286.   /** 重组模型 结束 */
  287. 142
  288.  
  289. 143
  290.   //指定播放走路动作
  291. 144
  292.  
  293. 145
  294.   animation.Play(“walk”);
  295. 146
  296.  
  297. 147
  298.   }
  299. 148
  300.  

写完程式后,记得把场景中的 Source 及 Target 两个物件分别拉给附属在 Target 物件上的 TestChar script 的 source 及 target 栏位;

程式动作都在 Start() 内进行,是因为最初目标物件并没有模型等资料,所以要先依照指定的各部位资料把人物建立出来并使它动作,而 smr = _part.Value[“1”]; 的 “1” 则是表示指定此部位的 “1” 模型资料,所以只要改变各部位的这个值,就能为人物配置不同的造型,当然,前题是来源模型资源必须要有这个编号的物件才行;以上程式码主要是测试及解说流程用,在实作上应该把标示 /** 重组模型 */ 这一段程式独立出来,在需要换装时,给予各部位指定编号来执行。

以上是 Unity 官方范例中处理换装的方法,它把各部位模型、材质等资料重新组合合并成单一的模型并重建骨架列表,如此即使看起来人物身上有其中一个部位被置换了,仍能持续正常动作;当查看 Target 物件时会发现它的子物件仍然维持不变,只有 Target 物件本身在 Inspector 视窗中的 Component 多出了 Skinned Mesh Renderer 及各部位的 Material,如果查看 SkinnedMeshRenderer 的 Mesh 栏位 也会发现看不到任何的 Mesh。

Target 物件的内容

这种做法的来源模型与材质数量必须相对应,否则模型的贴图将会变得不正常,也就是说如果裤子的 material 有两个,其他部位的 materail 只有一个,那麽结果模型上的贴图将与预期的不同;为了使各部位的 material 使用上更为弹性,前面的程式将做些修改,使它的各部位都是独立的 GameObject,如下所示:

  1.  
  2.  
  3.  
  4.  
  5. 001
  6. //来源模型资源的物件
  7. 002
  8.  
  9. 003
  10.   public Transform source;
  11. 004
  12.  
  13. 005
  14.   //目标物件
  15. 006
  16.  
  17. 007
  18.   public Transform target;
  19. 008
  20.  
  21. 009
  22.   //模型资源资料
  23. 010
  24.  
  25. 011
  26.   private Dictionary<string , Dictionary<string,Transform>> data = new Dictionary<string, Dictionary<string,Transform>>();
  27. 012
  28.  
  29. 013
  30.   //目标物件的骨架
  31. 014
  32.  
  33. 015
  34.   private Transform[] hips;
  35. 016
  36.  
  37. 017
  38.   //目标物件各部位的 SkinnedMeshRenderer 资料(参照)
  39. 018
  40.  
  41. 019
  42.   private Dictionary<string , SkinnedMeshRenderer> targetSmr = new Dictionary<string, SkinnedMeshRenderer>();
  43. 020
  44.  
  45. 021
  46.   void Start () {
  47. 022
  48.  
  49. 023
  50.   //从来源模型资源取出各部位的 SkinnedMeshRenderer
  51. 024
  52.  
  53. 025
  54.   SkinnedMeshRenderer[] parts = source.GetComponentsInChildren<SkinnedMeshRenderer>(true);
  55. 026
  56.  
  57. 027
  58.   foreach(SkinnedMeshRenderer part in parts){
  59. 028
  60.  
  61. 029
  62.   //利用 ? 字元分隔档名做为资料结构的 key,档名为 部位?编号 储存为 [部位][编号]=Transform资料
  63. 030
  64.  
  65. 031
  66.   string[] partName = part.name.Split('?');
  67. 032
  68.  
  69. 033
  70.   // 在 data 加入资料
  71. 034
  72.  
  73. 035
  74.   if(!data.ContainsKey(partName[0])){
  75. 036
  76.  
  77. 037
  78.   data.Add(partName[0] , new Dictionary<string,Transform>());
  79. 038
  80.  
  81. 039
  82.   //建立新的 GameObject 并使用部位名称来命名,指定为目标物件的子物件
  83. 040
  84.  
  85. 041
  86.   GameObject partObj = new GameObject();
  87. 042
  88.  
  89. 043
  90.   partObj.name = partName[0];
  91. 044
  92.  
  93. 045
  94.   partObj.transform.parent = target;
  95. 046
  96.  
  97. 047
  98.   //为新建立的 GameObject 加入 SkinnedMeshRenderer,并将此 SkinnedMeshRenderer 存入 targetSmr
  99. 048
  100.  
  101. 049
  102.   targetSmr.Add(partName[0] , partObj.AddComponent<SkinnedMeshRenderer>());
  103. 050
  104.  
  105. 051
  106.   }
  107. 052
  108.  
  109. 053
  110.   data[partName[0]].Add(partName[1],part.transform);
  111. 054
  112.  
  113. 055
  114.   }
  115. 056
  116.  
  117. 057
  118.   //从目标物件取得骨架资料 (Female_Hips 的全部物件)
  119. 058
  120.  
  121. 059
  122.   hips = target.GetComponentsInChildren<Transform>();
  123. 060
  124.  
  125. 061
  126.   /** 开始 重组模型 */
  127. 062
  128.  
  129. 063
  130.   foreach(KeyValuePair<string , Dictionary<string,Transform>> _part in data){
  131. 064
  132.  
  133. 065
  134.   switch(_part.Key){
  135. 066
  136.  
  137. 067
  138.   case eyes”:
  139. 068
  140.  
  141. 069
  142.   ChangePart(“eyes , 1”);
  143. 070
  144.  
  145. 071
  146.   break;
  147. 072
  148.  
  149. 073
  150.   case face”:
  151. 074
  152.  
  153. 075
  154.   ChangePart(“face , 1”);
  155. 076
  156.  
  157. 077
  158.   break;
  159. 078
  160.  
  161. 079
  162.   case hair”:
  163. 080
  164.  
  165. 081
  166.   ChangePart(“hair , 1”);
  167. 082
  168.  
  169. 083
  170.   break;
  171. 084
  172.  
  173. 085
  174.   case pants”:
  175. 086
  176.  
  177. 087
  178.   ChangePart(“pants , 1”);
  179. 088
  180.  
  181. 089
  182.   break;
  183. 090
  184.  
  185. 091
  186.   case shoes”:
  187. 092
  188.  
  189. 093
  190.   ChangePart(“shoes , 1”);
  191. 094
  192.  
  193. 095
  194.   break;
  195. 096
  196.  
  197. 097
  198.   case top”:
  199. 098
  200.  
  201. 099
  202.   ChangePart(“top , 1”);
  203. 100
  204.  
  205. 101
  206.   break;
  207. 102
  208.  
  209. 103
  210.   }
  211. 104
  212.  
  213. 105
  214.   }
  215. 106
  216.  
  217. 107
  218.   /** 重组模型 结束 */
  219. 108
  220.  
  221. 109
  222.   //指定播放走路动作
  223. 110
  224.  
  225. 111
  226.   target.animation.Play(“walk”);
  227. 112
  228.  
  229. 113
  230.   }
  231. 114
  232.  
  233. 115
  234.   private void ChangePart(string part , string item){
  235. 116
  236.  
  237. 117
  238.   //从资料中取得各部位指定编号的 SkinnedMeshRenderer
  239. 118
  240.  
  241. 119
  242.   SkinnedMeshRenderer smr = data[part][item].GetComponent<SkinnedMeshRenderer>();
  243. 120
  244.  
  245. 121
  246.   //取得相对应名称的骨架物件来建立新的骨架列表
  247. 122
  248.  
  249. 123
  250.   List<Transform> bones = new List<Transform>();
  251. 124
  252.  
  253. 125
  254.   foreach(Transform bone in smr.bones){
  255. 126
  256.  
  257. 127
  258.   foreach(Transform hip in hips){
  259. 128
  260.  
  261. 129
  262.   if(hip.name != bone.name) continue;
  263. 130
  264.  
  265. 131
  266.   bones.Add(hip);
  267. 132
  268.  
  269. 133
  270.   break;
  271. 134
  272.  
  273. 135
  274.   }
  275. 136
  276.  
  277. 137
  278.   }
  279. 138
  280.  
  281. 139
  282.   // 更新指定部位 GameObject 的 SkinnedMeshRenderer 内容
  283. 140
  284.  
  285. 141
  286.   targetSmr[part].sharedMesh = smr.sharedMesh;
  287. 142
  288.  
  289. 143
  290.   targetSmr[part].bones = bones.ToArray();
  291. 144
  292.  
  293. 145
  294.   targetSmr[part].materials = smr.materials;
  295. 146
  296.  
  297. 147
  298.   }
  299. 148
  300.  

在建立 data 变数内容时,同时为每个部位建立 GameObject,另外也把变更部位内容的程式码独立出来为 ChangePart 方法,如此在每次需要变更该部位时,只要指定部位名及编号就可以直接为该部位换装,而不需要将每个部位都重建;因为每个部位都是 GameObject 实体,我们在 Hierarchy 或 Scene 视窗中点选该部位也可以清楚的从 Inspector 视窗中看到此部位内容,正因如此,每个部位就可以自由配置 Material 的数量了。

从以上程式中会发现换装除了把 Mesh 和 Material 从来源取出给目标置换之外,有个关键的地方是重建骨架列表,为什麽要重建骨架列表呢?最主要是变更 Mesh 之後的 SkinnedMeshRenderer.bones 及 SkinnedMeshRenderer.sharedMesh.bindposes 数量有可能会不同而产生错误讯息 Number of bind poses doesn't match number of bones in skinned mesh,即使数量相同而没有错误讯息,SkinnedMeshRenderer.sharedMesh.bindposes
内的 Matrix4x4[] 资料也会因为数值不正确而发生执行期模型扭曲成奇怪形状的问题;这部份可以将 Female 模型档汇入到 3DS Max 中查看,以鞋子为例,在 Modify 视窗中,可以很明显看出 shoes-1 和 shoes-2 的 Bones 列表内容是不同的,所以在为模型物件变更 Mesh 的同时必须重建骨架列表。

以上的说明主要是用于了解换装所需要的做法,实作时,不太可能把游戏中的角色全身各部位的模型资料全部都载入做为来源资料,例如游戏中的武器有100种,角色背包中有3种武器,但为了换装却把100种武器都载入到游戏中,而实际上此角色最多也只能变换背包中的3种武器而已,这样无疑是浪费了97种武器所占用的资源;所以在了解如何换装後,实作时应该尽量像官方范例那样把来源资源包装起来,只取出需要的资源来进行换装。

Unity3D教程:换装方法的更多相关文章

  1. unity3d教程-01-安装及使用Unity

    我们前往unity官网:https://unity3d.com/cn/ 选择下载个人版,免费使用,功能齐全,就是在应用启动时有unity的动画 支持正版从我做起 整个安装过程需要网络的支持 下载安装程 ...

  2. 【Unity3D】3D角色换装++ Advance

    http://www.cnblogs.com/dosomething/archive/2012/12/15/2818897.html 本文在之前的文章Unity3D角色换装的原理 基础上做一个补充 给 ...

  3. 【AS3 Coder】任务六:人物换装(纸娃娃)系统的制作

    使用框架:AS3(Flash Professional CS5.0及更高版本 + Flash Buider)任务描述:了解人物换装系统的制作原理难度系数:2 本章源码下载:http://www.iam ...

  4. 3D游戏中人物换装解决方案

    换装基本上是每个网游都必须有的一个功能,每种网游的做法都各有不同,有些是换掉整个模型,有些则是通过可以换掉模型的一个部分完成.前者属于整体换,相对简单些:后者则是通过部分替换实现,目前用的比较多,本文 ...

  5. Unity3D教程:无缝地形场景切换的解决方法

    http://www.unitymanual.com/6718.html 当我们开发一个大型项目的时候-会遇到这样的问题(地形场景的切换)这个只是字面意思-并不是重场景1的100  100 100坐标 ...

  6. Unity3d 换装Avatar系统

    原理就是用新造的部件和角色的骨骼进行重新对接. demo的使用方法: PartIdx设置要换那个部件[0,4],一共5个部件 EquipIdx设置要更换部件的装备索引[0,1],具体看我的Change ...

  7. Unity3d 3d角色换装实现原理及步骤

    http://www.cnblogs.com/dosomething/archive/2012/04/15/2450526.html 1.角色模型制作 unity3d支持Skin动画  但是不支持Ph ...

  8. 【Unity3d】3d角色换装实现原理及步骤

    http://www.cnblogs.com/dosomething/archive/2012/04/15/2450526.html 1.角色模型制作 unity3d支持Skin动画  但是不支持Ph ...

  9. Unity3d 换装 之 模型动画分离

    在手游中换装成了越来越不可缺的一个功能,毫无疑问各式各样的时装为游戏增添了不同的色彩. 对于2D手游,或许是更换对应的序列帧,也或许是如同3D手游一般,更换模型动画. 对于游戏中的人物,一般分为头.上 ...

随机推荐

  1. Windows-速度优化的几个方面

    One. Win+R - > cmd- > msconfig 禁用不需要的启动项! Two. 关闭一些视觉选项 Three. 设置应用启动快捷键

  2. swift,demo,ios8

    swift交流群:342581988,欢迎增加. 刚刚写的小 demo.搞得还是不是太好.请大家拍砖! 能够直接复制执行 import UIKit class ViewController: UIVi ...

  3. java开始到熟悉60

    本次主题:多维数组 1,多维数组的初始话有三种:默认初始化.静态初始化.动态初始化. 这里只讲解静态初始化: 这里以二位数组为例,实际应用中,一维用得最多,二维次之,三维以及三维以上几乎很少使用,而且 ...

  4. 【网络协议】IP协议、ARP协议、RARP协议

    IP数据报 IP是TCP/IP协议族中最核心的协议,全部的TCP.UDP.ICMP.IGMP数据都以IP数据报的格式传输.IP仅提供尽力而为的传输服务.假设发生某种错误.IP会丢失该数据.然后发送IC ...

  5. fruitstrap 安装.app文件

    1. 下载ipa的ios文件然后解压成.app的文件 2. 进入fruitstrap文件夹,copy .app文件到fruitstrap文件夹中 执行./fruitstrap -b umetrip.a ...

  6. JAVA学习之 Model2中的Servlet与.NET一般处理程序傻傻分不清楚

    时隔多日,多日合适吗,应该是时隔多月.我又想起了一般处理程序.这都是由于近期在实现的DRP系统中经经常使用到jsp+servlet达到界面与逻辑的分离.servlet负责处理从jsp传回的信息:每当这 ...

  7. android adapter公共写法

    在开发过程中,会写很多的adapter类,其中很多公共的部分,不需要每次都去书写,可以为开发者省下很多时间 提取一个ListViewAdapter public abstract class List ...

  8. 使用a标签下载文件,而不是直接打开,使用属性 download

    有的时候,下载的链接文件如果是普通文件类型,如txt,我们下载文件的时候,有的浏览器不会弹出下载框,.而是直接打开了该文件. 针对这种情况,我们只需要在a标签上加上download属性即可显示下载框. ...

  9. 浅谈js执行机制

    关于js执行机制,老早之前就一直想写篇文章做个总结,因为和js执行顺序的面试题碰到的特别多,每次碰到总是会去网上查,没有系统地总结,搞得每次碰到都是似懂非懂的感觉,这篇文章就系统的总结一下js执行机制 ...

  10. I2S

    音频数据传输而制定: Inter—IC Sound : 单线 时钟和数据一条线,时分复用: 标准的I2S总线电缆是由3根串行导线组成的:1根是时分多路复用(简称TDM)数据线:1根是字选择线:1根是时 ...