Unity 用10000个方块做地形运动的一种思路, 取周围方块的平均高度
先上图:
上代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Smooth : MonoBehaviour
{
int maxX = 99;
int maxY = 99;
public Dictionary<int, Transform> dic = new Dictionary<int, Transform>();
public Dictionary<int, List<int>> dicRound = new Dictionary<int, List<int>>();
void Awake()
{
CreateAllCubes();
}
void CreateAllCubes()
{
for (int i = 0; i <= maxX; i++)
{
for (int j = 0; j <= maxY; j++)
{
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
go.transform.position = new Vector3(i, 0, j) * 1.2f;
go.transform.localScale = new Vector3(1, 5, 1);
//把碰撞盒删掉, 不然消耗很大
BoxCollider box = go.GetComponent<BoxCollider>();
Destroy(box);
dic.Add(i * 100 + j, go.transform);
}
}
//先把相邻的方块存下来, 省得每次都去算邻居. (空间换时间)
foreach (var item in dic)
{
dicRound.Add(item.Key, GetRound(item.Key));
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
Reset();
}
if (Input.GetKeyDown(KeyCode.Space))
{
RandomHeight();
StopAllCoroutines();
StartCoroutine(DoSmooth());
}
}
void RandomHeight()
{
foreach (var item in dic)
{
item.Value.position = new Vector3(item.Value.position.x, Random.Range(0, 500), item.Value.position.z);
}
}
void Reset()
{
foreach (var item in dic)
{
item.Value.position = new Vector3(item.Value.position.x, 250, item.Value.position.z);
}
}
Dictionary<int, float> tempDicPos = new Dictionary<int, float>();
IEnumerator DoSmooth()
{
while (true)
{
tempDicPos.Clear();
foreach (var item in dic)
{
//List<int> round = new List<int>() { 0 };
List<int> round = dicRound[item.Key];
float y = 0;
for (int i = 0; i < round.Count; i++)
{
y += dic[round[i]].position.y;
}
float posY = y / round.Count;
tempDicPos.Add(item.Key, posY);
}
foreach (var item in dic)
{
item.Value.position = new Vector3(item.Value.position.x, tempDicPos[item.Key], item.Value.position.z);
}
yield return new WaitForSeconds(0.1f);
}
}
List<int> GetRound(int str)
{
int j = str % 100;
int i = (str - j) / 100;
int[] next0 = { i - 1, j };
int[] next1 = { i + 1, j };
int[] next2 = { i, j - 1 };
int[] next3 = { i, j + 1 };
int[] next4 = { i - 1, j - 1 };
int[] next5 = { i - 1, j + 1 };
int[] next6 = { i + 1, j - 1 };
int[] next7 = { i + 1, j + 1 };
int[][] a = { next0, next1, next2, next3, next4, next5, next6, next7 };
List<int> b = new List<int>();
for (int k = 0; k < a.Length; k++)
{
if (a[k][0] < 0)
{
a[k][0] = maxX;
}
else if (a[k][0] > maxX)
{
a[k][0] = 0;
}
if (a[k][1] < 0)
{
a[k][1] = maxY;
}
else if (a[k][1] > maxY)
{
a[k][1] = 0;
}
b.Add(a[k][0] * 100 + a[k][1]);
}
b.Add(str);
return b;
}
}
Unity编辑器里才跑50来帧, 毕竟遍历1万个方块, 就先这样吧, 字段命名都是乱起的名, 有空再改
我回来啦, 改了一版, 稳定70多帧, 甚至还加上了颜色
先上图:
上代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Smooth : MonoBehaviour
{
const int maxX = 100;
const int maxZ = 100;
Transform[,] allCubes = new Transform[maxX, maxZ];
Dictionary<Transform, Transform[]> allNeighbours = new Dictionary<Transform, Transform[]>();
Dictionary<MeshRenderer, MaterialPropertyBlock> allMaterials = new Dictionary<MeshRenderer, MaterialPropertyBlock>();
void Awake()
{
CreateAllCubes();
Reset();
}
void CreateAllCubes()
{
Transform prefab = Resources.Load<Transform>("Prefabs/SmoothCube");
for (int x = 0; x < maxX; x++)
{
for (int z = 0; z < maxZ; z++)
{
Transform tr = Instantiate(prefab);
tr.position = new Vector3(x, 250, z) * 1.2f;
tr.localScale = new Vector3(1, 5, 1);
//把碰撞盒删掉, 不然消耗很大
BoxCollider box = tr.GetComponent<BoxCollider>();
Destroy(box);
allCubes[x, z] = tr;
allMaterials.Add(tr.GetComponent<MeshRenderer>(), new MaterialPropertyBlock());
}
}
for (int x = 0; x < maxX; x++)
{
for (int z = 0; z < maxZ; z++)
{
allNeighbours.Add(allCubes[x, z], GetNeighbours(x, z));
}
}
}
Transform[] GetNeighbours(int myX, int myZ)
{
int lowX = myX - 1 < 0 ? maxX - 1 : myX - 1;
int highX = myX + 1 > maxX - 1 ? 0 : myX + 1;
int lowZ = myZ - 1 < 0 ? maxZ - 1 : myZ - 1;
int highZ = myZ + 1 > maxZ - 1 ? 0 : myZ + 1;
return new Transform[]
{
allCubes[lowX, myZ],
allCubes[highX, myZ],
allCubes[myX, lowZ],
allCubes[myX, highZ],
allCubes[lowX, lowZ],
allCubes[lowX, highZ],
allCubes[highX, lowZ],
allCubes[highX, highZ]
};
}
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
StopAllCoroutines();
Reset();
}
if (Input.GetKeyDown(KeyCode.Space))
{
StopAllCoroutines();
RandomHeight();
StartCoroutine(DoSmooth());
}
}
void Reset()
{
for (int x = 0; x < maxX; x++)
{
for (int z = 0; z < maxZ; z++)
{
Transform tr = allCubes[x, z];
tr.position = new Vector3(tr.position.x, 250, tr.position.z);
}
}
}
void RandomHeight()
{
for (int x = 0; x < maxX; x++)
{
for (int z = 0; z < maxZ; z++)
{
Transform tr = allCubes[x, z];
tr.position = new Vector3(tr.position.x, Random.Range(0, 500), tr.position.z);
}
}
}
Dictionary<Transform, float> targetYDic = new Dictionary<Transform, float>();
WaitForSeconds waitForSeconds = new WaitForSeconds(0.1f);
IEnumerator DoSmooth()
{
while (true)
{
targetYDic.Clear();
foreach (var item in allNeighbours)
{
float y = 0;
int length = item.Value.Length;
for (int i = 0; i < length; i++)
{
y += item.Value[i].position.y;
}
float targetY = y / length;
targetYDic.Add(item.Key, targetY);
}
foreach (Transform tr in allNeighbours.Keys)
{
tr.position = new Vector3(tr.position.x, targetYDic[tr], tr.position.z);
}
RefreshColor();
yield return waitForSeconds;
}
}
void RefreshColor()
{
foreach (var item in allMaterials)
{
item.Value.SetColor("_Color", GetColorByHeight(item.Key.transform.position.y));
item.Key.SetPropertyBlock(item.Value);
}
}
int h1 = 280;
int h2 = 258;
int h3 = 250;
int h4 = 242;
int h5 = 220;
Color color;
Color GetColorByHeight(float height)
{
if (height > h1)
{
color = Color.red;
}
else if (height > h2)
{
color = Color.Lerp(Color.yellow, Color.red, (height - h2) / (h1 - h2));
}
else if (height > h3)
{
color = Color.Lerp(Color.green, Color.yellow, (height - h3) / (h2 - h3));
}
else if (height > h4)
{
color = Color.Lerp(Color.cyan, Color.green, (height - h4) / (h3 - h4));
}
else if (height > h5)
{
color = Color.Lerp(Color.blue, Color.cyan, (height - h5) / (h4 - h5));
}
else
{
color = Color.blue;
}
return color;
}
}
为什么帧数能提高这么多? 主要是用上了GPUInstancing, 合批计算都交给GPU了, 官网文档如下
https://docs.unity3d.com/Manual/GPUInstancing.html
shader也是用官网上的:
Shader "Custom/InstancedColorSurfaceShader" {
Properties{
_Color("Color", Color) = (1, 1, 1, 1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Glossiness("Smoothness", Range(0, 1)) = 0.5
_Metallic("Metallic", Range(0, 1)) = 0.0
}
SubShader{
Tags{ "RenderType" = "Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use Shader model 3.0 target
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)
void surf(Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
↓这个Enable GPU Instancing 一定要勾选上
合批: