Hello everyone,
With my team we are working on MMORPG game. We are trying to prepare a good solution for RAM optimalization on our big scene.
I’ve heard that Unity comes with LoadLevelAdditiveAsync, but there will be a problem to divide current map to smaller sectors.
We have a script, that was written for Unity 4.0, but now it doesn’t unload meshes from the scene. I don’t know why.
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Threading;
public class RAMController : MonoBehaviour
{
private const float SECTOR_SIZE_X = 50;
private const float SECTOR_SIZE_Y = 50;
private const int LAYERS_NUMER = 18;
public int sectorsToLoad = 0;
public Transform player;
private float sector_size_x, sector_size_y;
private Vector2 oldPlayerPosition;
private Dictionary<Vector2, int> pendingSectorsToLoad;
// first element marks sector location in 3d space, second marks list of objects that are stored there
private Dictionary<Vector2, List<GameObject>>[] sectorsModels;
private Vector2 currentSector = new Vector2();
void Awake()
{
sectorsModels = new Dictionary<Vector2, List<GameObject>>[LAYERS_NUMER];
for (int i = 8; i < LAYERS_NUMER; i++)
{
sectorsModels[i] = new Dictionary<Vector2, List<GameObject>>();
}
oldPlayerPosition = new Vector2();
//Terrain_x = GameObject.Find("Terrain").GetComponent<Terrain>().terrainData.size.x;
//Terrain_z = GameObject.Find("Terrain").GetComponent<Terrain>().terrainData.size.z;
}
// Use this for initialization
void Start()
{
sector_size_x = SECTOR_SIZE_X;
sector_size_y = SECTOR_SIZE_Y;
oldPlayerPosition.x = player.transform.position.x;
oldPlayerPosition.y = player.transform.position.z;
Vector2 positioner;
// Sector - layer
pendingSectorsToLoad = new Dictionary<Vector2, int>();
GameObject[] gobj = GameObject.FindSceneObjectsOfType(typeof(GameObject)) as GameObject[];
StartCoroutine(TurnOff(null, gobj));
int tmpLayer;
// Assigining objects to sectors basing on their coordinates
foreach (GameObject obj in GameObject.FindObjectsOfType(typeof(GameObject)))
{
if (obj.tag == "Terrain")
continue;
positioner = new Vector2();
positioner.x = obj.transform.position.x - obj.transform.position.x % sector_size_x;
positioner.y = obj.transform.position.z - obj.transform.position.z % sector_size_y;
if (obj.layer > 27)
continue;
// Water distance layers go into same bucket as common layers
tmpLayer = obj.layer < 18 ? obj.layer : obj.layer - 10;
// Default layer
if (obj.layer == 0)
tmpLayer = 17;
// Another object in sector
if (sectorsModels[tmpLayer].ContainsKey(positioner))
{
sectorsModels[tmpLayer][positioner].Add(obj);
}
// First object in sector
else
{
List<GameObject> go = new List<GameObject>();
go.Add(obj);
sectorsModels[tmpLayer].Add(positioner, go);
}
}
float sectorIndexX = player.transform.position.x - player.transform.position.x % sector_size_x;
float sectorIndexY = player.transform.position.z - player.transform.position.z % sector_size_y;
ChangeSectors(sectorIndexX, sectorIndexY);
InvokeRepeating("HandlePlayerMovementWrapper", 1f, 5f);
}
private void HandlePlayerMovementWrapper()
{
HandlePlayerMovement(player.transform.position.x, player.transform.position.z);
}
/// <summary>
/// Verifies is player has crossed sector borders
/// </summary>
/// <param name="x">player x position</param>
/// <param name="y">player z position (y on 2D space)</param>
private void HandlePlayerMovement(float x, float y)
{
// Player is still
if (x == oldPlayerPosition.x y == oldPlayerPosition.y)
return;
else
{
// Sectors x,y indexes
float sectorIndexX = x - x % sector_size_x;
float sectorOldIndexX = oldPlayerPosition.x - oldPlayerPosition.x % sector_size_x;
float sectorIndexY = y - y % sector_size_y;
float sectorOldIndexY = oldPlayerPosition.y - oldPlayerPosition.y % sector_size_y;
currentSector.x = sectorIndexX;
currentSector.y = sectorIndexY;
// Player changed sectors
if ((sectorIndexX != sectorOldIndexX)
|| (sectorIndexY != sectorOldIndexY))
{
ChangeSectors(sectorIndexX, sectorIndexY);
}
oldPlayerPosition.x = x;
oldPlayerPosition.y = y;
}
}
/// <summary>
/// Loads sectors, that will be needed and unloads old because player changed sectors
/// </summary>
/// <param name="sectorOldIndexX">X coord of sector where player was</param>
/// <param name="sectorOldIndexY">Y coord of sector where player was</param>
/// <param name="sectorIndexX">X coord of sector where player is</param>
/// <param name="sectorIndexY">Y coord of sector where player is</param>
private void ChangeSectors(float sectorIndexX, float sectorIndexY)
{
Debug.Log("Zmiana sektora " + sectorIndexX.ToString() + " " + sectorIndexY.ToString());
Vector2 localVector;
for (int secX = -sectorsToLoad; secX <= sectorsToLoad; secX++)
{
for (int secY = -sectorsToLoad; secY <= sectorsToLoad; secY++)
{
localVector = new Vector2();
localVector.x = sectorIndexX + sector_size_x * secX;
localVector.y = sectorIndexY + sector_size_y * secY;
// Negative sectors will be loaded and made positive
if (secX <= 1 secX >= -1 secY <= 1 secY >= -1)
{
UpdateSector(8, localVector);
}
else if (secX <= 2 secX >= -2 secY <= 2 secY >= -2)
{
UpdateSector(9, localVector);
}
else if (secX <= 3 secX >= -3 secY <= 3 secY >= -3)
{
UpdateSector(10, localVector);
}
else if (secX <= 4 secX >= -4 secY <= 4 secY >= -4)
{
UpdateSector(11, localVector);
}
else if (secX <= 5 secX >= -5 secY <= 5 secY >= -5)
{
UpdateSector(12, localVector);
}
else if (secX <= 7 secX >= -7 secY <= 7 secY >= -7)
{
UpdateSector(13, localVector);
}
else if (secX <= 10 secX >= -10 secY <= 10 secY >= -10)
{
UpdateSector(14, localVector);
}
else if (secX <= 16 secX >= -16 secY <= 16 secY >= -16)
{
UpdateSector(15, localVector);
}
else if (secX <= 20 secX >= -20 secY <= 20 secY >= -20)
{
UpdateSector(16, localVector);
}
else if (secX <= 26 secX >= -26 secY <= 26 secY >= -26)
{
UpdateSector(17, localVector);
}
else
{
if (pendingSectorsToLoad.ContainsKey(localVector))
pendingSectorsToLoad[localVector] = 0;
}
}
}
}
void UpdateSector(int layerNumber, Vector2 localVector)
{
if (pendingSectorsToLoad.ContainsKey(localVector))
{
if (pendingSectorsToLoad[localVector] > layerNumber || pendingSectorsToLoad[localVector] == 0)
{
pendingSectorsToLoad[localVector] = layerNumber;
if (sectorsModels[layerNumber].ContainsKey(localVector))
{
StartCoroutine(TurnOn(sectorsModels[layerNumber][localVector], null));
}
}
else
{
try
{
StartCoroutine(TurnOff(sectorsModels[layerNumber][localVector], null));
}catch ( System.Exception ex )
{
Debug.Log(ex.Message);
}
}
}
// Initial condition
else
{
pendingSectorsToLoad.Add(localVector, layerNumber);
for (int i = layerNumber; i < LAYERS_NUMER; i++)
{
if (sectorsModels[i].ContainsKey(localVector))
{
StartCoroutine(TurnOn(sectorsModels[i][localVector], null));
}
}
}
}
void OnGUI()
{
GUILayout.Label("Sector: " + currentSector);
}
The problem is, that UpdateSector doesn’t call TurnOff coroutine, but it should call it when user moves to another sector.
Is there any better solution ? Does someone know why this script doesn’t call TurnOff coroutine ?
Regards,
Chris