Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Ever participated in one our Game Jams? Want pointers on your project? Our Evangelists will be available on Friday to give feedback. Come share your games with us!
    Dismiss Notice

Multi-angle sprites mechanics

Discussion in 'General Graphics' started by Metalhawk93, Feb 9, 2017.

  1. Metalhawk93


    Jul 13, 2016
    So here's the basic premise of what I'm trying to do. I'm in the middle of making a Racing game, where 2D characters move around a 3D environment.

    The player object is set up as a parent which consists of target object, the camera, and graphics component. The Parent object itself is assigned with a box collider, and rigidbody. and a simple controller script for the moment being. The graphics object is a basic quad object set up with a simple billboard script, which makes the object face towards the assigned camera relative to said cameras forward position.
    Code (CSharp):
    1. public Camera m_Camera;
    3.     void Update()
    4.     {
    5.         transform.LookAt(transform.position + m_Camera.transform.rotation * Vector3.forward,
    6.             m_Camera.transform.rotation * Vector3.up);
    7.     }
    What I want, is to create a player object that shows different sprites states depending on the angle the camera views it from. Sort of like what was done with Doom like the first video shown,

    or in this case, Mario Kart 64.

    Now I have a sample script, which I believe was used for enemy sprites, but is probably goes by the same principle.

    Code (CSharp):
    1. int GetAngleIndex()
    2.      {
    3.          var dir = GetComponent<Camera>().transform.position - transform.parent.forward;
    4.          var enemyAngle = Mathf.Atan2(dir.z, dir.x) * Mathf.Rad2Deg;
    5.          if (enemyAngle < 0.0f)
    6.              enemyAngle += 360;
    7.          Debug.Log("Angle from the player is: " + enemyAngle);
    8.          if (enemyAngle >= 292.5f && enemyAngle < 337.5f)
    9.              return 8;
    10.          else if (enemyAngle >= 22.5f && enemyAngle < 67.5f)
    11.              return 2;
    12.          else if (enemyAngle >= 67.5f && enemyAngle < 112.5f)
    13.              return 3;
    14.          else if (enemyAngle >= 112.5f && enemyAngle < 157.5f)
    15.              return 4;
    16.          else if (enemyAngle >= 157.5f && enemyAngle < 202.5f)
    17.              return 5;
    18.          else if (enemyAngle >= 202.5f && enemyAngle < 247.5f)
    19.              return 6;
    20.          else if (enemyAngle >= 247.5f && enemyAngle < 292.5f)
    21.              return 7;
    22.          else if (enemyAngle >= 337.5f || enemyAngle < 22.5f)
    23.              return 1;
    24.          else return 0;
    25.      }
    Still, If anyone has any questions to ask, advice to give out, or any critiques to give, I probably would appreciate the attention. There's a lot I got to figure out.
  2. jvo3dc


    Oct 11, 2013
    I used to do this by having a model that contained all view directions. (A bit like the classic + shaped tree models.) The shader would then reject the views that were not needed. Indeed based on the angle between the forward direction of the camera and the normal of the view. (Using the view direction leads to some undesired results.)

    The idea behind this is that it is more batch friendly and the GPU is fast enough to skip some extra pixels anyway. This might apply less directly to mobile hardware.
    Last edited: Feb 10, 2017
  3. Metalhawk93


    Jul 13, 2016
    do you mean like the ones used in The Legend of Zelda: Ocarina of Time? Just how might something like that be constructed?
  4. bgolus


    Dec 7, 2012
    You can do the rejection using the cameras forward dir in the vertex shader by moving the vertex behind the camera. Way cheaper than pixel rejection, mobile friendly.
  5. Metalhawk93


    Jul 13, 2016
    could it work for a simple quad object as well? Also, how does rejection work with vertex shaders?
  6. bgolus


    Dec 7, 2012
    You're rejecting each vertex individually, so you have to be sure the test you use to reject is the same for all vertices of a polygon, but by placing the polygon behind the camera it will get automatically rejected by the GPU during rasterization.
  7. Metalhawk93


    Jul 13, 2016
    ohhh, okay starting to get it now. thanks for the explanation.
  8. Metalhawk93


    Jul 13, 2016
    alright, so I've created a simple vertex shader to start things off. I just need to figure out where I edit it so that able to move the vertex behind my camera.

    Code (CSharp):
    1. Shader "Unlit/sprite shader"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 100
    12.         Pass
    13.         {
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             // make fog work
    18.             #pragma multi_compile_fog
    20.             #include "UnityCG.cginc"
    22.             struct appdata
    23.             {
    24.                 float4 vertex : POSITION;
    25.                 float2 uv : TEXCOORD0;
    26.             };
    28.             struct v2f
    29.             {
    30.                 float2 uv : TEXCOORD0;
    31.                 UNITY_FOG_COORDS(1)
    32.                 float4 vertex : SV_POSITION;
    33.             };
    35.             sampler2D _MainTex;
    36.             float4 _MainTex_ST;
    38.             v2f vert (appdata v)
    39.             {
    40.                 v2f o;
    41.                 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    42.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    43.                 UNITY_TRANSFER_FOG(o,o.vertex);
    44.                 return o;
    45.             }
    47.             fixed4 frag (v2f i) : SV_Target
    48.             {
    49.                 // sample the texture
    50.                 fixed4 col = tex2D(_MainTex, i.uv);
    51.                 // apply fog
    52.                 UNITY_APPLY_FOG(i.fogCoord, col);          
    53.                 return col;
    54.             }
    55.             ENDCG
    56.         }
    57.     }
    58. }
    Now do I adjust the vertex for postition here

    Code (CSharp):
    1. struct appdata
    2.             {
    3.                 float4 vertex : POSITION;
    4.                 float2 uv : TEXCOORD0;
    5.             };
    of perhaps here?

    Code (CSharp):
    1. struct v2f
    2.             {
    3.                 float2 uv : TEXCOORD0;
    4.                 UNITY_FOG_COORDS(1)
    5.                 float4 vertex : SV_POSITION;
    6.             }
    Anyone familiar with vertex shaders?
    Last edited: Feb 11, 2017
  9. bgolus


    Dec 7, 2012
  10. Metalhawk93


    Jul 13, 2016
    Alright, so I've read through every part of shaders introduction, and now I think a have a good idea on what I should do. It might not be exactly though, so feel free to correct me on thing mentioned.

    So for my shader, it has to work with a sprite sheet, specifically it various units. For my character, there are twelve sprites being used for it, ten of which will be flip horizontally. In total that makes 22 images used for a three hundred sixty degree rotation around a player, and as "bgolus" said in the comments,
    Using a vertex shader, If I program the units of my sprite sheets with
    in way that similar to an animation transition, then maybe it might give the illusion of a 2d character with a 3d rotation. Basically, depending on the angle the graphics face the assigned camera from, it will show a different view of character.
  11. bgolus


    Dec 7, 2012
    POSITION in the appdata struct is just a vertex position from the mesh, and TEXCOORD0 is just the first UV map for that vertex.

    The vertex shader takes that data and transforms the vertex positions into the screen space position (called clip space or projection space), and passing the texture coordinates on to the fragment shader (optionally transforming those too, in Unity's case often using the scale and offset values you set on the material).

    To move the position behind the camera after:
    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    you can set:
    o.vertex.z = -1;

    Except that won't necessarily work properly in Unity 5.5 on Windows, or in OpenGL. You'll actually need to do:
    o.vertex.z = 1; // note, that's not a negative
    o.vertex.z = -o.vertex.w * 2.0;

    For the rest of it, like determining if it should be shown or not, you'll need to have some decent understanding of vector math and dot products. I suggest reading some tutorials on cat like coding.

    You'll need to compare the camera's forward vector to the surface normal and, depending on the angle hide or show it.

    There's also the task of creating the original mesh with the multiple planes and UVs setup for each of the rotations. And then you'll have to deal with animation somehow.

    edit: Or you can try this one which may or may not use the same technique.
    Last edited: Feb 13, 2017