Giter Club home page Giter Club logo

blog's People

Contributors

liuxq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

c++小记录

<<和 >> 操作符不能读写二进制文件
对于二进制存储string,在文本里保存的也是asc11,跟直接存文本差不错
生成dll时对外接口的参数中不要有string(vector等猜测也不行)否则会有删除临时string时堆异常,至少我做的那个例子是这样,不是道是否恒成立。
类导出dll类导出dll类导出dll,最好让导出.h和导入.h共用,否则容易不一致,如果不一致,将会在大部分情况是没有错误的,而在小部分情况(如使用release时)程序崩溃,导致极难找到错误所在。另外程序不要只在debug下跑,很多错误(例如内存泄露)只在release时才会出现


char temp[10][10];
char destTemp[10][10];
memcpy(destTemp,temp,100);
复制的单位是所有元素数(而不是想自己考虑的那样以10个为单位);


10是换行
char a[20] = {'a','b','c',10,'a','b'};
cout<< a<<endl;
显示结果是:
abc
ab
ifstream aa;aa.get();可以得到字符,包括换行符


while (inFileReader.peek() == ' ' || inFileReader.peek() == '\t')
{
inFileReader.ignore();
}
用于去掉空格


dfs可以通过bool数组保存状态,对处理过得状态作为临界条件
可以使用筛法计算n内的所有素数,时空复杂度都为o(n)


std::string 和string不一样,比如:一个支持“==”,一个不支持


注意注意在c++中如果不导入math.h,则abs(double)会使double截尾。usaco中不支持abs(double)只支持int,导入math.h也不行,


ofstream 写文件时到最后close或flush或超过了缓存才会真的写到文件中,或者调用了endl


struct char4
{
unsigned char a:4;
unsigned char b:4;
}
可以这样来节省空间
vector::resize(n,v) .在0-min(n,size)之间的值是不变的,后面的值赋为v


dll还需要lib作为引导库,其中记录了函数头信息,但是在发布之后lib信息都被链接到了exe中,就不需要在发布文件件中再放入lib文件了


vtk小tip
actor 如果new了并且render->addActor了,就要render->removeactor,但是只new一次actor,后面的render->addActor就不需要render->removeactor了
sgrid->GetPointData()->GetScalars()->GetRange()才能获得当前scalar范围
而sgrid->GetPointData()->GetArray(int)->GetRange()获取的范围不对
vtkdataArray 要调用getRange之后才会计算范围


glsl
uniform sampler2D tex;定义的变量如果没有在代码中引用,则不会成功定义,不会放到表中
opengl中的quad点的顺序是需要顺时针的
glColor3ub要记得加u不然很悲剧


需要设置项目依赖项,否则其他项目中lib是不可见的
动态链接库版本:
/MD Multithreaded DLL 使用导入库MSVCRT.LIB
/MDd Debug Multithreaded DLL 使用导入库MSVCRTD.LIB
静态库版本:
/ML Single-Threaded 使用静态库LIBC.LIB
/MLd Debug Single-Threaded 使用静态库LIBCD.LIB
/MT Multithreaded 使用静态库LIBCMT.LIB
/MTd Debug Multithreaded 使用静态库LIBCMTD.LIB
使用一个开源库,先看生成库时使用的运行时库,在调用时必须一致


openGL在使用拾取时,注意鼠标的y值使用viewport[3]来减,此时viewport中的高和宽不可以归一,因为归一后高可能不再是屏幕的高,此时有两种应对变形的解决方案:1.使用glOrtho对应当前窗口的宽和高,2.viewport继续归一,只是不再使用viewport[3]来减,而是使用当前窗口的高来减
gldisableClientState(GL_COLOR_ARRAY);再glEnableClientState(GL_COLOR_ARRAY);时必须重新指定颜色数组,其他数组也一样
glewInit();必须先调用才能使用glew库里的东西
在写入帧缓存时不需要glEnable(texture_2D);想想也不需要,前者是要把纹理帖到表面上
半透明要记得先绘制不透明的,再绘制半透明的
帧缓冲对象需要给它指定深度纹理,才能向其中写入带深度测试的结果,否则深度测试没法进行
纹理一旦取出来了(bind)就不可以再往里写,除非重启帧缓存

GL_TEXTURE_RECTANGLE需要注意,sample时,坐标并不归一,而GL_TEXTURE_2D的坐标是归一的
openGL的透视投影是把坐标按照ortho的参数使之在三个方向都规约到-1-1之间,而在范围之外的点被裁减掉,之后再由viewport参数进行光栅化。
向量叉乘的左右手定则按照坐标系而变换,是左手坐标系就用左手,否则就用右手
旋转矩阵的推导,先把p分解为x和y方向的两个向量的和,之后旋转x和y向量,再做加法


vector多线程时,如果一个线程在写,另一个同时读的条件是集合的元素是小类型如(double)而如果存着vector等大数据,则不可以同时读写
集合中pushback元素时,集合中该元素的地址跟原来元素的地址是不同的

QSplitter当delete时,里面放置的widget也一起delete

定义结构体的时候,成员最好能从大到小来定义,那样能相对的省空间

CMyString& CMyString::operator =(const CMyString &str) {
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return *this;
}这样是可以的,虽然m_pData是私有的,但是可以访问,因为跟自己的同一个类
顺序表删除多个元素时,可以通过类似partition的**,遍历一次删除完全
signal里面的函数参数只能写类型,不能写变量名字

Vc6.0中的fstream必须要加ios::out才能创建文件

UnityEditorWindow停靠实现

  • 背景:很多自定义编辑器可能带有好多的窗口,比如一个自定义的模型编辑器,可能有模型资源选择窗口、动作编辑窗口、动作播放设置窗口等。如果这些互相有联系的窗口都各自分散的显示。会给相关的策划和美术带来很多困扰。于是最近有一个实现编辑器EditorWindow窗口布局的需求,具体要求是把一些自定义的编辑器窗口按照策划喜欢的方式停靠到一个窗口中去(如下图)。

dock

  • 实现方案一:编辑器的停靠效果并没有现成的api可用,反编译unityEditor.dll找到了用反射实现的方法,主要思路就是模拟鼠标拖拽一个窗口停靠到另一个窗口的过程。代码如下:
public static void DockEditorWindow(EditorWindow parent, EditorWindow child)
{
    Vector2 screenPoint = parent.position.min + new Vector2(parent.position.width * .9f, 100f);

    Assembly assembly = typeof(UnityEditor.EditorWindow).Assembly;
    Type ew = assembly.GetType("UnityEditor.EditorWindow");
    Type da = assembly.GetType("UnityEditor.DockArea");
    Type sv = assembly.GetType("UnityEditor.SplitView");

    var tp = ew.GetField("m_Parent", BindingFlags.NonPublic | BindingFlags.Instance);
    var opArea = tp.GetValue(parent);
    var ocArea = tp.GetValue(child);
    var tview = da.GetProperty("parent", BindingFlags.Public | BindingFlags.Instance);
    var oview = tview.GetValue(opArea, null);
    var tDragOver = sv.GetMethod("DragOver", BindingFlags.Public | BindingFlags.Instance);
    var oDropInfo = tDragOver.Invoke(oview, new object[] { child, screenPoint });
    var tDockArea_ = da.GetField("s_OriginalDragSource", BindingFlags.NonPublic | BindingFlags.Static);
    tDockArea_.SetValue(null, ocArea);
    var tPerformDrop = sv.GetMethod("PerformDrop", BindingFlags.Public | BindingFlags.Instance);
    tPerformDrop.Invoke(oview, new object[] { child, oDropInfo, null });

}

效果如下:
dock

由于使用的拖拽模拟,有一些限制,比如窗口比例如果想1:1的话,需要先把第一个窗口的大小调到最小(我用的100,100),然后再停靠一个窗口时,两个窗口就会一样大,停靠之后再把窗口放大到想要的尺寸可以保持1:1的比例。

  • 实现方案二:方案一是使用纯代码的方式实现一个窗口停靠到另一个窗口。有很多缺陷,比如停靠的样式完全靠代码,导致如果策划想要窗口是另一个停靠样式,就需要改代码;而且一些复杂的停靠样式并不能实现,比如本文第一张图的效果。其实unity已经为我们提供了一个靠谱的自己设计停靠样式并保存的方式,如下图
    dock
    可以先把各个窗口拖动停靠到想要的位置,然后savelayout,下一次直接点击保存的布局就可以啦,那么保存的布局存放在哪里了呢,放在C:\Users\Administrator\AppData\Roaming\Unity\Editor-5.x\Preferences\Layouts这里啦,是一个.wlt文件,存在这里是有问题的,布局文件没有存储在unity工程下面就不会被项目组的所有人共享,而懒惰的策划是希望一次update就可以使用别人调好的布局的。所以要实现一个保存布局文件在自定义位置的功能,这个功能也是需要用反射的,unity大大并没有把现成的函数暴露出来。。。,下面是实现保存和加载自定义布局的方法:
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Reflection;

using Type = System.Type;

public static class LayoutUtility
{
    private static MethodInfo _miLoadWindowLayout;
    private static MethodInfo _miSaveWindowLayout;
    private static MethodInfo _miReloadWindowLayoutMenu;

    private static bool _available;

    static LayoutUtility()
    {
        Type tyWindowLayout = Type.GetType("UnityEditor.WindowLayout,UnityEditor");
        Type tyEditorUtility = Type.GetType("UnityEditor.EditorUtility,UnityEditor");
        Type tyInternalEditorUtility = Type.GetType("UnityEditorInternal.InternalEditorUtility,UnityEditor");

        if (tyWindowLayout != null && tyEditorUtility != null && tyInternalEditorUtility != null)
        {
            _miLoadWindowLayout = tyWindowLayout.GetMethod("LoadWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(bool) }, null);
            _miSaveWindowLayout = tyWindowLayout.GetMethod("SaveWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null);
            _miReloadWindowLayoutMenu = tyInternalEditorUtility.GetMethod("ReloadWindowLayoutMenu", BindingFlags.Public | BindingFlags.Static);

            if (_miLoadWindowLayout == null || _miSaveWindowLayout == null || _miReloadWindowLayoutMenu == null)
                return;

            _available = true;
        }
    }

    public static bool IsAvailable
    {
        get { return _available; }
    }

    public static void SaveLayoutToAsset(string assetPath)
    {
        SaveLayout(Path.Combine(Directory.GetCurrentDirectory(), assetPath));
    }

    public static void LoadLayoutFromAsset(string assetPath)
    {
        if (_miLoadWindowLayout != null)
        {
            string path = Path.Combine(Directory.GetCurrentDirectory(), assetPath);
            _miLoadWindowLayout.Invoke(null, new object[] { path , false });
        }
    }

    public static void SaveLayout(string path)
    {
        if (_miSaveWindowLayout != null)
            _miSaveWindowLayout.Invoke(null, new object[] { path });
    }

}

我们可以拖出来一个设计好的布局然后使用SaveLayoutToAsset保存布局到自定义路径,然后把布局文件上传到svn,策划update之后就可以直接通过菜单调用LoadLayoutFromAsset来加载布局啦

Unity模型导入的check

  • 介绍:
    资源管理是Unity开发者经常遇到的问题,比如在导入模型(fbx)资源时,对于一些不需要材质的纯动作模型,如果美术没有取消勾选Import Materials选项(如下图),就会增加包中的无用材质和贴图,造成空间浪费。
    dock
    如果单纯靠人工每个模型检查设置,无疑既增加了成本,也为疏漏创造了条件。因此本文提供一种自动核查的方式处理模型资源导入的管理。
  • 过程:
    要实现资源导入核查需要获取Unity导入资源时的回调入口,这里需要介绍一下AssetPostprocessor 类。该类是Unity大部分资源(模型、图片、声音)导入时调用的处理类。有导入之前、导入之后等时间节点的处理函数。具体介绍可以查看api(https://docs.unity3d.com/ScriptReference/AssetPostprocessor.html)。
  • 今天要介绍的是导入fbx模型时的核查,比如帮助美术检查导入fbx文件,如果是动作模型,就不导入材质。用到的AssetPostprocessor方法是OnPreprocessModel,该方法在导入fbx之前调用,可以用于设置各种导入参数。代码如下:
    private string path = "Assets/Resources/Model";
    private void OnPreprocessModel()
    {
        ModelImporter modelim = assetImporter as ModelImporter;
        if (modelim == null)
            return;
        //model目录下带动作的fbx不要导入材质
        if (assetPath.Contains(path))
            {
            if (modelim.importAnimation && modelim.clipAnimations.Length > 0 && modelim.importMaterials)
            {
                 modelim.importMaterials = false;
            }
        }
    }
  • 另外有的fbx模型的材质是有问题的,此时可以在导入时给美术提示,此时可以用到OnAssignMaterialModel方法,该方法在导入模型之后设置材质时候调用,可以查看导入的材质信息。代码如下:
private void OnAssignMaterialModel(Material material, Renderer renderer)
    {
        //提示材质有问题的fbx
        if (material.mainTexture == null)
        {
            ModelImporter modelim = assetImporter as ModelImporter;
            if (modelim != null && modelim.importMaterials == true)
            {
                EditorUtility.DisplayDialog("fbx材质问题", "模型:" + assetPath + "的材质mainTexture是空!请检查模型", "ok");
            }
        }
    }
  • 如果模型材质文理是空就会提示:
    dock

导入其他资源也同理。

基于Unity+KBEngine开发的mmo游戏demo的第二版(Lua热更新)

lua代码结构说明:

ui-demo

ip、port等配置位置:

ui-demo

游戏截图:

ui-demo

身份证被冒用

今年初买房凑首付,办理信用贷的时候自己查了一下征信记录,发现一笔已经三个月逾期未还的贷款记录,放贷方是平安易贷。
打电话问发现是有人冒用的身份证在三月份办理了贷款。过程是那个人先用我的身份证在上海的农业银行办理了一张银行卡,然后在平安易贷的网络APP上面申请了贷款并放贷。至今已经四个月的逾期。
去了两次派出所,打了无数次电话,金融服务办,银监会,市民热线,法务热线,拖了一个月还没有办成。最终妥协,帮骗子还了钱了事。以后一定要看好自己的身份证。

开源自己最近做的一个mmo游戏

这阵子使用kbengine+unity做了一个游戏。一款mmorpg的移动端网络游戏,采用大世界即时战斗方式。 这个游戏有两个职业,每个职业有三个技能。战士的血量和防御比较强,有近程打击和吸血的技能;法师血少攻击距离远,具有远程攻击和治疗的技能。 左下角是控制手柄,视角会随着人物方向变换,点击视角可以瞬间完成变换,右下角是技能按钮和切换对象按钮用来选择对象并攻击或援助。 点选怪物或者玩家左上角会出现血条和名字,表示已选中,此时点击技能可以对该目标释放,如果目标距离大于技能范围则会自动移动到技能范围内,点击左上角自己的血条可以取消对其他目标的选中,此时释放治疗技能可以治疗自己。

v1.0版,目前有物品系统,装备系统,战斗系统,聊天系统完成

使用kbengine-0.7.0,客户端使用unity5.2
客户端代码地址:https://github.com/liuxq/TestGame/releases
服务端代码地址:https://github.com/liuxq/MyGameServerAssets/releases

ui-demo
ui-demo2

凸包半平面碰撞遇到的问题和解决

  • 最近在公司修改碰撞的编辑器并查一个碰撞的bug,在此总结一下
  • 我们的碰撞采用记录凸包的方式,给美术做一个凸包编辑器(使用unityEditor),提供创建碰撞圆柱,盒子,以及mesh的方式,之后使用giftwrap算法生成凸包,最后将凸包保存成半平面形式(即面法矢和到原点有向距离),这种保存方式使每个凸包上的面只需要用两个变量来表示,比较节省空间,并且判断碰撞时只需要将点到原点的向量点乘面法矢得到的距离和面距离做比较就可以得到是否碰撞。导出的碰撞数据供服务器和客户端共同使用。在使用之前还需要进行aabb碰撞盒的限定并用八叉树来分割空间,从而减少碰撞判断的复杂度。
  • 这里说一下遇到的一个碰撞的bug:主要表现是人物陷入一个阶梯场景中了,尝试把aabb的预碰撞注释掉就没问题了。查找原因,是因为生成的aabb盒子没有包住凸包。这个错误发生在由凸包mesh导出半平面的过程中。aabb是用凸包mesh的顶点集合计算的,而凸包mesh导出成半平面之后由于float误差导致半平面生成的凸包超出了原来的aabb范围。解决:按照aabb增加六张半平面,这样就强制缩小凸包的范围到aabb了。如果原来的凸包有垂直于轴的面就不再补该轴的半平面。

UI上显示模型时残留轮廓问题

在UI上显示模型大致两种方法,一是直接把模型放在UI上渲染,二是另启一个相机(或者commandbuffer)将模型渲染到rendertexture上,然后把rendertexture在UI上当做图片来渲染。

第一种方式由于不利于UI层级管理,大部分项目用的第二种方式。
大体方案是指定一个分辨率的rendertexture,赋给渲染模型的camera,相机clearcolor的a设置成0,这样模型存在的像素上a是大于0的。后续混合到UI背景上时使用One OneMinusSrcalpha的blend就能保证保留实体模型,丢弃原背景。

dock

如上图所示,大体上达到要求,但是细心观察能看到模型的周围有一个轮廓边。轮廓边的产生是由于纹理采样的误差导致的。

dock

在填充一个像素的时候,通过uv坐标去采样纹理,但是uv坐标不一定正好指定在一个像素上,还可能(很可能)指定在四个像素之间,这时候硬件会根据周围四个像素插值出这个坐标的颜色。如下图所示,图中的0和1表示纹理的alpha值,我们把它想象成rendertexture中模型实体和背景的边界,红点表示当前要渲染的像素,此时红点的值应该是0到1之间,blend之后是会渲染出背景颜色的,这个漏出的背景颜色就是我们看到的轮廓边。(比如在渲染模型时用的ClearColor是红色,则轮廓边就是红色的)

想要去掉轮廓边,就需要uv坐标正好取在纹理的像素之上,而不是像素之间。要达到这个要求,需要满足两点:1.渲染像素和输入的纹理像素是一一对应的,也就是渲染的quad的分辨率和输入的rendertexture的分辨率必须一致。2.渲染的每个像素要和输入的纹理像素一一对齐,由于分辨率是一致的,所以只要起始像素对齐,也就都对齐了。

分辨率一致的要求比较简单,需要换算一下渲染的UI上的quad的实际分辨率是多少,然后根据这个分辨率去设置rendertexture的分辨率。
像素对齐的要求可以用一个取巧的方法,把纹理采样的方式从Bilinear改为Point,也就是即使没有对齐,但是会找最近的像素,而不是去相邻四个像素去插值。

Unity5的ShaderVariants实验和总结

ShaderVariants(下文用shader变种描述)是unity中用来合并组织shader的一个方式之一,在shader中的使用类似宏定义。最近项目使用shader变种的时候发现了一些坑,所以做了如下实验和总结。其中前两节是基础部分,看官方文档也可以了解,只是通过实验来加强理解。第三节shader变种的打包是重点描述的,比较重要。

一、生成shader变种机制:

为了做实验,制作shader如下:

Shader "Custom/Color"
{
        SubShader 
        {
            Pass 
            {
            Cull Off
            ZWrite Off
                        Lighting Off
 
                        CGPROGRAM
                        #pragma vertex vert
                        #pragma fragment frag
                        #include "UnityCG.cginc"
 
                        #pragma shader_feature RED GREEN BLUE
                        //#pragma shader_feature GREEN
                        //#pragma shader_feature BLUE
                        //#pragma multi_compile RED GREEN BLUE
                        //#pragma multi_compile __ GREEN
 
                        struct v2f 
                        {
                                fixed4 pos : SV_POSITION;
                        };
                         
                        v2f vert (appdata_base v)
                        {
                                v2f o;
                                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                                return o;
                        }
                         
                        half4 frag(v2f i) : COLOR
                        {
                                fixed4 c = fixed4(0,0,0,1);
                                #ifdef RED
                                c += fixed4(1,0,0,1);
                                #endif
                                #ifdef GREEN
                                c += fixed4(0,1,0,1);
                                #endif
                                #ifdef BLUE
                                c += fixed4(0,0,1,1);
                                #endif
                                return c;
                        }
                        ENDCG
                }
        }
        CustomEditor "ColorsGUI"
}

shader的变种数量可以通过shader面板上面查看到,点击Show按钮可以看到都有哪些变种

dock

做如下实验对比shader_feature:

单一行命令

#pragma shader_feature RED

两个变种: __, RED

#pragma shader_feature RED GREEN

两个变种:RED,GREEN

#pragma shader_feature RED GREEN BLUE

三个变种:RED,GREEN,BLUE



#pragma multi_compile RED

一个变种:RED

#pragma multi_compile RED GREEN

两个变种:RED,GREEN

#pragma multi_compile RED GREEN BLUE

三个变种:RED,GREEN,BLUE



分析:shader_feature 和multi_compile 在Keyword 数量大于1时,生成变种的机制是一样的,都是一个keyword一个变种;当keyword只有1个时,shader_feature 会增加一个none变种。再来做个实验:

#pragma shader_feature _ RED

两个变种: none, RED

可见,当shader_feature 的keyword数量是1时不论是否有__符号,都会增加一个空keyword(__),除了这个在生成变种的机制上和multi_compile都是一致的。

多行命令

#pragma multi_compile __ RED

#pragma multi_compile __ GREEN

四个变种:__,RED,GREEN,RED GREEN

分析:多行命令就是单行命令的乘法组合,shader_feature和multi_compile除了单一keyword时是否补__之外,在多行命令中也是一致的。

二、匹配shader变种机制

为了实验shader变种的匹配,做一个方便定义keyword的shader界面,代码如下:

public class ColorsGUI: ShaderGUI {
 
    private static bool bRed = false;
    private static bool bGreen = false;
    private static bool bBlue = false;
 
        public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
        {
        // render the default gui
        base.OnGUI(materialEditor, properties);
 
        Material targetMat = materialEditor.target as Material;
 
        bRed = Array.IndexOf(targetMat.shaderKeywords, "RED") != -1;
        bGreen = Array.IndexOf(targetMat.shaderKeywords, "GREEN") != -1;
        bBlue = Array.IndexOf(targetMat.shaderKeywords, "BLUE") != -1;
 
        EditorGUI.BeginChangeCheck();
 
        bRed = EditorGUILayout.Toggle("红", bRed);
        bGreen = EditorGUILayout.Toggle("绿", bGreen);
        bBlue = EditorGUILayout.Toggle("蓝", bBlue);
 
        if (EditorGUI.EndChangeCheck())
        {
            if (bRed) 
                targetMat.EnableKeyword("RED");
            else
                targetMat.DisableKeyword("RED");
            if (bGreen)
                targetMat.EnableKeyword("GREEN");
            else
                targetMat.DisableKeyword("GREEN");
            if (bBlue)
                targetMat.EnableKeyword("BLUE");
            else
                targetMat.DisableKeyword("BLUE");
        }
        }
}

shader界面如下:
dock

这样,勾选一个颜色,就会enable一个keyword,通过查看结果颜色就能知道匹配到了哪个shader变种,实验如下:

#pragma multi_compile RED GREEN(两个变种:RED, GREEN)

材质keyword为RED : 显示红色(匹配RED)

材质keyword为GREEN : 显示绿色(匹配GREEN)

材质keyword为__ : 显示红色(匹配RED)

材质keyword为RED GREEN: 显示红色(匹配RED)

分析:当keyword存在正好匹配的变种时直接匹配、当keyword不存在匹配变种时取第一个变种

三、shader变种打包

打包的代码如下:

[MenuItem("Assets/Build AssetBundles")]
    static void BuildAllAssetBundles()
    {
        List<AssetBundleBuild> maps = new List<AssetBundleBuild>();
        maps.Clear();
        //资源打包
 
        string[] files = {
                            "Assets/ShaderVariants/Resources/red.prefab",
                         };
 
        AssetBundleBuild build = new AssetBundleBuild();
        build.assetBundleName = "ShaderVariantsPrefab";
        build.assetNames = files;
        maps.Add(build);
 
        string[] file2s = {
                            "Assets/ShaderVariants/Resources/Shader/Colors.shader",
                          };
 
        AssetBundleBuild build2 = new AssetBundleBuild();
        build2.assetBundleName = "ShaderVariantsShader";
        build2.assetNames = file2s;
        maps.Add(build2);
 
        BuildAssetBundleOptions options = BuildAssetBundleOptions.DeterministicAssetBundle;
        BuildPipeline.BuildAssetBundles("Assets/StreamingAssets", maps.ToArray(), options, BuildTarget.StandaloneWindows);
 
        AssetDatabase.Refresh();
    }

读包并实例化的代码如下:

public class BundleLoader : MonoBehaviour
{
        void Start () {
        load();
        }
 
    public void load()
    {
        StartCoroutine(LoadMainGameObject("file://" + Application.dataPath + "/StreamingAssets/" + "ShaderVariantsShader"));
        StartCoroutine(LoadMainGameObject("file://" + Application.dataPath + "/StreamingAssets/" + "ShaderVariantsPrefab"));
    }
 
    private IEnumerator LoadMainGameObject(string path)
    {
        WWW bundle = new WWW(path);
 
        yield return bundle;
 
        if (bundle.url.Contains("ShaderVariantsShader"))
        {
            //依赖shader包
            bundle.assetBundle.LoadAllAssets();
        }
        else
        {
            UnityEngine.Object obj = bundle.assetBundle.LoadAsset("assets/ShaderVariants/resources/red.prefab");
            Instantiate(obj);
            bundle.assetBundle.Unload(false);
        } 
    }
}

为了对比shader_feature 和multi_compile 以及shader依赖和非依赖打包,做如下实验:

#pragma shader_feature RED GREEN BLUE

将选中RED关键字的prefab打包,加载bundle和其中的prefab,显示了红色,此时改变此材质的keyword为GREEN或者BLUE,没有效果

#pragma multi_compile RED GREEN BLUE

将选中RED关键字的prefab打包,加载bundle和其中的prefab,显示了红色,此时改变此材质的keyword为GREEN或者BLUE,可以显示绿色和蓝色

分析:shader_feature声明变种时,打包只会打包被资源引用的keyword变种,multi_compile声明变种时,打包会把所有变种都打进去

#pragma shader_feature RED GREEN BLUE

将选中RED关键字的prefab和shader依赖打包,加载bundle和其中的prefab,显示了异常粉红,任何变种都没有生效

分析:shader_feature标记的shader单独依赖打包时,任何变种都不会打进去,分析原因估计是unity认为单包中shader没有被引用过

总结:

unity5中新出的shader_feature可以只将引用过的shader变种打进包里面,听起来很有用,可是大部分项目中为了节省冗余shader的内存,shader都是作为依赖包单独成一包的,此时没有任何shader变种被打进包中;更何况即使shader没有依赖打包,如果计划代码中动态修改shader的变种而不是记录在材质里面,此时也不能用shader_feature。基本上我们的项目中shader_feature可以废弃了。。。

MMO服务器引擎kbengine的lua版unity客户端插件

之前做的基于kbengine的一个lua版的mmo小游戏https://github.com/liuxq/StriveGame
有同学提issue希望把插件层独立出来。于是做了逻辑和插件层的解耦,主要代码结构如下:

dock

其中Csharp文件夹中的memoryStream, NetworkInterface, PacketReceiver, PacketSender 都是网络基础类,基本上不会改动热更,并没有lua化;Dbg, Profile, Event, 在lua层都有类似功能,暂时保留c#版本;
KBELuaUtil是lua层需要用到的一些基础函数,还有c#调用lua函数的工具函数指针(为了和tolua解耦,比如如果要用xlua之类的可以传入相应lua插件的调用lua接口)。

其中Lua文件夹中的eventlib.lua和events.lua是lua层的事件结构(event依赖于tolua,如果不用tolua需要修改这里);
其他的lua文件都是同名c#插件的lua化。

为了验证插件的可用性,把kbengine的官方demo做了lua化,包含了所有逻辑:ui,移动,镜头等
游戏截图:

dock
dock

最后附上plugins和demo的github地址:

plugins: https://github.com/liuxq/kbengine_unity3d_lua_plugins

demo: https://github.com/liuxq/kbengine_unity3d_tolua_demo

地形八张纹理的可行性和消耗研究

最近美术希望增加地形纹理到8张,希望研究可行性和消耗,于是做了如下测试。
修改4纹理shader到8张纹理,控制图也从一张增加为两张,第一张的RGBA控制前四个纹理,第二章的RGBA控制后四个纹理,然后用ps做出两个控制图,将图分成八份,shader界面和渲染结果如下。

dock
dock

测试机器是macmini,使用的unity4.7,查看profile,测试结果如下。

内存方面:

512512的dxt1 纹理内存大小128k(安卓ect-4bit,苹果pvrtc-4bit)
256
256的rgba32 纹理内存大小256k

每个场景增加4个dxt1纹理1个rgba32纹理,每个场景增加内存:768k,如果使用mipmap则增加为1.33倍到1m

GPU方面:

1*1米的场景2个drawcall,使用4纹理shader的GPU时间:0.233ms

dock

1*1米的场景2个drawcall,使用8纹理shader的GPU时间:0.464ms

dock

512*512米的场景合并之后13个drawcall,使用4纹理shader的GPU时间:0.805ms

dock

512*512米的场景合并之后13个drawcall,使用8纹理shader的GPU时间:1.274ms

dock

注:这里的512512米用的是1616的网格,共1024个,静态batch过后drawcall达到13

分析8纹理方案的GPU消耗是4纹理的1.6~2倍,由于测试GPU使用的mac,在真机上消耗时间的基数会大大增加。另外测试发现八张贴图的shader如果删掉其中四张,GPU时间消耗不变,因此如果有的图不打算使用8贴图,一定要使用4贴图的shader。

真机兼容性:

部分低端机型由于没有测试过有可能存在shader不支持或者纹理增多导致的显存不足,存在不支持的风险。

总结建议:

项目目前的主要性能瓶颈在内存,所以内存这块消耗的增加需要其他模块考虑降低消耗。
(比如只有256*256的地图才使用8纹理)
GPU这块由于不是瓶颈,暂时考虑可以接受
兼容问题需要考虑,即如果有的机型不支持8纹理,需要替代方案(比如只使用前四张纹理)

任意多边形顺逆时针判断

今天在工作上遇到了需要判断一个给定的任意多边形是逆时针还是顺时针的问题。
如果是凸多边形,那看任意两个边的向量做一个叉乘就知道方向了。考虑到有可能是凹多边形,所以我判断了所有相邻的边的向量叉乘,然后取大于一般的方向。具体代码如下:

//判断多边形是逆时针还是顺时针, true:逆时针

    bool isAntiClock(vector<Vec2d>& polygon)
    {
        int sz = polygon.size();
        int greaterC = 0, lessC = 0;
        for (int i = 0; i < polygon.size(); i++)
        {
            Vec2d a = polygon[i];
            Vec2d b = polygon[(i+1)%sz];
            Vec2d c = polygon[(i+2)%sz];
            if ((b-a)/(c-b) > 0)
                greaterC ++;
            else
                lessC ++;
        }
        if (greaterC > lessC)
        {
            return true;
        }
        else
            return false;
    }

之后,我查了一下网上的资料,用的方法更科学高效,是找到多边形最左下角的节点,然后看该节点相邻两个边的向量方向。因为是最左下角,所以一定是一个凸的节点,顿时觉得自己low爆儿了。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.