unity如何實現戰爭迷霧?思路一
斷更很久了,剛好今天放假,寫一個unity下實現戰爭迷霧的思路.
效果如下:
思路如下:
建一個plane作為地面,用UI中的rawimage放在最上層作為迷霧顯示,用cube作為主角移動.cube每次移動,將它的坐標轉化為rawimage上相對位置的點,然后用texture2d的setpixel方法逐像素清除出一個區域出來.
左下角rawimage顯示小地圖,有單獨一個camera專門將迷霧繪制到小地圖中.
具體實現:
1.新建plane,cube,分別作為地面和主角,並把場景中的Main Camera作為cube的子物體.如下設置.
在project窗口創建一個material並賦給cube方便改顏色.
2.新建一個canvas和一個rawimage,並按如下設置.
3.新建一個gameobject,在它的下面,新建一個camera,新建一個canvas並在它的下面新建image,rawimage.設置如下.
在project窗口創建一個rendertexture並賦給camera和rawimage.
image用於小地圖的背景框,也可忽略.
3.新建一個gameobject,並加上腳本.
4.首先,先將迷霧初始化出來.打開編輯FogOfWar腳本.定義fogRawImage,是worldcanvas下的rawimage.定義fogDensity ,迷霧的像素密度,由多少個點組成.定義fogTexture,賦給fogRawImage后,后續的像素顏色更改,只需對fogTexture進行.InitializeTheFog方法將迷霧初始化為黑色.
1 public RawImage fogRawImage; 2 3 public Vector2Int fogDensity = new Vector2Int(100, 100); 4 5 private Texture2D fogTexture; 6 7 void Start() 8 { 9 fogTexture = new Texture2D(fogDensity.x, fogDensity.y); 10 11 fogRawImage.texture = fogTexture; 12 13 InitializeTheFog(); 14 } 15 16 void InitializeTheFog() 17 { 18 int pixelCount = fogDensity.x * fogDensity.y; 19 //將迷霧的默認顏色設置為黑色 20 Color[] blackColors = new Color[pixelCount]; 21 for (int i = 0; i < pixelCount; i++) 22 { 23 blackColors[i] = Color.black; 24 } 25 fogTexture.SetPixels(blackColors); 26 27 fogTexture.Apply(); 28 }
5.接着,設置要消除的形狀的坐標數組.定義beEliminatedShapeSize ,要消除的形狀的長和寬.定義shapeLocalPosition,這個形狀的點分布數組,假設中心為(0,0),后續只需要將要消除的點的坐標跟這個數組所有值相加,即可得到該點出現的形狀的所有坐標.InitializeTheShape方法是初始化視野的形狀,此處是矩形,如果想將視野做成橢圓形,三角形,六角形,修改InitializeTheShape中的寫法然后賦給shapeLocalPosition.
1 public Vector2Int beEliminatedShapeSize = new Vector2Int(8, 6); 2 private Vector2Int[] shapeLocalPosition; 3 void Start() 4 { 5 //... 6 7 InitializeTheShape(); 8 } 9 10 void InitializeTheShape() 11 { 12 int pixelCount = beEliminatedShapeSize.x * beEliminatedShapeSize.y; 13 shapeLocalPosition = new Vector2Int[pixelCount]; 14 15 int halfX = Mathf.FloorToInt(beEliminatedShapeSize.x * 0.5f); 16 int remainingX = beEliminatedShapeSize.x - halfX; 17 int halfY = Mathf.FloorToInt(beEliminatedShapeSize.y * 0.5f); 18 int remainingY = beEliminatedShapeSize.y - halfY; 19 20 int index = 0; 21 for (int y = -halfY; y < remainingY; y++) 22 { 23 for (int x = -halfX; x < remainingX; x++) 24 { 25 shapeLocalPosition[index] = new Vector2Int(x, y); 26 index++; 27 } 28 } 29 }
6.接着是在texture中消除出一個形狀來.這里需要注意的是,cube是在世界坐標中,通過消除rawimage中的像素透明度來實現獲得視野,所以需要將cube的坐標轉換為rawimage上(texture2d上)相對應的點.因為texture是左下角為原點,向右為正x,向上為正y,如下圖.
所以,一種思路就是將cube的世界最左下的一點作為它的原點,然后根據cube的位置和這個原點的位置偏移量,換算成texture中相對應的點,也就是plane的左下角的坐標.下圖,是從上往下看的視角.
定義cubeTransform,cube的transform.定義planeMeshCollider,用於獲取plane的尺寸.定義planeOriginPoint,儲存世界坐標中cube的假定原點.定義worldSize,地面的尺寸,也就是plane的尺寸.EliminateFog方法,獲得當前cube位置的視野.
1 public Transform cubeTransform; 2 public MeshCollider planeMeshCollider; 3 4 private Vector2 planeOriginPoint; 5 private Vector2 worldSize; 6 7 void Start() 8 { 9 //... 10 11 worldSize = new Vector2(planeMeshCollider.bounds.size.x, planeMeshCollider.bounds.size.z); 12 //將plane的坐標減去它尺寸的一半,即可得到它的左下角的坐標 13 planeOriginPoint = new Vector2(planeMeshCollider.transform.position.x - worldSize.x * 0.5f, planeMeshCollider.transform.position.z - worldSize.y * 0.5f); 14 15 InitializeTheShape(); 16 InitializeTheFog(); 17 18 EliminateFog(); 19 } 20 21 void EliminateFog() 22 { 23 Vector2 cubePos = new Vector2(cubeTransform.position.x, cubeTransform.position.z); 24 //相對假定原點的距離比例,因為是世界坐標,兩個點相減有可能是負數,texture中不存在負數的坐標,所以轉化為正數. 25 Vector2 originDistanceRatio = (cubePos - planeOriginPoint) / worldSize; 26 originDistanceRatio.Set(Mathf.Abs(originDistanceRatio.x), Mathf.Abs(originDistanceRatio.y)); 27 //距離比例乘以密度,即可知道cube相當在texture中的點即可計算出來 28 Vector2Int fogCenter = new Vector2Int(Mathf.RoundToInt(originDistanceRatio.x * fogDensity.x), Mathf.RoundToInt(originDistanceRatio.y * fogDensity.y)); 29 for (int i = 0; i < shapeLocalPosition.Length; i++) 30 { 31 int x = shapeLocalPosition[i].x + fogCenter.x; 32 int y = shapeLocalPosition[i].y + fogCenter.y; 33 //因為消除迷霧的形狀是比cube的位置還要大的,在最邊緣的時候,消除的像素點的坐標會超出texture范圍,所以超出部分忽略. 34 if (x < 0 || x >= fogDensity.x || y < 0 || y >= fogDensity.y) 35 continue; 36 37 fogTexture.SetPixel(x, y, Color.clear); 38 } 39 40 fogTexture.Apply(); 41 }
7.設置cube的移動控制,並在每次移動消除迷霧.這里簡單寫一個.
1 public float cubeMoveSpeed = 0.1f; 2 private void Update() 3 { 4 if (Input.anyKey) 5 { 6 if (Input.GetKey(KeyCode.W)) 7 { 8 cubeTransform.position += Vector3.forward * cubeMoveSpeed; 9 } 10 else if (Input.GetKey(KeyCode.S)) 11 { 12 cubeTransform.position += Vector3.back * cubeMoveSpeed; 13 } 14 else if (Input.GetKey(KeyCode.A)) 15 { 16 cubeTransform.position += Vector3.left * cubeMoveSpeed; 17 } 18 else if (Input.GetKey(KeyCode.D)) 19 { 20 cubeTransform.position += Vector3.right * cubeMoveSpeed; 21 } 22 23 EliminateFog(); 24 } 25 }
8.完成后的場景結構和FogOfQar腳本設置如下.
9.完整代碼如下:
1 using UnityEngine; 2 using UnityEngine.UI; 3 4 public class FogOfWar : MonoBehaviour 5 { 6 public RawImage fogRawImage; 7 public MeshCollider planeMeshCollider; 8 public Transform cubeTransform; 9 10 public float cubeMoveSpeed = 0.1f; 11 12 public Vector2Int fogDensity = new Vector2Int(100, 100); 13 public Vector2Int beEliminatedShapeSize = new Vector2Int(8, 6); 14 15 private Texture2D fogTexture; 16 17 private Vector2Int[] shapeLocalPosition; 18 19 private Vector2 planeOriginPoint; 20 private Vector2 worldSize; 21 22 // Start is called before the first frame update 23 void Start() 24 { 25 fogTexture = new Texture2D(fogDensity.x, fogDensity.y); 26 fogRawImage.texture = fogTexture; 27 28 worldSize = new Vector2(planeMeshCollider.bounds.size.x, planeMeshCollider.bounds.size.z); 29 //將plane的坐標減去它尺寸的一半,即可得到它的左下角的坐標 30 planeOriginPoint = new Vector2(planeMeshCollider.transform.position.x - worldSize.x * 0.5f, planeMeshCollider.transform.position.z - worldSize.y * 0.5f); 31 32 InitializeTheShape(); 33 InitializeTheFog(); 34 35 EliminateFog(); 36 } 37 38 private void Update() 39 { 40 if (Input.anyKey) 41 { 42 if (Input.GetKey(KeyCode.W)) 43 { 44 cubeTransform.position += Vector3.forward * cubeMoveSpeed; 45 } 46 else if (Input.GetKey(KeyCode.S)) 47 { 48 cubeTransform.position += Vector3.back * cubeMoveSpeed; 49 } 50 else if (Input.GetKey(KeyCode.A)) 51 { 52 cubeTransform.position += Vector3.left * cubeMoveSpeed; 53 } 54 else if (Input.GetKey(KeyCode.D)) 55 { 56 cubeTransform.position += Vector3.right * cubeMoveSpeed; 57 } 58 59 EliminateFog(); 60 } 61 } 62 63 void InitializeTheShape() 64 { 65 int pixelCount = beEliminatedShapeSize.x * beEliminatedShapeSize.y; 66 shapeLocalPosition = new Vector2Int[pixelCount]; 67 68 int halfX = Mathf.FloorToInt(beEliminatedShapeSize.x * 0.5f); 69 int remainingX = beEliminatedShapeSize.x - halfX; 70 int halfY = Mathf.FloorToInt(beEliminatedShapeSize.y * 0.5f); 71 int remainingY = beEliminatedShapeSize.y - halfY; 72 73 int index = 0; 74 for (int y = -halfY; y < remainingY; y++) 75 { 76 for (int x = -halfX; x < remainingX; x++) 77 { 78 shapeLocalPosition[index] = new Vector2Int(x, y); 79 index++; 80 } 81 } 82 } 83 84 void InitializeTheFog() 85 { 86 int pixelCount = fogDensity.x * fogDensity.y; 87 //將迷霧的默認顏色設置為黑色 88 Color[] blackColors = new Color[pixelCount]; 89 for (int i = 0; i < pixelCount; i++) 90 { 91 blackColors[i] = Color.black; 92 } 93 fogTexture.SetPixels(blackColors); 94 95 fogTexture.Apply(); 96 } 97 98 void EliminateFog() 99 { 100 Vector2 cubePos = new Vector2(cubeTransform.position.x, cubeTransform.position.z); 101 //相對假定原點的距離比例,因為是世界坐標,兩個點相減有可能是負數,texture中不存在負數的坐標,所以轉化為正數. 102 Vector2 originDistanceRatio = (cubePos - planeOriginPoint) / worldSize; 103 originDistanceRatio.Set(Mathf.Abs(originDistanceRatio.x), Mathf.Abs(originDistanceRatio.y)); 104 //距離比例乘以密度,即可知道cube相當在texture中的點即可計算出來 105 Vector2Int fogCenter = new Vector2Int(Mathf.RoundToInt(originDistanceRatio.x * fogDensity.x), Mathf.RoundToInt(originDistanceRatio.y * fogDensity.y)); 106 for (int i = 0; i < shapeLocalPosition.Length; i++) 107 { 108 int x = shapeLocalPosition[i].x + fogCenter.x; 109 int y = shapeLocalPosition[i].y + fogCenter.y; 110 //因為消除迷霧的形狀是比cube的位置還要大的,在最邊緣的時候,消除的像素點的坐標會超出texture范圍,所以超出部分忽略. 111 if (x < 0 || x >= fogDensity.x || y < 0 || y >= fogDensity.y) 112 continue; 113 114 fogTexture.SetPixel(x, y, Color.clear); 115 } 116 117 fogTexture.Apply(); 118 } 119 }
歡迎交流.
轉載注明出處.