赞
踩
2.5D可能并不是一个很精准的词,我这里所指的是DNF这种可以在3个轴向上运动的2D精灵类型的效果。
以DNF为例,精灵实际上没有Z轴坐标的,当按↑,人物向屏幕里(远离玩家)方向移动时(z轴),在渲染上,我们看到人物是在向上移动。而我们按c,人物跳起(y轴),我们仍然看到人物在向上移动。这就是DNF类游戏看起来有点3d效果的奥秘:它营造一种我们在斜向下45度(或者其它角度)观看地面的感觉,因此当人物坐标在Z轴移动时,在屏幕投影位置上我们看到人物向上或向下移动。
所以,这种效果可以通过3维坐标在渲染时的一个变换来完成。
设点A(x1,y1,z1),那么它渲染时的世界坐标会变换为:
x2 = x1,
y2 = y1*cos(a)+z1*cos(90-a)
z2 = 0
其中a代表视线与xz平面的夹角,不过一般Y轴不计算投影,因为要保持精灵的正常显示,不能让它因斜视而变矮。
因此,y2 = y1 + z1*cos(90-a)
最开始,我打算了unity中为一个物体建立两个gameobject,一个物体赋值3维坐标,因为它处于真实位置,因此可直接使用物理系统,另一个物体用上面的映射方法,计算出坐标并使用它来表示精灵的位置,SpriteRenderer附加在第二个物体上。我确实这样实现了,也运行正常。
不过后面我想到,可以用shader来更改显示位置,这样只用一个gameobject,用普通Sprite即可,比较符合直觉,也便于使用。下面先看下效果:
45度角:
这样好处在于,物体的transform和boxcollider仍然是在正常的3D空间中的,所以碰撞检测可以完全以我们熟悉的3D物理组件来完成,而不需要自己实现。
另外,对于要做小游戏的程序员来说,2D游戏对素材要求会小一些。。用此方式,表现力会比用单纯的2d精灵和2d物理好很多。
附上shader,用精灵的默认shader改的:
- Shader "Sprites/YX2.5D"
- {
- Properties
- {
- [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
- _Color ("Tint", Color) = (1,1,1,1)
- [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
- [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
- [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
- [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
- [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
- [HideInInspector] ZFactor ("Z-Factor",float) = 0.707
- [HideInInspector] WOrigin ("Origin",Vector) = (0,0,0,0)
- }
-
- SubShader
- {
- Tags
- {
- "Queue"="Transparent"
- "IgnoreProjector"="True"
- "RenderType"="Transparent"
- "PreviewType"="Plane"
- "CanUseSpriteAtlas"="True"
- }
-
- Cull Off
- Lighting Off
- ZWrite Off
- Blend One OneMinusSrcAlpha
-
- Pass
- {
- CGPROGRAM
- #pragma vertex SpriteVert
- #pragma fragment SpriteFrag
- #pragma target 2.0
- #pragma multi_compile_instancing
- #pragma multi_compile _ PIXELSNAP_ON
- #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
- #include "UnityCG.cginc"
-
- #ifdef UNITY_INSTANCING_ENABLED
-
- UNITY_INSTANCING_CBUFFER_START(PerDrawSprite)
- // SpriteRenderer.Color while Non-Batched/Instanced.
- fixed4 unity_SpriteRendererColorArray[UNITY_INSTANCED_ARRAY_SIZE];
- // this could be smaller but that's how bit each entry is regardless of type
- float4 unity_SpriteFlipArray[UNITY_INSTANCED_ARRAY_SIZE];
- UNITY_INSTANCING_CBUFFER_END
-
- #define _RendererColor unity_SpriteRendererColorArray[unity_InstanceID]
- #define _Flip unity_SpriteFlipArray[unity_InstanceID]
-
- #endif // instancing
-
- CBUFFER_START(UnityPerDrawSprite)
- #ifndef UNITY_INSTANCING_ENABLED
- fixed4 _RendererColor;
- float4 _Flip;
- #endif
- float _EnableExternalAlpha;
- CBUFFER_END
-
- // Material Color.
- fixed4 _Color;
- float4 WOrigin;
- float ZFactor;
- float YFactor;
-
- 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;
- UNITY_VERTEX_OUTPUT_STEREO
- };
-
- v2f SpriteVert(appdata_t IN)
- {
- v2f OUT;
-
- UNITY_SETUP_INSTANCE_ID (IN);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
-
- #ifdef UNITY_INSTANCING_ENABLED
- IN.vertex.xy *= _Flip.xy;
- #endif
- float4 wpos = mul(unity_ObjectToWorld, IN.vertex)-WOrigin;
- float4 wposMap = float4(wpos.x,wpos.y + wpos.z*ZFactor,0,wpos.w);
- OUT.vertex = mul(UNITY_MATRIX_VP,wposMap);
- OUT.texcoord = IN.texcoord;
- OUT.color = IN.color * _Color * _RendererColor;
-
- #ifdef PIXELSNAP_ON
- OUT.vertex = UnityPixelSnap (OUT.vertex);
- #endif
-
- return OUT;
- }
-
- sampler2D _MainTex;
- sampler2D _AlphaTex;
-
- fixed4 SampleSpriteTexture (float2 uv)
- {
- fixed4 color = tex2D (_MainTex, uv);
-
- #if ETC1_EXTERNAL_ALPHA
- fixed4 alpha = tex2D (_AlphaTex, uv);
- color.a = lerp (color.a, alpha.r, _EnableExternalAlpha);
- #endif
-
- return color;
- }
-
- fixed4 SpriteFrag(v2f IN) : SV_Target
- {
- fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
- c.rgb *= c.a;
- return c;
- }
- ENDCG
- }
- }
- }
使用方法为,创建一个材质,使用上面提供的shader,默认就是45度角的效果。然后SpriteRender使用该材质。
为了方便起见,还写了一个c#脚本,方便视角、原点等参数设置,但它不是必须的:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Sprite25D : MonoBehaviour {
-
- public Vector3 Origin;
- public float Angle = 45;
- private new SpriteRenderer renderer;
-
-
- private void OnEnable()
- {
- Apply();
- }
-
- public void Apply()
- {
- if (renderer == null)
- {
- renderer = GetComponent<SpriteRenderer>();
- }
- Material mat;
- if (Application.isPlaying)
- mat = renderer.material;
- else
- mat = renderer.sharedMaterial;
- mat.SetVector("WOrigin", Origin);
- mat.SetFloat("ZFactor", Mathf.Sin(Angle * Mathf.Deg2Rad));
- }
- }
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEditor;
-
- [CustomEditor(typeof(Sprite25D))]
- public class Sprite25DInspector : Editor
- {
-
- public override void OnInspectorGUI()
- {
- base.OnInspectorGUI();
- GUILayout.Label("以下操作会修改sharedMaterial");
- if (GUILayout.Button("设置当前位置为映射原点"))
- {
- var inst = (target as Sprite25D);
- var pos = inst.transform.position;
- inst.Origin = new Vector3(pos.x, pos.y, pos.z);
- inst.Apply();
- }
- if (GUILayout.Button("重置原点为zero"))
- {
- var inst = (target as Sprite25D);
- inst.Origin = Vector3.zero;
- inst.Apply();
- }
- }
- }
就是这样了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。