使用xlua 进行Unity3D 热更新

之前一直在纠结项目的某些运营界面使用全Lua来写,这样可以热更新上线不同的活动,而不需要上架不同的版本,虽然可以通过资源更新加载xml的方式来实现,但是要通过xml来组合一些新的活动逻辑会很困难.

然后听了很多朋友最近都在推荐xlua, 不但可以做纯lua的逻辑更新,还可以做 C# 代码的bug hotfix.
就是可以在保持项目使用C#逻辑开发的前提下,出现bug后使用lua来修复.听起来很棒棒.

github地址 : xlua

试用一下之后,发现xlua的hotfix原理也很简单, 就是通过反射取出打上了 [hotfix] 标记的类,
然后对需要fix的函数执行下列伪代码

void Start()
{
  if(_hotfix)
   _lua_add(a,b);
  else
   _csharp_add(a,b);
}

那么思路就来了.
设定一个全局 hotfix管理类, 在游戏初始化的时候拉取服务器的 hotfix 版本号, 看看服务器上是否有新的补丁需要下载, 如果有,将补丁存到本地. 之后加载 补丁, 打补丁. 最后进入游戏.

正好以前自己写过好几个lua项目比如 : PrompaLua, lua还没忘记..正好用上 哈哈

首先是一个范例:
一个有bug的类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;

[Hotfix]
public class FuckYouSelf : MonoBehaviour {

    private int A;
    private int B;

    void Init() {

    }
	// Use this for initialization
	void Start () {
        Init();
        UnityEngine.Debug.Log(Add(A, B));
    }

    int Add(int a, int b) {
        return a - b;
    }
	
	// Update is called once per frame
	void Update () {
		
	}
}

错误有 A,B 没有初始化
Add函数被写成了减法

好在类的开头被打上了 [Hotfix] 标记,让我们有机会热修复他.

然后是HotFix类

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;


public class HotFix : MonoBehaviour {

    public GameObject StartUp;

    [Serializable]
    struct VersionData {
        public string Version;
        public string FixUrl;
    }

    LuaEnv luaevn = new LuaEnv();
    // Use this for initialization
    void Awake () {

        StartCoroutine(LoadVersion("http://www.dreamfairy.cn/xlua/test/version.txt"));
    }

    IEnumerator LoadVersion(string versionUrl) {
        WWW versionData = new WWW(versionUrl);

        yield return versionData;

        if(null != versionData.error) {
            Debug.LogError(versionData.error);
        } else {
            VersionData data = JsonUtility.FromJson<VersionData>(versionData.text);
            StartCoroutine(LoadFix(data.FixUrl, data.Version));
        }
    }
	
	IEnumerator LoadFix(string fixUrl, string version) {

        //todo: check storage hotfix version

        WWW fixData = new WWW(fixUrl);

        yield return fixData;

        if (null != fixData.error) {
            Debug.LogError(fixData.error);
        } else {
            ApplyHotFix(fixData.text);
            SaveToStorage(fixData.text, version);
        }
    }

    void ApplyHotFix(string luastr) {
        luaevn.DoString(luastr);

        if(null != StartUp) {
            StartUp.SetActive(true);
        }
    }

    void SaveToStorage(string luastr, string version) {
        //todo
    }

    private void OnDestroy() {
        
    }
}

他会在游戏启动的时候去下载 http://www.dreamfairy.cn/xlua/test/version.txt 版本号数据
之后去补丁地址下载补丁文件 http://www.dreamfairy.cn/xlua/test/hotfix.lua

下载完毕后,缓存到本地, 然后调用游戏启动 StartUp

启动游戏后, 原本控制台输出 0 的结构, 热更新后 控制台输出 600

最后是lua部分的代码

xlua.private_accessible(CS.FuckYouSelf)

xlua.hotfix(CS.FuckYouSelf, 'Init',
function(self)
    self.A = 300
    self.B = 300
end
)

xlua.hotfix(CS.FuckYouSelf, 'Add', 
function(self, a, b)
    return a + b
end
)

lua部分的代码,就3段 第一段 xlua.private_accessible(CS.FuckYouSelf)
让lua可以访问私有字段, 目的是为了改成员变量 A,B 他们是私有的

第二段是修改 类中的 Init函数, 来初始化A,B

第三段是修改 类中的 Add函数,让他正确执行加法

榨干GPU 使用顶点颜色信息来创建千千万的高性能植被

gdc vertex color

之前做Terrain Brush Grass 的时候一直被它的性能折磨,稍微密集一些的植被,帧数直接变为个位数。 而GDC上使用顶点动画的想法瞬间给了我许多启发。

说干,就干。
在 Unity 中拖出一个 Cube 拉伸出 草的模样,然后给顶点上色。 像这样
红色表示可以被晃动的顶点
绿色表示晃动的幅度

两个颜色叠加在一起的时候,就变成屎黄了。。。
不过根部没有颜色呈现黑色可以看出过度

vertex color1

最后在Shader如下

v2f vert (appdata v)
            {
                v2f o;
                float scale = cos(_Time.y * _Speed) * v.color.r * (v.color.g);

                float szControl = randValue > 0.5;
                v.vertex.z += scale * szControl;
                v.vertex.x += scale * (1.0 - szControl);

                float4 worldPos = mul(_Object2World, v.vertex);
               
                o.normal = normalize(mul(_Object2World, v.normal));
                o.worldPos = worldPos;
                o.pos = mul(UNITY_MATRIX_VP, worldPos);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
               
                return o;
            }
           
            fixed4 frag (v2f i) : SV_Target
            {
                float3 lightDir = normalize(WorldSpaceLightDir(i.worldPos));
                float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
                float3 halfDir = normalize(lightDir + viewDir);
                float3 diffuse = tex2D(_MainTex, i.uv) * max(0, dot(i.normal, lightDir));
                float3 specular = _LightColor0.rgb * pow(max(0, dot(i.normal, halfDir)), _Shiness);
                float4 finalCol = float4(diffuse + specular + UNITY_LIGHTMODEL_AMBIENT, 1.0);
   
                return finalCol;
            }

最后我在场景中放了 2.0w 根草, 随机摆动 draw call 1, 25帧
使用Terrain 的 Grass Brush 2000个草,就已经在 2.5帧了
grass

Unity3D 中的贴花效果

这周研究一下,以前从 Stage3D 时代就想做的效果 -> 贴花

具体是怎样的呢

decay3

就是可以贴合模型全新纹理,差不多是这样
于是我做了个 动态贴花插件,这样美术童鞋在做场景的时候就比较直观咯

decayCircle

具体应用的美术场景,可以看看这个图
decay1

decay2

贴花做法其实就是复制要贴花模型的某一部分三角形,然后绘制新的纹理
decay4

如何截取三角形呢

继续阅读“Unity3D 中的贴花效果”

半色调抖动与近景透明


Dithering4

话说,上个月神秘海域4 出了开发者日志, 其中的半色调近景透明技术让我非常感兴趣, 毕竟非常多游戏要用到这个功能, 比如很多RPG游戏,近景的墙壁如果遮挡住主角,就会半透明。 而且半透明又是很消耗性能的,这篇日志也提到使用半色调技术是因为性能考虑。

开发者日志地址 : http://www.paopaoche.net/tv/99983.html

之前一直没空,在研究了2天半色调技术后,终于渲染出了我的半色调皮卡丘!!
Dithering

之后,上周看 火猫tv 的 dota2 马尼拉比赛直播的时候,发现 dota2的新地图也用到了类似的技术, 如图中的柱子 😀
我想估计这个技术应该会慢慢铺开,不过玩家能不能马上接受这种像素风的半透明效果就另说咯。

Dithering2Dithering3

— 未完待续, 周末有空的话, 会补充 贝尔矩阵的求解过程,可以自己计算任意 偶次方长度的矩阵。

Unity3d ddx ddy 法线

最近摸到一个很神奇的API -> DDX, DDY (DX) 或者 FDX, FDY (Opengl)

ddxddy normal

三角面片这么明显的法线有木有

在Nvidia 文档说, ddx/ddy 是求一个近似偏导函数, 以对齐屏幕分辨率的值进行取值

看了好几遍,不懂,然后仔细一想一阶求导不就是斜率嘛, 那么斜率是什么呢,就是平面向量啊。
于是测试了 ddx(worldSpaceVertex.xyz) ddy(worldSpaceVertex.xyz) 可以求出 ddx[(x,x) – (x+1,x)] , ddy[ (y,y) – (y-y)] 即隔壁像素所在三角形和当前像素的斜率差,或者说当前像素到隔壁像素的斜率

x轴 斜率表示前进方向向量, 和 y 轴斜率表示右手方向向量,就可以求 三角面的法线
normalize(ddx) cross normalize(ddy) = normal;
normal = normalize(normal);

这个时候,如果直接输出fixed4(normal,1.0) 就是这样
ddxddy normal fragment

继续阅读“Unity3d ddx ddy 法线”

Unity3d Compute Shader

=。= 准备开始花1个小时探讨这个复杂的 Compute Shader的, 结果从家里拷贝做的 Demo 在女朋友的小霸王笔记本上运行不起来。。只能明天回家在写了。。

就是这个破显卡 🙁 Intel HD Graphics 3000

—–

I’m back…

话说, Compute Shader 能做什么呢,可以把大量的并行计算丢到GPU去计算,因为GPU并行计算的能力爆表
举例来说呢
1.大规模粒子特效,计算粒子的位置,大小,颜色等等(当然传统粒子也可以做到这点,通过传入时间值和带有时间的公式来计算位置,但是Compute Shader 可以实现带有逻辑更复杂的效果, 而且编码更加简单)
2.烟雾,液体的模拟
3.可以做贪食蛇 =.= 像这样
ComputeSnake

继续阅读“Unity3d Compute Shader”

Unity3D 中的高度雾

fog
图中有2个效果, 分别是 HDR(做法在这里uniy3d-hdr_bloom/, 高度5的白色高度雾
不过除了雾以外,其他都不重要.

unity3d 一直都有一个距离雾,也可以说是深度雾,随着物体的远近慢慢渐变颜色,设置在 Window->Lightning->Fog(Unity 5.x)

这次来实现一个u3d中没有的,从低到高,而不是从远到近的雾效 – 高度雾。

之前做这个效果之前,有查阅些资料,有看到某外国的大大做了个类似的效果,地址在这里 altitude-fog
我们将要做的效果和这个差不多,但是区别在于,老外的做法是需要场景中的每个物体都添加一个雾效材质,这个雾效Shader是判断物件顶点Y轴在世界坐标系中的位置,然后 (FogEnd – 物体Y) / (FogEnd – FogStart) 算出一个在Y轴方向的雾效颜色比例,最后差值一下。

继续阅读“Unity3D 中的高度雾”

Unity3D Geomerty Shader

Geomerty Shader 是 SM4.0 Directx 9.0c 中引用的新技术, 它的位置位于 Vertex Shader 和 Fragment Shader 之间, 可以对一组(多个)顶点进行编程

Geometry Shader 可以自定义输出方式
PointStream
LineStream
TriangleStream
分别是 点,线,面
这样如果想做线框模式渲染,就非常的简单

上个线框模式图
LineStream

继续阅读“Unity3D Geomerty Shader”

Unity3d 中的 HDR_BLOOM

虽然u3d标准资源包自带这货,但是我就喜欢重复造轮子,你奈我何

unity3d_hdr_bloom

简单来说 hdr_bloom 就3个Shader
第一个是shader, 筛选出像素亮度
第二个shader, 对像素进行模糊
第三个shader, 合并原始纹理 + 模糊亮度的纹理

第一个Shader
筛选亮度的算法

float brightness = col.r * 0.299 + col.g * 0.587 + col.b * 0.114;

之后我们给 Shader 一个 uniform 参数,作为亮度的调节参数即可

之后,为了让亮度周围出现光晕,我们将亮度纹理绘制在一张特别小的纹理上,然后再将这张小纹理进行拉伸,使其出现像素马赛克效果,这样处理后,迈赛克的亮度部分就会比原始纹理的明亮部分范围更大了,初步的光晕范围就出来了。
但是这部分光晕因为是马赛克,于是要将其进行模糊处理

绘制到缩小纹理再拉伸的效果是这样的

unity3d_hdr_bloom_2

继续阅读“Unity3d 中的 HDR_BLOOM”

Unity3D 中的 X-Ray 透视效果

unity_xray

感觉是第三次写Xray了,最早是 stage3d, 然后是 havok, 现在是u3d… 真是闲的蛋疼。。。
不过u3d 写shader 真是比 havok 方便多了, 比如混合模式,深度检测方式都可以直接和 shader一起写,而havok 中还要去shader外部配置。。

直接上完整shader好了,渲染2次,1次深度测试以小于缓冲区的深度通过,绘制透视部分, 第二次正常渲染

继续阅读“Unity3D 中的 X-Ray 透视效果”