0

我正在创建一个平铺的世界,其中的平铺由 GameObjects 组成。但是,我想在这个 GameObject 中有物理。如果我为每个图块实例化一个游戏对象,我会得到丢帧,如果我使用引用,每个图块的物理特性都是相同的(即一个球落在一个图块上,再走一点,它已经落在下一个图块上)。

有没有解决上述问题的方法?

编辑:请参阅我添加了一些内容的最后一部分。

脚本:

using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

/// <summary>
/// A double key dictionary.
/// </summary>
/// <typeparam name="K">
/// The first key type.
/// </typeparam>
/// <typeparam name="T">
/// The second key type.
/// </typeparam>
/// <typeparam name="V">
/// The value type.
/// </typeparam>
/// <remarks>
/// See http://noocyte.wordpress.com/2008/02/18/double-key-dictionary/
///   A Remove method was added.
/// </remarks>
public class DoubleKeyDictionary<K, T, V> : IEnumerable<DoubleKeyPairValue<K, T, V>>, 
                                            IEquatable<DoubleKeyDictionary<K, T, V>>
{
    #region Constants and Fields

    /// <summary>
    /// The m_inner dictionary.
    /// </summary>
    private Dictionary<T, V> m_innerDictionary;

    #endregion

    #region Constructors and Destructors

    /// <summary>
    /// Initializes a new instance of the <see cref="DoubleKeyDictionary{K,T,V}"/> class. 
    ///   Initializes a new instance of the <see cref="DoubleKeyDictionary&lt;K, T, V&gt;"/> class.
    /// </summary>
    public DoubleKeyDictionary()
    {
        this.OuterDictionary = new Dictionary<K, Dictionary<T, V>>();
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets or sets OuterDictionary.
    /// </summary>
    private Dictionary<K, Dictionary<T, V>> OuterDictionary { get; set; }

    #endregion

    #region Public Indexers

    /// <summary>
    ///   Gets or sets the value with the specified indices.
    /// </summary>
    /// <value></value>
    public V this[K index1, T index2]
    {
        get
        {
            return this.OuterDictionary[index1][index2];
        }

        set
        {
            this.Add(index1, index2, value);
        }
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Clears this dictionary.
    /// </summary>
    public void Clear()
    {
        OuterDictionary.Clear();
        if (m_innerDictionary!=null)
            m_innerDictionary.Clear();
    }

    /// <summary>
    /// Adds the specified key.
    /// </summary>
    /// <param name="key1">
    /// The key1.
    /// </param>
    /// <param name="key2">
    /// The key2.
    /// </param>
    /// <param name="value">
    /// The value.
    /// </param>
    public void Add(K key1, T key2, V value)
    {
        if (this.OuterDictionary.ContainsKey(key1))
        {
            if (this.m_innerDictionary.ContainsKey(key2))
            {
                this.OuterDictionary[key1][key2] = value;
            }
            else
            {
                this.m_innerDictionary = this.OuterDictionary[key1];
                this.m_innerDictionary.Add(key2, value);
                this.OuterDictionary[key1] = this.m_innerDictionary;
            }
        }
        else
        {
            this.m_innerDictionary = new Dictionary<T, V>();
            this.m_innerDictionary[key2] = value;
            this.OuterDictionary.Add(key1, this.m_innerDictionary);
        }
    }

    /// <summary>
    /// Determines whether the specified dictionary contains the key.
    /// </summary>
    /// <param name="index1">
    /// The index1.
    /// </param>
    /// <param name="index2">
    /// The index2.
    /// </param>
    /// <returns>
    /// <c>true</c> if the specified index1 contains key; otherwise, <c>false</c>.
    /// </returns>
    public bool ContainsKey(K index1, T index2)
    {
        if (!this.OuterDictionary.ContainsKey(index1))
        {
            return false;
        }

        if (!this.OuterDictionary[index1].ContainsKey(index2))
        {
            return false;
        }

        return true;
    }

    /// <summary>
    /// Equalses the specified other.
    /// </summary>
    /// <param name="other">
    /// The other.
    /// </param>
    /// <returns>
    /// The equals.
    /// </returns>
    public bool Equals(DoubleKeyDictionary<K, T, V> other)
    {
        if (this.OuterDictionary.Keys.Count != other.OuterDictionary.Keys.Count)
        {
            return false;
        }

        bool isEqual = true;

        foreach (var innerItems in this.OuterDictionary)
        {
            if (!other.OuterDictionary.ContainsKey(innerItems.Key))
            {
                isEqual = false;
            }

            if (!isEqual)
            {
                break;
            }

            // here we can be sure that the key is in both lists, 
            // but we need to check the contents of the inner dictionary
            Dictionary<T, V> otherInnerDictionary = other.OuterDictionary[innerItems.Key];
            foreach (var innerValue in innerItems.Value)
            {
                if (!otherInnerDictionary.ContainsValue(innerValue.Value))
                {
                    isEqual = false;
                }

                if (!otherInnerDictionary.ContainsKey(innerValue.Key))
                {
                    isEqual = false;
                }
            }

            if (!isEqual)
            {
                break;
            }
        }

        return isEqual;
    }

    /// <summary>
    /// Gets the enumerator.
    /// </summary>
    /// <returns>
    /// </returns>
    public IEnumerator<DoubleKeyPairValue<K, T, V>> GetEnumerator()
    {
        foreach (var outer in this.OuterDictionary)
        {
            foreach (var inner in outer.Value)
            {
                yield return new DoubleKeyPairValue<K, T, V>(outer.Key, inner.Key, inner.Value);
            }
        }
    }

    /// <summary>
    /// Removes the specified key.
    /// </summary>
    /// <param name="key1">
    /// The key1.
    /// </param>
    /// <param name="key2">
    /// The key2.
    /// </param>
    public void Remove(K key1, T key2)
    {
        this.OuterDictionary[key1].Remove(key2);
        if (this.OuterDictionary[key1].Count == 0)
        {
            this.OuterDictionary.Remove(key1);
        }
    }

    #endregion

    #region Explicit Interface Methods

    /// <summary>
    /// Returns an enumerator that iterates through a collection.
    /// </summary>
    /// <returns>
    /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
    /// </returns>
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    #endregion
}

/// <summary>
/// Represents two keys and a value.
/// </summary>
/// <typeparam name="K">
/// First key type.
/// </typeparam>
/// <typeparam name="T">
/// Second key type.
/// </typeparam>
/// <typeparam name="V">
/// Value type.
/// </typeparam>
public class DoubleKeyPairValue<K, T, V>
{
    #region Constructors and Destructors

    /// <summary>
    /// Initializes a new instance of the <see cref="DoubleKeyPairValue{K,T,V}"/> class. 
    /// Initializes a new instance of the <see cref="DoubleKeyPairValue&lt;K, T, V&gt;"/> class.
    /// </summary>
    /// <param name="key1">
    /// The key1.
    /// </param>
    /// <param name="key2">
    /// The key2.
    /// </param>
    /// <param name="value">
    /// The value.
    /// </param>
    public DoubleKeyPairValue(K key1, T key2, V value)
    {
        this.Key1 = key1;
        this.Key2 = key2;
        this.Value = value;
    }

    #endregion

    #region Public Properties

    /// <summary>
    ///   Gets or sets the key1.
    /// </summary>
    /// <value>The key1.</value>
    public K Key1 { get; set; }

    /// <summary>
    ///   Gets or sets the key2.
    /// </summary>
    /// <value>The key2.</value>
    public T Key2 { get; set; }

    /// <summary>
    ///   Gets or sets the value.
    /// </summary>
    /// <value>The value.</value>
    public V Value { get; set; }

    #endregion

    #region Public Methods

    /// <summary>
    /// Returns a <see cref="System.String"/> that represents this instance.
    /// </summary>
    /// <returns>
    /// A <see cref="System.String"/> that represents this instance.
    /// </returns>
    public override string ToString()
    {
        return this.Key1 + " - " + this.Key2 + " - " + this.Value;
    }

    #endregion
}

public class TerrainManager : MonoBehaviour {

    public GameObject playerGameObject;
    public GameObject referenceTerrain;
    public int spread = 1;
    public int rememberSpread = 3;

    private int[] currentTerrainID;
    private DoubleKeyDictionary<int, int, GameObject> terrainUsageData;
    private Vector3 referencePosition;
    private Vector2 referenceSize;
    private Quaternion referenceRotation;


    // Use this for initialization
    void Start () {

        currentTerrainID = new int[2];
        terrainUsageData = new DoubleKeyDictionary<int, int, GameObject>();

        referencePosition = referenceTerrain.transform.position;
        referenceRotation = referenceTerrain.transform.rotation;

        float width = 0;
        float height = 0;
        foreach(Renderer rend in referenceTerrain.GetComponentsInChildren<Renderer>()) {
            if (rend.bounds.size.x > width)
                width = rend.bounds.size.x;
            if (rend.bounds.size.z > height)
                height = rend.bounds.size.z;
        }

        referenceSize = new Vector2(width, height);
        print (referenceSize);
    }

    // Update is called once per frame
    void Update () {
        Vector3 warpPosition = playerGameObject.transform.position;
        TerrainIDFromPosition(ref currentTerrainID, ref warpPosition);

        string dbgString = "";
        dbgString = "CurrentID : " + currentTerrainID[0] + ", " + currentTerrainID[1] + "\n\n";
        for(int i=-spread;i<=spread;i++)
        {
            for(int j=-spread;j<=spread;j++)
            {   
                DropTerrainAt(currentTerrainID[0] + i, currentTerrainID[1] + j);
                dbgString += (currentTerrainID[0] + i) + "," + (currentTerrainID[1] + j) + "\n";
            }
        }
        Debug.Log(dbgString);
        ReclaimTiles();
    }

    void TerrainIDFromPosition(ref int[] currentTerrainID, ref Vector3 position)
    {
        currentTerrainID[0] = Mathf.RoundToInt((position.x - referencePosition.x) / referenceSize.x);
        currentTerrainID[1] = Mathf.RoundToInt((position.z - referencePosition.z) / referenceSize.y);
    }

    void DropTerrainAt(int i, int j)
    {
        if(terrainUsageData.ContainsKey(i,j))
        {
            // Restore the data for this tile
        }
        else
        {
            // Create a new data object
            terrainUsageData[i,j] = CreateNewTerrainData();
        }

        ActivateUsedTile(i, j);
    }

    GameObject CreateNewTerrainData()
    {
        GameObject terr = (GameObject)Instantiate(referenceTerrain);
        terr.SetActive(true);
        return terr;
    }

    void ReclaimTiles()
    {
        foreach(DoubleKeyPairValue<int,int,GameObject> pair in terrainUsageData)
        {
            if (pair.Key1 > currentTerrainID[0] + rememberSpread || pair.Key1 < currentTerrainID[0] - rememberSpread ||
                    pair.Key2 > currentTerrainID[1] + rememberSpread || pair.Key2 < currentTerrainID[1] - rememberSpread) {
                pair.Value.SetActive(false);
                terrainUsageData.Remove(pair.Key1, pair.Key2);
            }
        }
    }

    void ActivateUsedTile(int i, int j)
    {
        terrainUsageData[i,j].transform.position = 
                                    new Vector3(    referencePosition.x + i * referenceSize.x,
                                                    referencePosition.y,
                                                    referencePosition.z + j * referenceSize.y);
        terrainUsageData[i,j].transform.rotation = referenceRotation;
        terrainUsageData[i,j].SetActive(true);
            foreach(Transform ct in terrainUsageData[i,j].transform) {
                if (!ct.gameObject.name.Equals("Ground"))
                    ct.gameObject.rigidbody.isKinematic = false;
            }
    }   
}

还有我的其他更改:

        GameObject terr = new GameObject();
        foreach(Transform go in referenceTerrain.transform) {
            GameObject obj = new GameObject();

            GameObject origobj = go.gameObject;

            obj.AddComponent<MeshRenderer>();
            MeshRenderer mr = obj.GetComponent<MeshRenderer>();
            mr.materials = origobj.renderer.materials;
            mr.receiveShadows = origobj.renderer.receiveShadows;
            mr.castShadows = origobj.renderer.castShadows;

            obj.AddComponent<MeshFilter>();
            MeshFilter mer = obj.GetComponent<MeshFilter>();
            mer.mesh = origobj.GetComponent<MeshFilter>().mesh;

            obj.AddComponent<Rigidbody>();
            Rigidbody origrb = origobj.rigidbody;
            Rigidbody rb = obj.rigidbody;
            rb.isKinematic = origrb.isKinematic;
            rb.useGravity = origrb.useGravity;

            MeshCollider origmc = origobj.GetComponent<MeshCollider>();
            if (origmc) {
                MeshCollider mc = obj.AddComponent<MeshCollider>();
                mc.sharedMesh = origmc.sharedMesh;
                mc.sharedMaterial = origmc.sharedMaterial;
            }
            BoxCollider origbc = origobj.GetComponent<BoxCollider>();
            if (origbc) {
                BoxCollider bc = obj.AddComponent<BoxCollider>();
                bc.sharedMaterial = origbc.sharedMaterial;
            }

            obj.transform.parent = terr.transform;
            obj.transform.localPosition = origobj.transform.localPosition;
            obj.transform.localRotation = origobj.transform.localRotation;
            obj.transform.localScale = origobj.transform.localScale;

            obj.SetActive(true);
        }
        return terr;
4

1 回答 1

2

老实说,没有办法规避由 MeshColliders 引起的尖峰。它们非常昂贵,最好使用通用的或编写自己的对撞机。

此外,无论如何,您正在做的事情将花费过多的资源。处理这一切的最佳方法是为所有图块使用单个网格,并仅在调用物理或您可以编写自己的物理时渲染游戏对象。

另一种选择是创建一个“块”机制来将图块组合在一起。然后你可以创建某种类型的剔除、加载和卸载块来提高性能。

简而言之,仅使用 Unity 的核心库就不太可能以具有成本效益的方式做到这一点。

于 2013-03-24T20:06:33.733 回答