Unity3D Custom Render Texture Shader 编译到低版本 Shader model 环境

写好的 Custom Render Texture 的 shader 在打包的时候报出了一个错误:

[Unity] Player export failed. Reason: Shader error in 'Hidden/MyCustomRenderTextureShader': 'asuint' : no matching overloaded function found at ***/CGIncludes/UnityCustomRenderTexture.cginc(189) (on gles)

查资料发现 asuint 这个函数是在 Shader Model 4 (SM4) 才开始支持。
然后稍微看了一下 cginc 里边的代码,发现只有一个地方用到了这个函数——是把浮点类型的 primitive ID 转成 uint 类型。

OUT.primitiveID = asuint(CustomRenderTexturePrimitiveIDs[primitiveID]);

这个 primitiveID 是 update zone 的当前正在处理的索引,而我目前并没有使用很多 update zone 来实现效果的需求,所以决定尝试一下,直接将 float 类型强转成 uint 。

OUT.primitiveID = (uint)(CustomRenderTexturePrimitiveIDs[primitiveID]);

操作步骤:

  • 在自己的工作目录下创建一个自己的 CustomRenderTexture 的 cginc ,也就是新建个文本文件,把后缀改成 .cginc。
  • 把 UnityCustomRenderTexture.cginc 中的代码全部复制到自己新建的 cginc 中,并且按照上面代码修改。
  • 别忘了修改一下头部的宏定义名称 #ifndef UNITY_CUSTOM_TEXTURE_INCLUDED 跟默认的做一个区分。
  • 在 shader 代码中引用我们自己的 cginc ,就可以用了。

Unity3D UGUI 基础布局

play-sharp-fill

1. 轴点 (Pivot)
作为控件的“轴”,在控件状态改变时对最终布局产生影响。比如,旋转的时候控件就以轴点为中心旋转,调整尺寸或缩放的时候轴点会固定在原位置。

2. 锚点 (Anchor)
Anchor 看起来的样子是四个相对的三角形。Anchor 决定了当前控件与父控件的位置关系。控件的 Rect Transform 的坐标 Pos X 和 Pox Y 就以 Anchor 为原点。也就是 Pivot 会放在 Anchor + (Pos X, Pox Y) 的位置上。当父控件状态(旋转、缩放、移动等)发生变化时,控件会保持这个关系跟随父控件。
在 Anchor 选择面板,按住 Shift 选择会同时将 Pivot 设置在 Anchor 选择的相同的位置;按住 Alt 选择会将控件位置同时停靠在 Anchor 位置。如果要同时将 Pivot 和位置放在 Anchor 点,就同时按住 Shift 和 Alt。

3. 尺寸自适应
也可以选择自动伸展的 Anchor 比如,全屏或者 X 轴或 Y 轴方向自动伸展。这个时候在 Rect Transform 中的坐标或(和)尺寸设置会变成 Left Right Top Bottom 。这些值是相对 Anchor 自动伸展方向的距离。这个概念是非常有用的,可以实现控件相对父控件同比例缩放。具体效果看我撸的视频会比较清楚一点。

Unity3D 解决默认碰撞检测的缺陷 —— 实现理想的匀速直线运动

首先介绍一下默认碰撞检测存在的问题。先不提【我只想要你丫告诉我碰撞盒撞上了,却非得要我挂个钢体组件,还得接受重力,阻尼,角速度的影响。不是在逗我?】,那么我们手动勾选上钢体的 Is Kinematic 属性,自己来控制。
我们看下边的图:
collider1
当这个圆走到实线圆圈位置的时候,就会触发碰撞检测回调。当然,如果是在虚线圆圈的位置,回调是不会被触发的。那么问题来了,如果一帧的移动距离刚好是从虚线位置移动到实线位置,情况就是这样的,移动前一帧不会触发碰撞回调,而后一帧两个碰撞体已经嵌到一起了。
当然,如果只是小小的插入一下,也并没有什么问题,接下来看下面的图:
collider2
当一帧的移动距离超过碰撞目标的时候,奇迹发生了,这时碰撞检测的回调根本不会触发。也就是说在速度足够快的情况下,你的墙是挡不住你的球的。
为了实现一个在理想条件下的匀速直线运动,我就用 Unity 本身的射线检测功能来解决这个问题。
首先取出自己的碰撞盒

var selfCollider = GetComponent<Collider2D>();
Collider 中有 Raycast 和 Cast 两个方法,参数差不多,具体的区别,还得画两张图:
ray1
图一
ray2
图二
ray3
图三
图一、图二中蓝色的线就是 Raycast 的结果,是从自己的中心射到目标碰撞盒上,如果按照这个射线的距离运动,结果就是图二的样子。
图三种的蓝色线是 Cast 方法的结果,射到目标碰撞盒上的是自己碰撞盒与目标碰撞盒的交点。这个距离就是从当前点移动到发生碰撞的点的距离。
简单一点就用 Cast 方法就够了,Raycast 比 Cast 提供了更多细节操作,比如专门针对某一层的检测等,如果需要做更细节的优化,就得自己用 Raycast 方法来处理。

RaycastHit2D[] casthits = new RaycastHit2D[maxRaycastCache]; 
int num = selfCollider.Cast(forward, casthits);
RaycastHit 中比较重要的属性:
collider —— 目标碰撞盒
distance —— 到碰撞点的距离(上面图3中蓝线的长度)
normal —— 碰撞点的法线
point —— 目标碰撞点
接下来的逻辑就是:
比较本帧移动距离和 distance ,如果本帧移动距离比较小,就直接移动。

float stepDistance = speed * Time.fixedDeltaTime; 
if (stepDistance < distance) 
{ 
   gameObject.transform.Translate(forward * stepDistance); distance -= stepDistance; 
}
当然之后要把总距离减去这步的移动距离。
如果这一帧的距离大于剩余的距离了,就需要处理反射了。
先移动到碰撞点→方向向量反射→继续移动完。

while (stepDistance > nextDistance) 
{ 
    gameObject.transform.Translate(forward * nextDistance); 
    stepDistance -= nextDistance;

    forward = Vector2.Reflect(forward, normal); 
    /* 找下一个碰撞目标 */ 
    …… 
}
打完收工。
当然这只是简单的描述,实际上还会遇到一些坑,比如当前自己和一个碰撞盒有重叠的时候,取到的第一个 RaycastHit 永远是有接触的这个碰撞盒,此时与方向向量无关,所以需要根据自己的逻辑处理。还有同时碰撞两个或多个碰撞盒等,都要小心处理。
RSS
Follow by Email
YouTube
YouTube
Pinterest
fb-share-icon
LinkedIn
Share
VK
Weibo
WeChat
WhatsApp
Reddit
FbMessenger
Copy link
URL has been copied successfully!