using System; using System.Collections; using System.Collections.Generic; using General; using UnityEngine; using Random = UnityEngine.Random; namespace Terrain { public class World : MonoBehaviour { public static int ChunkSize = 10; public static int AnimationTimeout = 30; public Material terrainMaterial; public Material bushMaterial; public Material treeMaterial; public Shader noOutline; public Shader outline; public GameObject terrain; public Player player; private TextureManager _terrainTextureManager; private TextureManager _bushTextureManager; private TextureManager _treeTextureManager; private readonly Dictionary _chunks = new Dictionary(); private readonly Dictionary _buildings = new Dictionary(); public bool ready; public void Start() { Debug.Log("Initializing States"); Random.InitState(Settings.Seed); BoxCollider2D col = terrain.AddComponent(); col.size = new Vector2(Settings.WorldSize, Settings.WorldSize) * ChunkSize * 2; col.isTrigger = true; Debug.Log("Creating Texture Managers"); _terrainTextureManager = new TextureManager(terrainMaterial, new Vector2Int(64, 64), this); _bushTextureManager = new TextureManager(bushMaterial, new Vector2Int(32, 32), this); _treeTextureManager = new TextureManager(treeMaterial, new Vector2Int(128, 128), this); StartCoroutine(GenerateWorld()); } private IEnumerator GenerateWorld() { Debug.Log("Generating World"); for (int x = -Settings.WorldSize; x < Settings.WorldSize; x++) { for (int y = Settings.WorldSize; y > -Settings.WorldSize; y--) { _chunks.Add(new Vector2(x, y), new Chunk(new Vector2(x, y), this)); yield return null; } } player.OnWorldReady(); ready = true; Debug.Log("Done"); } public Vector2 GetTile(Vector2 pos) { Vector2 tile = new Vector2( Mathf.FloorToInt(pos.x), Mathf.FloorToInt(pos.y) ); return tile; } public Chunk GetChunk(Vector2 pos) { try { pos = GetTile(pos); Vector2 chunkPos = new Vector2( Mathf.FloorToInt(pos.x / ChunkSize), Mathf.FloorToInt(pos.y / ChunkSize) ); return _chunks[chunkPos]; } catch (Exception) { return null; } } public Block GetBlock(Vector2 pos) { return GetChunk(pos).GetBlock(pos); } public Obstical GetObstical(Vector2 pos) { return GetChunk(pos).GetObstical(pos); } public void AddObstical(Obstical obstical) { obstical.Chunk.AddObstical(obstical); } public void RemoveObstical(Vector2 pos) { GetChunk(pos).RemoveObstical(pos); } public void AddBuilding(GameObject building) { _buildings.Add(building.transform.position, building); } public int GenerateBlock(Vector2 position, Chunk chunk) { Vector2Int pos = new Vector2Int( Mathf.FloorToInt(position.x), Mathf.FloorToInt(position.y) ); float noise = Noise.Get2DNoise(pos, -(Settings.WorldSize * 2), 0.2f) * 10; if (noise >= 8) return Block.Water.Id; if (noise >= 7.7f) return Block.WetSand.Id; if (noise >= 7.5f) return Block.Sand.Id; if (noise < 1.5f) { chunk.AddObstical(new Sapling(pos, chunk)); return Block.Forest.Id; } if (Random.Range(0f, 1f) < 0.002f) chunk.AddObstical(new Bush(pos, chunk, Random.Range(0, 29))); return Block.Grass.Id; } private void Update() { if (!ready) return; foreach (var chunk in _chunks.Values) { chunk.Update(); } } private int _currentAnimationFrame; private int _globalAnimationFrame; private void FixedUpdate() { if (!ready) return; _currentAnimationFrame++; if (_currentAnimationFrame >= AnimationTimeout) { _currentAnimationFrame = 0; foreach (var chunk in _chunks.Values) { chunk.OnAnimationFrameChange(_globalAnimationFrame); } _globalAnimationFrame++; } foreach (var chunk in _chunks.Values) { chunk.FixedUpdate(); } } public TextureManager GetTerrainTextureManager() { return _terrainTextureManager; } public TextureManager GetBushTextureManager() { return _bushTextureManager; } public TextureManager GetTreeTextureManager() { return _treeTextureManager; } } public static class Noise { public static float Get2DNoise(Vector2 pos, float offset, float scale) { return Mathf.PerlinNoise((pos.x + 0.1f) / World.ChunkSize * scale + offset, (pos.y + 0.1f) / World.ChunkSize * scale + offset); } public static bool Get3DNoise(Vector3 pos, float offset, float scale, float threshold) { float x = (pos.x + offset + 0.1f) * scale; float y = (pos.y + offset + 0.1f) * scale; float z = (pos.z + offset + 0.1f) * scale; float AB = Mathf.PerlinNoise(x, y); float BC = Mathf.PerlinNoise(y, z); float AC = Mathf.PerlinNoise(x, z); float BA = Mathf.PerlinNoise(y, x); float CB = Mathf.PerlinNoise(z, y); float CA = Mathf.PerlinNoise(z, x); return ((AB + BC + AC + BA + CB + CA) / 6f > threshold); } } }