2016-04-03 22:18:37 MAOMAOXIAOHUO 阅读数 5744
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

本文使用UV偏移,创建车道方向动画

原始素材
原始素材

最终效果
最终效果(带水平向移动)

Unity3d纹理坐标

这里写图片描述
偏移:Offset(xo,yo),将原点偏移到(xo,yo)。
平铺:Tiling(xt,yt),以偏移后的坐标为新原点,平铺(xt,yt)范围
Unity3d在创建Material,默认Offset = (0, 0),Tiling(1, 1)。即原点不偏移,平铺方向各1次。也就是原图显示。

Unity3d与OpenGl的纹理坐标同持一致。U方向从左到右,V方向从下到上。而DX U从左到右,V从上到下
这里写图片描述 这里写图片描述


这里写图片描述Offset = (0, 0.5) Tiling = (0.5, 0.5)

这里写图片描述 Offset = (0.5, 0.5) Tiling = (0.5, 0.5)

这里写图片描述 Offset = (0, 0)Tiling = (0.5, 0.5)

这里写图片描述 Offset = (0.5, 0) Tiling = (0.5, 0.5)


这里写图片描述 Offset = (0, 0.5)Tiling = (1.0, 0.5)

这里写图片描述 Offset = (0, 0)Tiling = (1.0, 0.5)

这里写图片描述 Offset = (0, 0)Tiling = (0.5, 1)

这里写图片描述 Offset = (0.5, 0)Tiling = (0.5, 1)


GPU实现

了解纹理坐标后。接下来,我们将编写Shader实现车道方向动画。实现原理就是持续地修改贴图在U方向的偏移。(注意创建场景、GameObject、Material等基本操作不在此本文说明)。下面给出UV偏移的Shader代码。

Shader "Custom/UVOffset" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _USpeed("USpeed ", float) = 1.0
        _UCount("UCount", float) = 1.0
        _VSpeed("VSpeed", float) = 1.0
        _VCount("VCount", float) = 1.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert

        // 贴图
        sampler2D _MainTex;

        // U轴方向滚动速度
        float _USpeed; 
        // U轴方向平铺个数
        float _UCount;

        // V轴方向滚动速度
        float _VSpeed;
        // V轴方向平铺个数
        float _VCount;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            float2 uv = IN.uv_MainTex;
            float detalTime = _Time.x;

            //  计算X轴方向变化
            uv.x += detalTime * _USpeed;
            uv.x *=  _UCount;

            // 计算Y轴方向变化
            uv.y += detalTime * _VSpeed;
            uv.y *= _VCount;

            half4 c = tex2D (_MainTex, uv);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

顶点纹理 = 时间 * 速度
即 uv.x += _Time.x * _USpeed 其中_Time是Unity3d内置变量。

Name Type Value
_Time float4 Time (t/20, t, t*2, t*3), use to animate things inside the shaders.

参考链接http://docs.unity3d.com/Manual/SL-UnityShaderVariables.html

当前纹理(平铺)= 当前纹理 * 平铺个数
即 uv.x *= _UCount;
我们假设顶点范围[0,1],以U轴方向为例(顶点与纹理不是同一个东西
在_UCount = 1的情况下,uv.x = uv.x * 1,顶点与纹理一一对应。
在_UCount = 2的情况下,uv.x = uv.x * 2, 原本水平方向x = 0.5顶点,这次由于uv.x * 2,抽取了uv.x = 1.0的纹理(也可以说是uv.x = 0.0的纹理)。即相比于_UCount = 1,这次在顶点从开始x = 0.5,便重新以uv.x =0提取纹理。

CPU实现

为GPU计算的负担,可以将UV偏移的计算工作放到CPU中,最后将计算结果传回GPU渲染即可。下面是使用MonoBehaviour实现UV偏移代码。
主要使用到了Material.GetTextureOffsetSetTextureOffset对应纹理坐标Offset
Material.GetTextureScaleSetTextureScale对应纹理坐标Tiling

using UnityEngine;

public class UVOffset: MonoBehaviour {

    // U轴方向滚动速度
    public float USpeed = 1.0f;
    // U轴方向平铺个数
    public int UCount = 1;

    // V轴方向滚动速度
    public float VSpeed = 1.0f;
    // V轴方向平铺个数
    public int VCount = 1;

    private Material mat = null;

    private void Awake()
    {
        if (this.renderer != null)
            this.mat = this.renderer.material;
    }

    private void Update()
    {
        if (this.mat == null)
            return;

        Vector2 offset = this.mat.GetTextureOffset("_MainTex");
        Vector2 tiling  = this.mat.GetTextureScale("_MainTex");

        offset.x += Time.deltaTime * USpeed;
        offset.y += Time.deltaTime * VSpeed;
        tiling.x = UCount;
        tiling.y = VCount;

        this.mat.SetTextureOffset("_MainTex", offset);
        this.mat.SetTextureScale("_MainTex", tiling);
    }
}
2019-01-07 17:07:21 PangNanGua 阅读数 318
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

 之前有一哥们把道光爷头像加到按钮上了,这种做法是错误的

点击链接加入QQ群聊【Unity3D开发者】

正确的效果如下:

正确的核心思路是一个UV颜色的旋转和移动

			fixed4 frag(v2f i) : COLOR
			{
				fixed4 color = tex2D(_MainTex, i.uv);
				fixed2 light_uv = (i.uv + fixed2(0, _Time.y * _speed % _interval - _interval * 0.5)) * _width;
				light_uv = mul(fixed2x2(-0.5, _rotation, 0, 0), light_uv);
				fixed rgba = lerp(1, 0, abs(light_uv.x));
				if( rgba > 0 )
					color.rgb += rgba;
				return color;
			}

整体可用代码如下,可以直接拿去用。

Shader "mgo/image/image_move_light" 
{
	Properties
	{
		[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
		_Color("Tint", Color) = (1,1,1,1)

		_StencilComp("Stencil Comparison", Float) = 8
		_Stencil("Stencil ID", Float) = 0
		_StencilOp("Stencil Operation", Float) = 0
		_StencilWriteMask("Stencil Write Mask", Float) = 255
		_StencilReadMask("Stencil Read Mask", Float) = 255

		_ColorMask("Color Mask", Float) = 15

		[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0

		_width("Light Width", Range(0, 20)) = 1 //光线宽度系数,值越大效果越细  
		_speed("Light Speed", Range(2, 30)) = 5
		_rotation("Light Angle" , Range(0.1, 0.8)) = 0.15
		_interval("Time Interval" , Range(1, 40)) = 4
	}

		SubShader
		{
			Tags
			{
				"Queue" = "Transparent"
				"IgnoreProjector" = "True"
				"RenderType" = "Transparent"
				"PreviewType" = "Plane"
				"CanUseSpriteAtlas" = "True"
			}

			Stencil
			{
				Ref[_Stencil]
				Comp[_StencilComp]
				Pass[_StencilOp]
				ReadMask[_StencilReadMask]
				WriteMask[_StencilWriteMask]
			}

			Cull Off
			Lighting Off
			ZWrite Off
			ZTest[unity_GUIZTestMode]
			Blend SrcAlpha OneMinusSrcAlpha
			ColorMask[_ColorMask]

			Pass
			{
				Name "Default"
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma target 2.0

				#include "UnityCG.cginc"
				#include "UnityUI.cginc"

				#pragma multi_compile __ UNITY_UI_ALPHACLIP

				struct appdata_t
				{
					float4 vertex   : POSITION;
					float4 color    : COLOR;
					float2 texcoord : TEXCOORD0;
					UNITY_VERTEX_INPUT_INSTANCE_ID
				};

				struct v2f
				{
					float4 vertex   : SV_POSITION;
					fixed4 color : COLOR;
					float2 texcoord  : TEXCOORD0;
					float4 worldPosition : TEXCOORD1;
					UNITY_VERTEX_OUTPUT_STEREO
				};

				fixed4 _Color;
				fixed4 _TextureSampleAdd;
				float4 _ClipRect;

				uniform float _width;
				uniform float _speed;
				uniform float _rotation;
				uniform float _interval;

				v2f vert(appdata_t IN)
				{
					v2f OUT;
					UNITY_SETUP_INSTANCE_ID(IN);
					UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
					OUT.worldPosition = IN.vertex;
					OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

					OUT.texcoord = IN.texcoord;

					OUT.color = IN.color * _Color;
					return OUT;
				}

				sampler2D _MainTex;

				fixed4 frag(v2f IN) : SV_Target
				{
					half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

					color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);

				#ifdef UNITY_UI_ALPHACLIP
					clip(color.a - 0.001);
				#endif

				fixed2 light_uv = (IN.texcoord + fixed2(0, _Time.y * _speed % _interval - _interval * 0.5)) * _width;
				light_uv = mul(fixed2x2(-0.5, _rotation, 0, 0), light_uv);
				fixed rgba = lerp(1, 0, abs(light_uv.x));
				if (rgba > 0)
					color.rgb += rgba;
				return color;
			}
			ENDCG
		}
	}
}

 

2018-02-03 22:01:51 qq_36429057 阅读数 1202
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

上一篇为大家介绍了Image简单的实现Player的移动并映射到小地图上:上篇链接点击打开链接

本篇为大家分享的是通过RawImage来实现实现小地图的缩放:最终实现效果Player的移动会控制RawImage的UV信息的改变使得RawImage移动,呈现一下最终效果:

具体UI的调整适配这里我就不多介绍了,界面的布局调整可以看上一篇 这里只是将Image换成了RawImage去实现,用面板Plane来代替记得加Collider哦

一般情况下我们用RawImage去实现精灵动画当然也是通过UV信息的大小调整

UVRect:X  Y 表示比例的起点 ;W H表示比例的宽和高

WH用来调节小地图的事业范围:   比如这里我调试的WH设定为0.25,0.25就将RawImage缩小了0.25,在Canvas下始终显示图片的1/4.

XY用来控制小地图的移动,也是与Player的位置实时更新的

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TestMap : MonoBehaviour
{
    public Transform player;
    public Transform myTerrain;
    Collider myTerrainCol;
    RectTransform litMap;
    RectTransform sonRect;
    float widthRate;
    float heightRate;
    RawImage rawImage;
    Vector3 tmpAngle;
    float timeCount = 0;
    Rect rowRect;

    void Start()
    {
        myTerrain = GameObject.FindGameObjectWithTag("Terrian").transform;
        myTerrainCol = myTerrain.GetComponent<Collider>();
        litMap = transform.parent.GetComponent<RectTransform>();
        sonRect = transform.GetComponent<RectTransform>();
        rawImage = transform.parent.GetComponent<RawImage>();

    }

    void Update()
    {
        timeCount += Time.deltaTime;
        if (timeCount > 0.5f)
        {
            timeCount = 0;
            UpdatePos();
        }
    }
    void UpdatePos()
    {
        widthRate = (player.transform.position.x - myTerrain.position.x) / myTerrainCol.bounds.size.x;
        heightRate = (player.transform.position.z - myTerrain.position.z) / myTerrainCol.bounds.size.z;
        //这是相对于RawImage的偏移量 根据UV取得的WH比例比如WH为0.25,那么加的位移偏移量为0.5-1/8=0.375 WH为0.5那么加的位移偏移量为0.5-1/4=0.25
        rowRect = rawImage.uvRect;
        rowRect.x = widthRate + 0.375f;
        rowRect.y = heightRate + 0.375f;
        rawImage.uvRect = rowRect;

        tmpAngle = sonRect.localEulerAngles;
        tmpAngle.z = 90 - player.localEulerAngles.y;
        sonRect.localEulerAngles = tmpAngle;

    }
}

但是以上操作为发现一个问题就是当移动到边界时会暴露,超出视野,当然真实项目中是绝对不允许这种的,你可以偷个懒一般RPG场景的的边缘留出来这些可能暴露的范围,让玩家限定范围不在场景边缘移动就好啦~当然也不是没有解决的办法

这时我们就要加限制条件了,有两种实现方法:

方案一:你可以在要暴露的范围控制WH的范围,越往边界走,调节WH的可视范围越大;

方案二:通过调节当要超出事业范围时控制UV信息的XY不再发生改变,而是通过调节Player的移动位置更新,实现方法就和Image更新位置的操作一样了,

但是这个限定条件个人觉得很繁琐,要加多层判定,这里只提供大致思路  我做了几个判定后发现可以限定视野不会暴露,但是PlayerIcon的位置会有微小的不同步,自己测试一下就好了

以下是我做了视野不会超出视野范围的限定,实现效果就是我前面附上的动图操作,也许你有更好的解决方案欢迎交流

吐舌头

下一篇我将会向大家介绍第三种实现等比例映射小地图的方案------通过ScrollView去实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TestMap : MonoBehaviour
{
    public Transform player;
    public Transform myTerrain;
    Collider myTerrainCol;
    RectTransform litMap;
    RectTransform sonRect;
    float widthRate;
    float heightRate;
    RawImage rawImage;
    Vector3 tmpAngle;
    float timeCount = 0;
    Rect rowRect;
    Vector2 tmpPos = Vector2.zero;

    void Start()
    {
        myTerrain = GameObject.FindGameObjectWithTag("Terrian").transform;
        myTerrainCol = myTerrain.GetComponent<Collider>();
        litMap = transform.parent.GetComponent<RectTransform>();
        sonRect = transform.GetComponent<RectTransform>();
        rawImage = transform.parent.GetComponent<RawImage>();
        

    }

    void Update()
    {
        timeCount += Time.deltaTime;
        if (timeCount > 0.5f)
        {
            timeCount = 0;
            UpdatePos();
        }
    }
    void UpdatePos()
    {
        widthRate = (player.transform.position.x - myTerrain.position.x) / myTerrainCol.bounds.size.x;
        heightRate =(player.transform.position.z - myTerrain.position.z) / myTerrainCol.bounds.size.z;
       // rowRect = rawImage.uvRect;
       // rowRect.x = widthRate +0.375f;
       // rowRect.y = heightRate +0.375f;
       // rawImage.uvRect = rowRect;


        rowRect = rawImage.uvRect;

        if (widthRate + 0.375f > 0f && widthRate + 0.375f < 0.75f && heightRate + 0.375f > 0 && heightRate + 0.375f < 0.75f)
        {
            rowRect.x = widthRate + 0.375f;
            rowRect.y = heightRate + 0.375f;
        }
        else
        {           
            if (widthRate + 0.375f <= 0f)
            {
                rowRect.x = 0;
            }
            else if (widthRate + 0.375f >= 0.75)
            {
                rowRect.x = 0.75f;
            }
            else if (heightRate + 0.375f >= 0.75)
            {
                rowRect.y = 0.75f;
            }
            else if (heightRate + 0.375f <= 0f)
            {
                rowRect.y = 0;
            }
            //tmpPos.x = litMap.sizeDelta.x * widthRate;
            //tmpPos.y = litMap.sizeDelta.y * heightRate;
            //sonRect.anchoredPosition = tmpPos;
        }

        
        rawImage.uvRect = rowRect;
        Debug.Log("rowRect==="+rowRect);
        Debug.Log("rawImage.uvRect===" + rawImage.uvRect);
        tmpAngle = sonRect.localEulerAngles;
        tmpAngle.z = 90 - player.localEulerAngles.y;
        sonRect.localEulerAngles = tmpAngle;

    }

   
}





2020-03-04 09:37:46 SwordMaster8060 阅读数 2373
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

 本人一直喜欢开发战争题材的游戏,在此简单介绍一下坦克履带模拟器的工作原理:

如果地形是平面,也就是说场景地形没有崎岖不平,坦克履带的移动效果完全可以采用偏移坦克履带材质的贴图来实现,即采用UV动画。

如果地形崎岖不平,这时候如果开动坦克,履带就要有适应地形的表现效果,而不仅仅是偏移履带贴图,因此本人采用的方法如下:

(1)坦克的履带是蒙皮,并且在每个轮子的正中心的位置都绑定了骨骼,并且刷好权重,当然绑定骨骼这一步骤我是在3dmax中完成的:

(2)坦克轮子的运动采用轮体碰撞器来模拟:

(3)游戏运行时,坦克的轮体碰撞器由于直接和地面发生接触,运行时相对于坦克自身的垂直方向会不断产生位移,那么就记录下轮体碰撞器此时的位置信息,再将此位置信息赋值给对应位置的轮子模型和对应位置的轮子骨骼,又由于相应位置的轮子骨骼是和履带蒙皮绑定在一起的,轮子骨骼相对于坦克发生位移,就会带动对应部分的履带发生拉伸或挤压的形变效果,这样坦克的履带就可以很好地模拟适应地形的效果:

此坦克履带模拟器可以在Unity Asset Store上下载:

https://assetstore.unity.com/packages/slug/99013

这是本人编写的坦克履带模拟器的编辑器界面:

 

如果有技术上的疑问请给我发邮件

2016-03-28 11:36:33 qq18052887 阅读数 8245
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

一直在思考怎么让场景更有生机,我觉得植被的随风摆动是必不可少的.CE3引擎的植被bending就做得特别棒.我也准备在手机上做一套.


先分析一下植被摆动常见的几种做法.其实不管哪种做法,核心就是让植被顶点做动画,有的顶点动的少(比如树根),有的顶点动的多(比如树顶),根据怎么样的权重来动?


方案1:  用UV来做权重.

这种方案对UV展开有要求,要从0到1,只适合面片草,这样的话草的根部和顶部的摆动权重就是一个0到1的线性的变化,随便用一个正玄波就能实现简单摆动了,

				// 根据UV实现简单的顶点动画
				float4 newPos = v.vertex; 
				newPos.xyz += _Wind_Simple.xyz * v.texcoord.y * _BendingFactor;


方案2: 用顶点和模型原点的距离来做权重.

 CE3的Main Bending就是这种方案.顶点到模型原点的距离,其实就是模型的顶点值,再用这个值除以包围盒的参数,就能得到每个顶点0到1的摆动权重,很简单是吧,这种方案对UV就没要求,适合所有植被,但是,这种方案不能用于合批,比如Unity自带的静态合批,因为合批以后,模型其实已经变了,顶点到原点的距离也已经变了.

方案3:用顶点颜色来做权重

CE3的Detail Bending就是此方案.让美术刷顶点颜色,来作为摆动权重,顶点颜色有几个通道,每个通道实现一种频率的摆动,这样就可以实现比较复杂的摆动,比如Blue通道用来处理主干的摆动,Red通道处理树枝的摆动,Green通道用来处理树叶的细节抖动.这种方案好处是,不受UV和合批的影响,适合任何植被,而且还可以实现比较复杂的细节抖动,让抖动更真实.麻烦的就是要教会美术刷顶点色,而且效率开销最大.

此方案应该是最完美的植被摆动方案,GPU GEM3对此有详细分析:

点击打开链接



结合项目实际情况,我的整体方案如下:

1.草用第一种,效率比较高

2.树木和旗子用第三种,控制使用,而且尽量减少这种模型的顶点数量,


全局风控制,

摆动的幅度还应该受风影响:

草的话用一种风,树木的话单独用一种风.

[ExecuteInEditMode] 
public class VegetationWind : MonoBehaviour
{
    public Vector4 Wind = new Vector4(0.85f, 0.075f, 0.4f, 0.5f);
    public float WindFrequency = 0.75f;

    private float WaveFrequency = 4.0f;
    private float WaveAmplitude = 0.1f;

    void Start()
    {
        Shader.SetGlobalVector("_Wind_VertexColor", Wind);
        Shader.SetGlobalVector("_Wind_Simple", Wind);
    }

    void Update()
    {
        // wind 1
        Vector4 Wind1 = Wind * ((Mathf.Sin(Time.realtimeSinceStartup * WindFrequency)));
        Wind1.w = Wind.w;

        // wind 2q
        //Vector4 Wind2 = Wind1 * Wind1.w;
        Vector4 Wind2 = new Vector4();
        Wind2.x += Mathf.Sin(Time.realtimeSinceStartup * WaveFrequency) * WaveAmplitude;
        Wind2.y = 0;
        Wind2.z += Mathf.Sin(Time.realtimeSinceStartup * WaveFrequency + Mathf.PI * 0.5f) * WaveAmplitude;
        Wind2.w = 0;

        Shader.SetGlobalVector("_Wind_VertexColor", Wind1);
        Shader.SetGlobalVector("_Wind_Simple", Wind2);
    }
}



没有更多推荐了,返回首页