我正在创建一个无限的地形生成器,我需要在玩家四处移动时不断更新地形。
一切都很好,但是我很难找到有关快速创建和渲染Sprite的最快方法的信息。
有关精灵的信息:
我正在使用一张精灵表,其中包含我的地形所需的所有帧。草,沙,水等全部包含在一个单个的.png文件中。所有帧都存储在一个数组中,我可以从中轻松抓取它们。
当前需要正确显示我的精灵对象的步骤:
生成的精灵存储在名为GameObject
的{{1}}数组中。这是我目前生成子画面的方式。
chunk
我不知道,每次想在代码中创建一个新的精灵时,都要添加组件并进行缩放似乎是多余和不必要的,而且我相信有更好的方法可以做到这一点。
总结:
我需要最好的(最快的)方式来生成大量精灵,设置其帧,位置和适当的比例。
答案 0 :(得分:2)
答案 1 :(得分:0)
很抱歉,我花了一些时间才能到达...我最终创建了一个项目,在其中生成了一个开放的世界,然后我使用Perlin Noise根据玩家所在的位置生成了地图的各个部分(因为我没有有实际地形)
我为此使用了对象池,因此我有一个世界管理器,它知道世界是什么样子(或者在我的情况下,它使用Perlin Noise),该世界管理器可以合并图块。
注意:这是使用对象池的一种方法。
所以基本上它看起来像这样:
public class WorldManager : MonoBehaviour {
// Being cheap here for the time, if you use this route make sure to use a proper singleton pattern
static public WorldManager instance;
[SerializeField] // Created a prefab for all my tile objects
private TileObject tilePrefab;
[SerializeField]
private int StartingTilePool = 300;
[SerializeField] // In my case this list stored 1 sprite, and I just changed that sprite color depending on the value of perlin noise
List<Sprite> terrainSprites;
private Queue<TileObject> objectPool = new Queue<TileObject>();
void Start() {
instance = this; // Again use a proper singleton pattern in your project.
GenerateTilePool();
LoadFirstSetOfTiles();
}
private void LoadFirstSetOfTiles()
{
// my player always starts at 0,0..
for(int x = -SpawnTileBoundry.HorizontalExtents; x <= SpawnTileBoundry.HorizontalExtents; ++x)
{
for(int y = -SpawnTileBoundry.VerticalExtents; y <= SpawnTileBoundry.VerticalExtents; ++y)
{
SetActiveTile(x,y);
}
}
}
private void GenerateTilePool()
{
for(int i =0; i < tilesToGenerate; ++i)
{
TileObject tempTile = Instantiate(tilePrefab);
EnqueTile(tempTile);
}
}
public void EnqueTile(TileObject tile)
{
objectPool.Enqueue(tile);
tile.gameObject.SetActive(false);
}
public void SetActiveTile(int x, int y)
{
TileObject newTile = null;
if(objectPool.count > 0)
{
newTile = objectPool.Dequeue();
}
else
{
// We didn't have enough tiles store in our pool so lets make a new 1.
newTile = Instantiate(tilePrefab);
}
newTile.transform.position = new Vector3(x,y,1); // Used 1 to put it behind my player...
newTile.gameObject.SetActive(true);
// The sprite here would be based off of your world data, where mine is only a white box, I use the second parameters to give it a gray-scaled color.
newTile.UpdateSprite(terrainSprites[0], Mathf.PerlinNoise(x/10.0f, y / 10.0f));
}
}
那只是我的WorldManager为我处理ObjectPooling ... 这是我的TileObject
[RequireComponent(typeof(SpriteRenderer))]
public class TileObject : MonoBehaviour {
private SpriteRenderer myRenderer;
private void Awake() {
myRenderer = getComponent<SpriteRenderer>();
}
void Update()
{
if(Mathf.Abs(transform.position.x - Camera.main.transform.position.x) > SpawnTileBoundry.HorizontalExtents || Mathf.Abs(transform.position.y - Camera.main.transform.position.y) > SpawnTileBoundry.VerticalExtents)
{
// With this check the tile knows it is no longer visible,
// I could have used OnBecameInvisible to handle this
// but I added a threshold to the extents, that prevent it so
// players would see tiles "appearing" this caused a nice bug, where
// if you moved just right a row/col of tiles wouldn't spawn.
WorldManager.instance.EnqueTile(this);
}
}
public void UpdateSprite(Sprite sprite)
{
myRenderer.sprite = sprite;
}
public void UpdateSprite(Sprite sprite, float grayColor)
{
UpdateSprite(sprite);
myRenderer.color = new Color(grayColor,grayColor,grayColor,1f);
}
}
这是我的SpawnTileBoundry脚本:
public class WorldManager : MonoBehaviour {
private int lastX = 0;
private int lastY = 0;
static public int HorizontalExtents;
static public int VerticalExtents;
void Start() {
VerticalExtents = (int)Camera.main.orthograpicSize + 2; // +2 is just my threshold
HorizontalExtents = (int)(VerticalExtents * Screen.width/Screen.height) +3; // +3 is just my threshold you can change these to what you want.
lastX = (int)transform.position.x;
lastY = (int)transform.position.y;
}
void Update() {
int newX = (int)transform.position.x;
int newY = (int)transform.position.y;
HandleNewTileSpawn(lastX - newX, lastY - newY);
}
// This will tell the WorldManager which tiles need to appear
// We are no longer creating new tiles unless we absolutely have to.
// We are also only making new tiles appear in the circumstance that
// we are about to see them.
void HandleNewTileSpawn(int x, int y)
{
if(x != 0)
{
// This code could be refactor to a method so it was less error prone for changes or tweaks...
if(x < 0)
{
for(int i = lastY - VerticalExtents; i < lastY + VerticalExtents; ++i)
{
WorldManager.instance.SetActiveTile(lastX + HorizontalExtents, i);
}
}
else
{
for(int i = lastY - VerticalExtents; i < lastY + VerticalExtents; ++i)
{
WorldManager.instance.SetActiveTile(lastX - HorizontalExtents, i);
}
}
lastX = (int)transform.position.x;
}
if(y != 0)
{
if(lastY < 0)
{
for(int i = lastX - HorizontalExtents; i < lastX + HorizontalExtents; ++i)
{
WorldManager.instance.SetActiveTile(i, lastY + VeritcalExtents);
}
}
else
{
for(int i = lastX - HorizontalExtents; i < lastX + HorizontalExtents; ++i)
{
WorldManager.instance.SetActiveTile(i, lastY - VeritcalExtents);
}
}
lastY = (int)transform.position.y;
}
}
}
我正在使用WorldManager来处理对象池,是的,我仍然在开始时实例化了许多sprite,但是由于没有必要继续生成新对象,因此我最终停止了生成。
Unity在对象池方面没有很多文档,但是他们有一个视频教程,介绍了对象池的一些基本知识:https://unity3d.com/learn/tutorials/topics/scripting/object-pooling