最近看到一個非常有趣的益智小游戲,是一個盜賊進入房子偷東西的,
其實這種游戲市面上已經很多了,吸引我的是那個類似手電筒的效果,
主角走到哪裡,光就到哪裡,被擋住的地方還有陰影。有點類似策略游戲裡的戰爭迷霧。
絞盡腦汁想了一天,終於實現類似的效果,這就給大家分享下。
實現這個效果用到的技術:
1.Shader實現攝影機透明穿透效果(光照的實現)
2.代碼裡動態改變mesh(陰影形狀的實現)
廢話不多說,動手。
--------------------------------------------------------------------------------------------
仔細觀察可以看出,沒被光照到的地方是灰色的,照到的地方是彩色的,而且被光照到的地方,
不僅能顯示出被光照到後的效果(彩色地面),還能顯示出一些物品,比如鑰匙,敵人等。
由此可推測,這場景應該是兩個層組成的,一個層在下,是彩色地面以及敵人等要隱藏的物品,
另一個層在上,是沒有被照亮的時候的地圖,然後根據光線進行裁剪,光線能到達的地方,就把
上層該地方裁剪掉,露出底下部分,從而實現“照亮”的效果。
因此我們先按照這種思路搭載場景:
1.題目說的是2D游戲,我這裡弄成3D的,其實都所謂,最終的視覺是俯視的,其實也跟2D一樣,這裡弄成這個角度是方便大家識別我的層次。
Up就是上層,其實什麼都沒有,就只有一張沒被光照的時候的貼圖(我這裡偷懶直接用一片灰色代替了)
Down就是下層,這裡把幾乎所有元素都放滿了,玩家,敵人,牆等
2.接下來將各種類型的對象放到相應的層裡去,方便相機篩選(這裡的層跟上面提到的層不一樣,這裡的層是Unity裡的定義)
這裡我為了方便大家識別,為一種類型的對象設置一個層,實際操作肯定不會這樣繁瑣的操作的,沒必要。
3.創建兩個相機,讓它們分別顯示對於的層。
(先無視Mask,這個待會再講)
如果設置正確,你的顯示應該跟我上圖是一樣的,並且把Up相機的Depth級別設置高點,讓它顯示在Down相機之前。
4.遮擋上層,顯示下層。
就是這麼個原理
具體至於要怎麼遮擋?我一開始想到的是UGUI的Mask組件,後來想想,這個Mask是固定的一張圖片,那麼只能顯示固定的遮擋(雖然可以動態更改,但較麻煩),
後來想,那麼就通過網格遮擋,畢竟網格動態改變形狀很容易,而且可以通過Shader輕易地實現遮擋效果。
具體如下,先寫這麼一個Shader:
Shader "Masked/Mask" { SubShader { // Render the mask after regular geometry, but before masked geometry and // transparent things. Tags {"Queue" = "Geometry+10" } // Don't draw in the RGBA channels; just the depth buffer ColorMask 0 ZWrite On // Do nothing specific in the pass: Pass {} } }
如果你懂Shader那麼一眼就可以看懂它,不懂也無所謂,直接Copy進去。
它其實就是讓你的網格變成一個遮擋物,攝影機一看到這個網格,就會直接穿透過去,直接看到背景。
然後添加一個GameObject,並添加MeshFilter以及MeshRender組件,並且賦予這個Shader,作為Mask。
如果你這個Mask當前的Mesh不為空的話,你可以很直觀地看到它所在的地方都不會被攝影機渲染出來,
直接變成一個穿透的洞:
它不僅是透明的,還是異次元洞,就算它身後的東西也顯示不出來,直接就看到背景了。
竟然它直接穿透到背景,那麼看到看不到底層,那麼我們要它何用呢?
對,它只是將當期攝影機穿透到背景,所以我們將當期攝影機的Clear Flags設置為“Depth Only",這樣第一個攝影機
的背景將顯示第二個攝影機的內容。這樣就可以輕易達到我們想要的穿透。
5.遮擋實現了,那麼該如何實現動態光照變化呢?
我這裡使用的是RayCast,以主角為原點,向四周輻射一定量的射線,這樣可以模擬光照,
然後根據射線探測到的點組合成上面提到的遮擋物的網格。最終就達到我們想要的效果。
using UnityEngine; public class FOVMesh : MonoBehaviour { // 玩家 public GameObject playerGameObject; // 光照半徑 public float range = 3; // 光照質量,數值越低,向四周輻射的射線越多,效果越好,但性能越低 public int levelOfDetails = 1; // 接受光照產生陰影的對象 public LayerMask[] layerMask; private Vector3 direction; private int index = 0; private int triIndex = 0; private int lod = 1; private float width; private GameObject go; private GameObject pGo; private Mesh mesh; private Vector3 worldPos; private Vector3[] verts; private int[] tris; private Vector2[] uvs; private GameObject didHit; private int mask; // Code that runs on entering the state. public void Start () { go = gameObject; pGo = playerGameObject; mesh = new Mesh (); go.GetComponent<MeshFilter> ().mesh = mesh; lod = levelOfDetails; width = range; // 若loa為1,則共發射360條射線作為光線,則有360個頂點加個圓心 verts = new Vector3[(360 / lod) + 1]; // 每兩個頂點跟圓心組成一個三角形,所以三角形的個數為定點數乘3 tris = new int[(360 / lod) * 3]; uvs = new Vector2[verts.Length]; for (int i = 0; i < layerMask.Length; ++i) { mask |= layerMask [i]; } } // Code that runs every frame. public void Update () { index = 0; triIndex = 0; worldPos = pGo.transform.position; verts [index] = worldPos; index++; for (var a=0; a<360; a += lod) { var direction = new Vector3 (Mathf.Sin (Mathf.Deg2Rad * a), 0, Mathf.Cos (Mathf.Deg2Rad * a)); direction = direction * width; RaycastHit hit; if (Physics.Raycast (worldPos, direction, out hit, width, mask)) { // 如果被射線探中,則將探測到的點作為網格的頂點 verts [index] = new Vector3 (hit.point.x, hit.point.y, hit.point.z); } else { // 否則將射線的末端作為網格的頂點 verts [index] = new Vector3 (direction.x + worldPos.x, worldPos.y, direction.z + worldPos.z); } index++; } // 根據網格頂點組合三角形 for (var i=1; i<(360/lod); i++) { tris [triIndex] = 0; tris [triIndex + 1] = i; tris [triIndex + 2] = i + 1; triIndex += 3; } tris [((360 / lod) * 3) - 3] = 0; tris [((360 / lod) * 3) - 2] = 360 / lod; tris [((360 / lod) * 3) - 1] = 1; // 網格貼圖 int j = 0; while (j < uvs.Length) { uvs [j] = new Vector2 (verts [j].x, verts [j].z); j++; } // 重新組合網格 mesh.Clear (); mesh.vertices = verts; mesh.triangles = tris; mesh.uv = uvs; mesh.RecalculateNormals (); } }
最終效果如圖:
源碼:
點我下載,歡迎轉載,轉載請注明出處
好吧,就在我寫完這個帖子後的一個小時,我百度了下2D偽陰影。。。然後發現了一個神物。。。
原來早就有類似的插件了,而且效果很強大,實現方法跟我這個類似,但那個還能實現光影的強弱,
遠處較暗近處較亮。
http://www.narkii.com/club/thread-358505-1.html
還有一個很屌的外國人寫的:
http://blog.jobbole.com/89193/