我正在创建一个平铺世界,其中平铺由GameObjects组成。但是,我想在这个GameObject中有物理。如果我为每个图块实例化一个GameObject,我得到了framedrops,如果我使用一个参考,每个图块的物理特性是相同的(即一个球落在一个图块上,走得更远,它已经落在下一个图块上)。
是否有针对上述问题的解决方案?
编辑:请参阅我添加了一些内容的最后一部分。
脚本:
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<K, T, V>"/> 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<K, T, V>"/> 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;
答案 0 :(得分:2)
说实话,没有办法规避MeshColliders造成的尖峰。它们非常昂贵,最好使用通用的或编写自己的对撞机。
此外,无论如何,您所做的事情都将花费过多的资源。处理这一切的最好方法是为所有瓷砖使用单个网格,并仅在调用物理时才渲染GameObjects,或者您可以编写自己的物理。
另一种选择是创建一个“块”机制来将瓦片组合在一起。然后,您可以创建某种类型的剔除,加载和卸载块以提高性能。
简而言之,只有Unity的核心库,你才能以经济高效的方式做到这一点。