private void LayoutLocation(ref DFLocation location)
        {
            // Get city dimensions
            int width  = location.Exterior.ExteriorData.Width;
            int height = location.Exterior.ExteriorData.Height;

            // Import blocks
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    string     blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y));
                    GameObject go        = RMBLayout.CreateGameObject(blockName);
                    go.transform.parent   = this.transform;
                    go.transform.position = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                }
            }

            // Enumerate start marker game objects
            EnumerateStartMarkers();
        }
예제 #2
0
        // Update terrain data
        // Spreads loading across several frames to reduce gameplay stalls
        // This can also be done using true multi-threading, but at much greater
        // complexity for only minor visible gains.
        // Only yields after initial init complete
        private IEnumerator UpdateTerrains()
        {
            // First stage updates terrain heightmaps
            for (int i = 0; i < terrainArray.Length; i++)
            {
                if (terrainArray[i].active && terrainArray[i].updateHeights)
                {
                    UpdateTerrainHeights(terrainArray[i]);
                    terrainArray[i].updateHeights = false;
                    if (!init)
                    {
                        yield return(new WaitForEndOfFrame());
                    }
                }
            }

            // Wait for physics update when streaming
            if (!init)
            {
                yield return(new WaitForFixedUpdate());
            }

            // Second stage updates terrain nature
            for (int i = 0; i < terrainArray.Length; i++)
            {
                if (terrainArray[i].active && terrainArray[i].updateNature)
                {
                    UpdateTerrainNature(terrainArray[i]);
                    terrainArray[i].updateNature = false;
                    if (!init)
                    {
                        yield return(new WaitForEndOfFrame());
                    }
                }
            }

            // Get key for central player terrain
            int playerKey = TerrainHelper.MakeTerrainKey(MapPixelX, MapPixelY);

            // Third stage updates location if present
            // Vast majority of terrains will not have a location
            // Locations are not optimised as yet and are quite heavy on drawcalls
            for (int i = 0; i < terrainArray.Length; i++)
            {
                // Get key for this terrain
                int key = TerrainHelper.MakeTerrainKey(terrainArray[i].mapPixelX, terrainArray[i].mapPixelY);

                if (terrainArray[i].active && terrainArray[i].hasLocation)
                {
                    // Create location if not present
                    if (!locationDict.ContainsKey(key))
                    {
                        // Create location object
                        DFLocation location;
                        GameObject locationObject = CreateLocationGameObject(i, out location);
                        if (!locationObject)
                        {
                            continue;
                        }

                        // Add location object to dictionary
                        locationDict.Add(key, locationObject);

                        // Create location beacon
                        // This is parented to location and shares its lifetime
                        if (AddLocationBeacon)
                        {
                            const float beaconHeight   = 900f;
                            const float beaconOffset   = (MapsFile.WorldMapTerrainDim * MeshReader.GlobalScale) / 2f;
                            GameObject  locationMarker = (GameObject)GameObject.Instantiate(Resources.Load <GameObject>("LocationBeacon"));
                            locationMarker.hideFlags               = HideFlags.HideAndDontSave;
                            locationMarker.transform.parent        = locationObject.transform;
                            locationMarker.transform.localPosition = new Vector3(beaconOffset, beaconHeight, beaconOffset);
                        }

                        // Add one nature batch for entire location
                        // This is parented to location and shares its lifetime
                        GameObject natureBatchObject = new GameObject("NatureBatch");
                        natureBatchObject.hideFlags               = HideFlags.HideAndDontSave;
                        natureBatchObject.transform.parent        = locationObject.transform;
                        natureBatchObject.transform.localPosition = Vector3.zero;
                        DaggerfallBillboardBatch natureBatch = natureBatchObject.AddComponent <DaggerfallBillboardBatch>();
                        int natureArchive = ClimateSwaps.GetNatureArchive(LocalPlayerGPS.ClimateSettings.NatureSet, dfUnity.WorldTime.Now.SeasonValue);
                        natureBatch.SetMaterial(natureArchive);

                        // RMB blocks are laid out in centre of terrain to align with ground
                        int     width   = location.Exterior.ExteriorData.Width;
                        int     height  = location.Exterior.ExteriorData.Height;
                        float   offsetX = ((8 * RMBLayout.RMBSide) - (width * RMBLayout.RMBSide)) / 2;
                        float   offsetZ = ((8 * RMBLayout.RMBSide) - (height * RMBLayout.RMBSide)) / 2;
                        Vector3 origin  = new Vector3(offsetX, 2.0f * MeshReader.GlobalScale, offsetZ);

                        // Perform layout and yield after each block is placed
                        DaggerfallLocation dfLocation = locationObject.GetComponent <DaggerfallLocation>();
                        for (int y = 0; y < height; y++)
                        {
                            for (int x = 0; x < width; x++)
                            {
                                // Set origin for billboard batch add
                                // This causes next additions to be offset by this position
                                Vector3 blockOrigin = origin + new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                                natureBatch.origin = blockOrigin;

                                // Add block and yield
                                string     blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y));
                                GameObject go        = RMBLayout.CreateGameObject(blockName, true, natureBatch);
                                go.hideFlags               = HideFlags.HideAndDontSave;
                                go.transform.parent        = locationObject.transform;
                                go.transform.localPosition = blockOrigin;
                                dfLocation.ApplyClimateSettings();
                                if (!init)
                                {
                                    yield return(new WaitForEndOfFrame());
                                }
                            }
                        }

                        // If this is the player terrain we may need to reposition player
                        if (playerKey == key && repositionPlayer)
                        {
                            // Position to location and use start marker for large cities
                            bool useStartMarker = (dfLocation.Summary.LocationType == DFRegion.LocationTypes.TownCity);
                            PositionPlayerToLocation(MapPixelX, MapPixelY, dfLocation, origin, width, height, useStartMarker);
                            repositionPlayer = false;
                        }

                        // Apply nature batch
                        natureBatch.Apply();
                    }
                }
                else if (terrainArray[i].active)
                {
                    if (playerKey == key && repositionPlayer)
                    {
                        PositionPlayerToTerrain(MapPixelX, MapPixelY, Vector3.zero);
                        repositionPlayer = false;
                    }
                }
            }

            // If this is an init we can use the load time to unload unused assets
            // Keeps memory usage much lower over time
            if (init)
            {
                Resources.UnloadUnusedAssets();
            }

            // Finish by collecting stale data and setting neighbours
            CollectTerrains();
            CollectLocations();
            UpdateNeighbours();
        }