Unity 用方块做地形运动的一种思路, 取周围方块的平均高度

29 篇文章 3 订阅
13 篇文章 0 订阅

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 一定要勾选上
在这里插入图片描述
合批:
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值