Programming pipeline & shading language
大家好,今天想给大家介绍一下可编程渲染管线和着色器语言的相关基础知识,使想上手SHADER编程的童鞋们可以快速揭开SHADER语言的神秘面纱
由于时间有限,我决定只讲三个主要方面的内容,其过程中肯定会有不详细之处,还请见谅,就算是抛砖引玉,给大家一个简单的入门引路。
本章内容总共分为三个部分 一、3D渲染管线工作流程 二、可编程管线 三、着色器语言
3D渲染管线作为整个工作流程的基础,是不可或缺的基本知识。因此,作一定的讲解是有必要的。 但作为一个回顾内容,就不会对具体的内容进行讲解,比如如何进行坐标系变换,如何进行光栅化等等。 我们仅关注的是整个工作的过程。
甚至,我们更关心的不是整个工作过程中的细节,而是我们所必须要关注的几大流程。 如下图
数据填充
当我们想实现一次渲染效果的时候,数据的提交(填充)是不可缺少的。 因此,工作流程的第一步就是要处理输入的数据。
而我们最直接的接触3D渲染流程的时机,也就是数据填充时,更确切的说,就是那一堆set数据的API。
数据填充允许我们提交我们想要的数据,比如顶点数据(如位置,法线,颜色,纹理坐标等)。常量(如世界矩阵,观察矩阵,投影矩阵,纹理因子等等)。
变换&顶点光照
在这个阶段,顶点会经过世界变换,观察变换,投影变换。 通常情况下,在顶点经过观察变换后,便开始做一些光照计算。 这一阶段也是可编程管线所提供的顶点处理阶段。也就是说,我们可以通过着色器程序来控制这一阶段的结果。 在着色器程序中,我们可以任意地处理想要的数据,比如进行纹理坐标缩放,旋转,随机偏移等等。
裁剪&光栅化
当经过坐标变换和光照后,顶点已经被投影为2D坐标+深度信息。 一些不可见的顶点会被裁剪掉,比如那些处于背面的点。 同时,剩下的顶点会被插值计算,以形成由像素构成的图元。 所有的信息都会被插值,如纹理坐标,法线,颜色等。
像素处理
像素处理阶段是一个最耗时,但是也是最能够使你的渲染效果品质更高的地方,像素最终的样子会在此决定,你可以进行纹理映射,纹理混合,模糊,扩散等效果。
这也是可编程管线中可以使用SHADER控制的另一个处理过程,
像素的一些额外处理和输出
当像素经过像素处理阶段后,并不能都有机会输出到屏幕上,因为它们还要经过深度(也有一些比较优化的渲染管线将深度测试提到了像素处理前)和模板测试,ALPHA测试,经过这些测试后, 还要进行一次ALPHA混合,这次与目标缓冲区的混合,就能够实现半透明效果。 虚拟世界中的五光十色就是因为这个半透明效果而生动。
-------------------------------------------------------
正如上面所说,在3D渲染流程中,我们能够用着色器语言控制的就是“顶点变换和光照” 以及 “像素处理”阶段。 在我们讲如何控制之前我大概介绍一下GPU中用于处理着色器的最基本的帮手 - 寄存器。
GPU中的寄存器与CPU中的普通寄存器有一点不同, GPU中的每一个寄存器都是一个四维向量寄存器,即一个寄存器拥有4个浮点分量。 通常我们用
(x,y,z,w)或者(r,g,b,a)来表示。
输入寄存器
输入寄存器是GPU用来接受数据的寄存器,当我们将渲染数据填充到GPU时,其实就是将这些数据填充到这些输入寄存器上。
比如,当我们将一个顶点的位置和法线提交后,GPU在处理这个顶点时,其对应的寄存器就会拥有这个顶点相应的值。
顶点处理阶段和像素处理阶段用到的输入寄存器是不同的。输入寄存器决定了对应的处理阶段能够做的事情。
比如,我们提交了一个三角形的顶点和纹理坐标信息,并且我们提交了一张纹理,用来对这个三角形做纹理映射。 但是,我们是不能在顶点处理阶段就对其纹理做处理的。 因为我们不能在顶点处理时访问纹理数据(如果真要这样,那就只能够使用顶点纹理了,这个内容超出了本次介绍的范围,固不再多说)。
常量寄存器
常量寄存器用来向着色器传递我们所需要控制的常量信息,比如,世界矩阵,观察矩阵,投影矩阵,纹理矩阵等。 以及我们可以设置一些值,比如当前时间,用来实时偏移一个顶点的纹理坐标,使其纹理呈移动的效果。 又或者通过这个值,动态改变顶点的位置,使其出现波动效果。 这些就是常量寄存器可以干的事。
同样,常量顶点处理阶段和像素处理阶段使用的常量寄存器也是不同的。不过,这种情况在SM 4.0以后得到改善,并且有一个趋势,就是顶点处理和像素处理阶段界线不再那么明显。 他们可以共用寄存器,共用一些缓存。 但在你没有完全掌握它们的特点以前,还是老老实实记住这个特性吧。
临时寄存器
临时寄存器使我们能够在着色器处理的过程中存放一些临时的值,若你是用高级着色语言编写着色程序,那你是感觉不到临时寄存器存在的,因为你仅仅是声明了一个临时变量。 但确实,这就是临时寄存器的功劳。 它才是真正的幕后黑手。
纹理采样寄存器
纹理寄存器用于存放你所提交的纹理,并且提供纹理采样功能。 如临近点采样,双线性采样,三线性采样等。 这些都肯定你的指示来做相应的工作。 它主要是辅助你完成纹理映射工作。
输出寄存器
输出寄存器就是你着色程序能够输出的内容,输出内容通过输出寄存器传递出来。 顶点处理程序的输出有两种, 一种是输出到帧缓冲,另一种是输出给像素处理程序,最典型的就是纹理坐标数据, 当顶点处理程序拿到输入寄存器传递过来的纹理坐标值后,经过一些处理,又输出。 而真正需要使用这个信息的,就是像素处理程序。
常见的有 位置,纹理坐标,颜色等。
-----------------------------------
下面,我们来看一下着色器语言吧。
着色器程序就如上面讲的那样,分为了顶点着色程序和像素着色程序。 你可能会发现,这里多了一个几何着色程序,这个是后来新加入的兄弟,传统的着色程序不能增加删除顶点。 但是,它可以。 有兴趣的童鞋中以继续去了解。
着色器语言有高级语言和低级语言两种。 低级语言采用的是汇编那种助记符方式。
如 dp4 r0,v0,c0 这样的,表示将v0,c0点乘,并放入临时寄存器r0中。
而高级语言则是C风格的,很符合人们的编程习惯。 与传统的编程语言发展规律是相同的。
如 float temp = dot(dir,normal);
而常见的着色器语言中,低级语言如D3D中的LLSL,以及Adobe新出的Statge3D协同工作的AGAL.
高级语言如 CG,HLSL,GLSL应该很熟了吧。
CG是NVIDIA公司出的语言,它可以在D3D和OPENGL中工作,但需要使用NVIDIA对应的SDK。
HLSL是D3D协同工作的高级着色器语言。
GLSL是OPENGL协同工作的高级着色器语言。
--------------------------------------------------------------------------------------------------------------
说了这么多,我们来看看一个简单的例子吧。 HLSL版
我打上了注释,就不再叙述
//VERTEX SHADER
float4x4 matViewProjection; //世界-观察-投影矩阵
struct VS_INPUT //输入结构,这个结构中的内容,表示我们SHADER所关心的内容,同时,程序在进行数据填充时,应该保证这些关注的数据被提交
{
float4 Position : POSITION0; //位置
float2 Texcoord : TEXCOORD0; //纹理信息
};
struct VS_OUTPUT //输出结构,这个结构中的内容,表示此SHADER的输出
{
float4 Position : POSITION0; //位置信息,已经经过坐标系变换
float2 Texcoord : TEXCOORD0; //纹理信息
};
VS_OUTPUT vs_main( VS_INPUT Input ) //顶点程序的入口函数
{
VS_OUTPUT Output; //声明一个结构体
Output.Position = mul( Input.Position, matViewProjection ); //做矩阵变换
Output.Texcoord = Input.Texcoord; //直接输出纹理信息, 如果你想对它做点手脚,是很容易的。 这就是FFP中纹理矩阵所做的事情。
return( Output );
}
//PIXEL SHADER 它就相对简单多了
sampler2D baseMap; //纹理
struct PS_INPUT //输入结构,与VS中的输入结构类似,但此输入结构均来自于VS的输出。
{
float2 Texcoord : TEXCOORD0; //表示我们只需要用到纹理坐标信息
};
float4 ps_main( PS_INPUT Input ) : COLOR0 //出口函数。 COLOR0表示我们输出的float4是用作颜色输出, 也可以定义类似 PS_OUTPUT的结构
{
return tex2D( baseMap, Input.Texcoord ); //很简单,就是取得对应纹理坐标处的像素值,输出, 你可以在此做一些事情,比如调得更亮,或者拿另一张纹理采样,与它混合。 混合的公式就由你自己定了,你想写得多复杂都可以。 理论是如此。
}
//========== 感兴趣的朋友可以看看上面的SHADER对应的低级版本==========
// VS
// Generated by Microsoft (R) HLSL Shader Compiler 9.22.949.2248
//
// Parameters:
//
// float4x4 matViewProjection;
//
//
// Registers:
//
// Name Reg Size
// ----------------- ----- ----
// matViewProjection c0 4
//
vs_2_0
dcl_position v0
dcl_texcoord v1
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3
mov oT0.xy, v1
// approximately 5 instruction slots used
//PS
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.22.949.2248
//
// Parameters:
//
// sampler2D baseMap;
//
//
// Registers:
//
// Name Reg Size
// ------------ ----- ----
// baseMap s0 1
//
ps_2_0
dcl t0.xy
dcl_2d s0
texld r0, t0, s0
mov oC0, r0
// approximately 2 instruction slots used (1 texture, 1 arithmetic)
说到这里,差不多要结束了。但还是再多说两句。
由于硬件条件的限制 VS和PS中对指令条数和可使用的寄存器个数都有限制,虽然随着硬件的发展,这个限制已经可以被忽略了。比如SM 4.0就已经将这个限制放宽到很大。
但当我们在写着色程序时,除了追求效果外,还要追求效率。因此,节约使用资源将会提升效率, 一个很好我评判标准就是你的SHADER所使用的指令数
如上面低级语言版本中 VS,PS都有如下内容
// approximately 5 instruction slots used
// approximately 2 instruction slots used (1 texture, 1 arithmetic)
因此,它将能够很直观地评估出你SHADER的效率。 但真正的结果,还是要实际测试。 由于硬件的不同,可能还存在兼容性上的问题。
祝各位一路顺风。
上面的SHADER代码取自 RenderDonkey 这个软件 在ATI官网上可以下载。
或者,直接点这里http://developer.amd.com/archive/gpu/rendermonkey/pages/default.aspx
强势三国卡版风页游《五虎上将》----等你来战
《五虎上将》神迹官网:点击进入游戏
看着五虎上线了,说实话,心里还是有一丝窃喜,却又一丝悲伤。
窃喜的是这也算是自己参与过的项目上线了,悲伤的是,这不是自己手把手从头到尾一路走来的项目。
没有那种一路风雨走过来的感觉。
话说,虽然自己不是从五虎从头到尾跟过来的,但我确实从五虎中学到了许多, 特别是学到了 “如果我也这样做,那结果就是现在这样”。
它就像是一面镜子,时刻让自己反醒。
我并不是说五虎代码差成什么样,而是,这就是传说中的“经验”。恰巧,这些东西,被我悄悄地偷取了。
由于是后入五虎项目组的,所以我并不能参与五虎的功能部分开发。当然,这也是我所期望的。 我不想让自己纠结于泥潭中无法抽身,又或者当我抽身后,
把别人陷入了泥潭中。
因此,我只能在资源加载,浏览器缓存,代码规范上给予他们帮助。
在为五虎寻找解决方案的同时,另一个项目也在进行着, 一个教育类养成页游项目。于是,我将所有的方案先应用于养成页游中,经测试和优化完毕后,再投入五虎项目组。
下面是我研究和整理出来的一些方案,这些方案并不完美,甚至说仅仅是一些很常见的手法。于是,我大可公布于下,不必担心任何纠葛。因为这并非是我个人所创的专利,也不是五虎项目所特有的东西,而是我在拥有芸芸众生的网络中摆渡,谷歌而来。同时,希望有志同道合的朋友能够给我提出建议,相互交流,共同进步。
资源加载相关事项:
一、首要的任务,就是要将资源请求打包为一个队列。 这样外部需要加载多个资源时,就可以把队列作为一个整体来进行事件监听等。
二、其次就是资源的分类,资源至少可以分为三类, 一类是Modules.即FLEX中用来模块化的东西,类似于C++中的DLL,(swc即类似于lib)。其次是ApplicationDomain.这多半是FLASH IDE编辑资源后的导出类。 最后就是纯数据了, 如XML或者一些配置文件。 所以,需要将其分类,并能够以一个很方便访问的方式来管理。
三、Loading方式。 Loading方式大概也可以分为两类, 一类是阻塞式,一类是非阻塞式。 阻塞式就是那种需要出现Loading条的方式,使用这种方式时,资源必须要完全加载完成后,才可以使用。如一些配置文件,UI组件的皮肤资源等。 非阻塞式就是不需要等待,而是在资源未加载完成时,统一采用一个显示资源来替换。 比如一些图标,或者RPG地图中一些人物的显示(许多游戏在人物未加载完成时,显示一个球形)。
pureMVC使用注意事项:
一、pureMVC的使用应尽量标准,如果MVC使用得不标准,还不如不用。
二、尽量只在UI与业务逻辑通信时才使用pureMVC,逻辑与逻辑通信应当避免使用,否则会导致逻辑BUG的完全不可控。
模块划分注意事项:
一、游戏是一个巨大的项目,模块划分在所难免。 不论是否拆分为新工程,都必须明确规定各模块代码所能操作的权限,若权限过大,会导致无力维护,人走茶凉的现象。
二、若是采用Flex module进行模块划分, 若各Modules分开建立项目,则需要注意文件夹的层级,以及Modules编译时相对主模块的优化选项要开启。
三、若将所有的Module依然放到一个项目里,则依然要注意文件夹的层级,规范各模块代码的操作权限,通信方式等。
UI方案:
一、Flex Button大小问题 若采用Flex Button并使用CSS Style方案,则要注意 对于 downSkin,若需要改变大小,则要保证其up down over状态使用的皮肤都具有同样的大小。(特别是FLASH IDE中导出元件的话,更要注意,仅仅将缩小后的元件锚点偏移,是会失真的,因为FLEX BUTTON只识别一个元件有效像素的包围框作为元件尺寸。 可以新建一个透明层或者遮罩层来解决这个问题。
二、Button的特殊性 采用CSS使用FlexButton需要将Button各帧拆开。这涉及到美术工作量问题,所以,对于那种以图片展现(即不需要在BUTTON上添加文本)的BUTTON,可以直接使用FLASH IDE导出SimpleButton并直接使用即可。 而对于那种需要动态修改文本的按钮,采用CSS即可。
三、CSS文件的使用。 CSS的使用可以说极大地方便了UI的工作,其实就是将FLEX的UI换肤而已。 但是采用CSS文件方式并不适合我们。因为CSS文件的静态和动态使用方式都会导致文件巨大。
静态使用方式, 即<mx:Style source=”ooxx.css”/> 方式,这种方式会把 ooxx.css中所嵌入的资源都嵌入到嵌入ooxx.css样式的SWF中来。
动态使用方式, 即将css编译为swf 如 ooxx.swf 并用styleManager加载。 这个方案有一个好处,就是可以动态加载和卸载需要的CSS样式。 看似天衣无缝的方案,却有极大的弱点。 当你查看ooxx.swf的大小时,你会发现,就算你什么资源也没嵌入,也会有300KB+的大小。 这是因为css本身是一个module.它会引入一个flex module所要import的符号。可惜,CSS编译时,并不能指定针对某个主模块进行优化。 我也曾尝试将ooxx.css嵌入到一个module A.mxml中。并在编译时针对主模块进行优化,文件减小到了60+KB. 60+KB足够一个公共UI库的大小了。所以,CSS文件一定要慎用。
四、CSSStyleDeclaration与setStyle. 可以通过动态注册CSS样式来达到修改FLEX组件样式的目的。 具体使用方法可以搜索这两个关键字。 但是,在使用时,请先将所需要的资源载入。
五、TabNavigator 视情况而用。
六、可拖动窗口 并不一定要使用TitleWindow 若要使窗口可拖动,只需要将窗口startDrag()即可。 而对于许多窗口来说,我们想要实现的是点击某个位置或者仅当点击标题栏才拖动。 这个时候,我们可以添加一些透明的组件在这个窗口上,监听这些透明的组件,并startDrag()窗口即可。
功能划分:
一、不管是哪种游戏类型,都应该把场景和UI划分清楚,就算是鼠标流的策略游戏,也是如此。 否则在逻辑功能上也会模糊不清。
二、游戏,永远都有场景和UI,没有PAGE。。。 这是页游和网页的本质区别。希望做网页转为做页游的朋友能够接受这个现实,转变一下思路。
----------------------------------分割线-----------------------------------
浏览器缓存避免
浏览器缓存方案也是世人皆知的,我也仅简单说一下。 更多内容,可以参考本BLOG中,浏览器缓存相关文章。
要想完成浏览器缓存避免需要弄清楚两个事情 一、浏览器会缓存以URL全路径方式缓存HTTP所请求的资源(js,html,swf,txt等等都可以)。 二、FLEX中的资源加载,是 HTTP请求方式。 也就是说, FLEX项目中加载的资源,都会被浏览器缓存, 这也是导致许多时候,游戏资源更新失败的原因。
而要完全解决浏览器缓存问题,需要保证两个东西 第一、主文件 (比如whsj.swf)更新正常。 第二、主文件所使用的资源(如 assets/loading.swf)更新正常 .
这里,我们要用到HTTP请求时的传参功能 即在请求的资源URL后面加上?号 如 assets/loading.swf?v=1.0 。
在时行资源请求时,HTTP会忽略掉?号,以及以后的字符。 但浏览器缓存会缓存整个URL路径。 并且在进行缓存访问匹配时,也是采用的全路径。 所以,我们可以在资源请求时,给每个资源加上一个版本号。
网上的一个兄弟说的采用SVN生成每一个资源的版本号方案确实很给力,但我认为实在很麻烦。于是,我们大可在请求每个资源时,加上游戏的发布版本号。 这样,浏览器在进行资源缓存时,会将版本号信息一起缓存,当版本更新时,匹配则不会成功,从而解决了资源更新时,使用了旧资源的问题。
而最主要的,还是主文件的缓存, 因为就算你资源更新了, 但主文件没更新, 那么,游戏功能就不会变。 当你采用旧版游戏功能与新版的服务器端通信时,自然会出现各种各样的DOWN和黑屏。 因此, 主文件也要加版本号 如whsj_1_0.swf 而每次版本发布时,运营官网作相应的更改即可。
此方案也是目前我用在五虎项目上的初步方案。 对于主文件版本号的管理,许多公司的做法是加一个壳, 即做一个专门用于加载主文件的swf 它几乎不会更改, 永远都是采用随机数或者时间戳读取主文件版本号信息 如 version.xml?v=20111113,并使用 whsj.swf?v=1.0这样的方式来加载主文件。 这在版本发布时,可以一层不变。仅修改版本号信息文件即可。 而版本号文件采用时间戳等方式读取,可以保证每次都加载到最新的。
最后,祝贺一下二部项目天地劫即将测试。
天地劫神迹官网:点击进入
在网上搜浏览器缓存问题时,遇上了很多问题。一是不知道应该用何种关键字搜索,二是一搜出来,就全是讲的是如何禁用浏览器缓存的方案。
作为大型点的FLASH WEBGAME来说,不缓存显然是不行的。总体上来说,我们要想达到的目标就是
一、浏览器需要缓存
二、当服务器资源更新时,浏览器缓存里相应的老版本资源失效。
下面两篇文章讲到了一个很好的解决方案,并且给出了源码。。
更新文件避免浏览器缓存的解决方案(基于svn)
http://www.itamt.com/2010/06/browser_cache_prevent_base_on_svn/
更新文件避免浏览器缓存的解决方法(源码)
http://www.itamt.com/2010/07/file-revision-prevent-browser-cache-svn/#comment-163
由于作者BLOG上的代码无法下载,并且作者的实现与第一篇中描述的略有差异,于是我试着留言给作者。热心的作者当天就给了我回复。
以下是我整理后的回复内容:
你好, 关于你说的:
1 、你最后的方案是不是 仅仅在资源请求时,追加一个版本号? 我发现IE浏览器缓存是将整个请求URL缓存下来了的。也就是说,版本号也缓存了。 这样,当我们使用版本号请求资源时,就可以实现总是得到服务器上的资源了。
2、IE和Firefox的缓存机制差异有多大? 主要是缓存后的URL路径上。。。
我把我的想法说一下, 尽量表述清楚-_-:
不管IE和Firefox浏览器缓存都是将整个请求url缓存下来的. 而我的解决方案本质与你说的是一样的, 即"仅仅在资源请求时,追加一个版本号".
一般的做法,发布新版本时,比如v1.5,我们大可以在所有资源请求都加上"....?v=1.5", 这样做没有错.肯定是会避免浏览器缓存的.
问题是:这种方法会重新请求所有的资源. 而项目发布的通常情况是:只有少部分资源有修改. 所以这种方法是很浪费带宽的, 也加重了服务器的压力. 理想的情况就是:只重新请求那些被修改过的资源.
下面表述一下我的解决方案:
假设现在项目是v1.0, 我们要发布v1.5上去. 我们利用SVN产生一份文件, 来记录v1.5中所有资源的SVN版本号. 当请求一个资源时, 追加这个资源的SVN版本号. 如果一个资源(比如: icon.jpg, SVN版本号是1010)在v1.5中没有被修改, 那么它的SVN版本号与在v1.0的SVN版本号肯定是一样的都是1010:
在v1.0中,浏览器请求icon.jpg的url是:http://.....icon.jpg?svn=1010
在v1.5中,浏览器请求icon.jpg的url是:http://.....icon.jpg?svn=1010, 这时候会直接从浏览器缓存中取出. 不会重新请求.
如果v1.5中一个资源(比如: logo.jpg, SVN版本号是1234)是修改过的, 它在v1.0的SVN版本号是1222, 请求资源时:
在v1.0中,浏览器请求icon.jpg的url是:http://.....logo.jpg?svn=1222
在v1.5中,浏览器请求icon.jpg的url是:http://.....logo.jpg?svn=1234, 这时候与浏览器缓存中的请求是不一样的. 所以会重新向服务端请求.
思路就是这样的. 所以关键是利用SVN来记录的版本号请求资源.
这个解决方案最终实现出来主要是两个文件.
1.cur.txt记录整个项目当前的SVN版本号.
2.revision.txt记录项目当前版本中所有资源的SVN版本号.
整个项目程序一开始(Preloader.swf), Preloader.swf会先确定整个项目SVN版本号(加载最新cur.txt, 或者在网页中指定:...Preloader.swf?cur=20), 然后根据整个项目SVN版本号加载revision.txt, 比如现在项目SVN版本是20, 那么Preloader.swf会加载:
http://.....revision.txt?svn=20
加载完成后, 就可以完成mFileRevisionManager的初始化...
注意Preloader.swf不是进度条(load.swf), 它是整个项目最开始的地方, 所以应该尽可能小, 而且一旦完成最不要再去修改它.
附件里,有我做的demo. 我的开发环境是FDT4.5+Flash CS5.5, 文件夹tool里一个ant脚本, 和jsfl脚本. 使用时你需要修改publish.xml开头的路径. 然后运行这个ant脚本.
运行ant前, 注意设置下:这样ant运行期间的输出信息会显示在eclipse里.
---------------------------------------------------------------
并且,我问了一下他对Preloader.swf的理解和用法
回复:
结合我参与的项目,说一下Preloader.swf是干么用的:
一般项目肯定会有一个load.swf和main.swf, 其中
1.load.swf用于显示加载main.swf的进度,说白了load.swf就是个进度条.
2.main.swf就是整个项目的主程序.
而Preloader.swf是在load.swf之前加载的.也就是说加载顺序是Preloader.swf->load.swf->main.swf
Preloader.swf主要用来加载"资源版本号文件"(revision.txt, cur.txt). 并且把整个资源版本机制初始化(在我的方案里就是mFileRevisionManager).
当然, 也完全可以把Preloader.swf并入load.swf(进度条)中. 但我的经验是load.swf(进度条)其实是经常需要修改更新的. 而且为了重用性考虑, 我会把Preloader.swf用于不同的项目中. 所以把Preloader.swf与load.swf分开.
-----------------------------分割线--------------------------------
而考虑到目前项目的原因,我们决定暂时采用一个很简单的方案。 就是在资源的根目录下加版本号。
比如 1.0.0版本的资源目录为
Assets_v1.0.0
我要加载一个ui.swf的URL就是 “Assets_v1.0.0/ui.swf”
而当版本更新时,比如到了1.0.1
我要加载一个ui.swf的URL就是 “Assets_v1.0.1/ui.swf”
这样就像回复内容中说的,这会使浏览器缓存中所缓存的所有资源都失效。 虽然是如此,但总比用户继续使用老版本资源好。 并且,对于一个发布版的更新总会有时间间隔的,不会一天到晚都是这样的情况。
而待项目趋于稳定后,再集中替换成上面那种最小资源更新的解决方案。
希望上面的信息能够帮助到和我一样迷惑的人。如果这些信息帮助到了你,请去参拜本文开头引用的两篇文章的作者,谢谢!!!!!!