/// <summary> /// Calculates information for each kingdom, such as its personality /// TODO - add kingdom 'types' such as dessert, horse riders, idk what else /// </summary> public void GenerateKingdoms() { foreach (Kingdom k in Kingdoms) { k.SetAggression(GenRan.Random()); } }
private void GenerateRiver(Vec2i start) { Vec2i end = null; for (int i = 1; i < 100; i++) { if (end != null) { break; } for (int j = 0; j < DIRS.Length; j++) { Vec2i pos = start + DIRS[j] * i; if (GameGenerator2.InBounds(pos)) { if (GameGen.TerGen.ChunkBases[pos.x, pos.z].Biome == ChunkBiome.ocean) { end = pos; break; } } } } if (end == null) { return; } LineI li = new LineI(start, end); List <Vec2i> basic = li.ConnectPoints(); float val1 = GenRan.Random(0.1f, 0.001f); float val2 = GenRan.Random(0.1f, 0.001f); int val3 = GenRan.RandomInt(8, 32); int val4 = GenRan.RandomInt(8, 32); for (int i = 0; i < basic.Count; i++) { basic[i] += new Vec2i((int)(val3 * Mathf.Sin(i * val1)), (int)(val4 * Mathf.Sin(i * val2))); } for (int i = 0; i < basic.Count - 1; i++) { Vec2i v1 = basic[i]; Vec2i v2 = basic[i + 1]; LineI i2 = new LineI(v1, v2); foreach (Vec2i v in i2.ConnectPoints()) { GameGen.TerGen.ChunkBases[v.x, v.z].SetChunkFeature(new ChunkRiverNode(v)); } } return; }
/// <summary> /// We override the create world object function for a tree. This is done as we need to load in multiple /// parts of the tree /// </summary> /// <param name="transform"></param> /// <returns></returns> public override WorldObject CreateWorldObject(Transform transform = null) { //Create random with seed unique to tree position - this ensures tree should be same every time. GenerationRandom genRan = new GenerationRandom(WorldPosition.x * World.WorldSize * World.ChunkSize + WorldPosition.z); int canopy = genRan.RandomInt(3, 7); float angleOffset = genRan.Random(0, Mathf.PI); float range = Mathf.Sqrt(canopy * 0.5f) * 0.8f; WorldObject treeBase = WorldObject.CreateWorldObject(this, transform); Vec2i zero = new Vec2i(0, 0); Vector3[] canopyPositions = new Vector3[canopy]; for (int i = 0; i < canopy; i++) { //Define the positions of each canopy as roughly circular about the middle of the tree. float angle = (Mathf.PI * 2 / (canopy)) * i + angleOffset; Vector2 delta = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * range + genRan.RandomVector2(-0.1f, 0.1f); float height = 1.8f + genRan.Random(0, 1f); //Store the total position for use later, then generate the canopy itself. canopyPositions[i] = new Vector3(delta.x, height, delta.y); TreeCanopy tr = new TreeCanopy(zero, canopyPositions[i]); tr.SetRandom(genRan); tr.CreateWorldObject(treeBase.transform); } //Generate the trunk float trunkHeight = 1.2f; float trunkScale = 1.2f; TreeBranch trunk = new TreeBranch(zero); trunk.SetCharacteristic(Vector3.zero, new Vector3(0, 0, 0), new Vector3(1, trunkScale, 1)); WorldObject trunkObj = trunk.CreateWorldObject(treeBase.transform); //MeshCollider mc =trunkObj.gameObject.AddComponent<MeshCollider>(); //mc.sharedMesh = trunkObj.gameObject.GetComponent<MeshFilter>().mesh; Vector3 trunkTop = Vector3.up * trunkHeight * trunkScale; //Scale to correct height foreach (Vector3 v in canopyPositions) { TreeBranch canopyBranch = new TreeBranch(zero); //Calculate the Euler angles from the branch base to the canopy Vector3 delta_pos = v - trunkTop; Quaternion.FromToRotation(Vector3.up, delta_pos); //Vector3 rot = Quaternion.FromToRotation(trunkTop, v*5f).eulerAngles; Vector3 rot = Quaternion.FromToRotation(Vector3.up, delta_pos).eulerAngles; float scale = Vector3.Distance(v, trunkTop) / trunkHeight; canopyBranch.SetCharacteristic(trunkTop, rot, new Vector3(1 / scale, scale, 1 / scale)); canopyBranch.CreateWorldObject(treeBase.transform); } return(treeBase); }
public override WorldObject CreateWorldObject(Transform transform = null) { GenerationRandom genRan = new GenerationRandom(WorldPosition.GetHashCode() * 13); int count = genRan.RandomInt(2, 5); WorldObject r1 = null; for (int i = 0; i < count; i++) { Vector3 pos = genRan.RandomVector3(-MaxSize, MaxSize, (i) * 0.1f, (i) * 0.2f + 0.1f, -MaxSize, MaxSize) / 100f; float rSize = genRan.Random(0.5f, 1.5f); Rock r = new Rock(WorldPosition, pos, rSize); if (i == 0) { r1 = r.CreateWorldObject(transform); } else { WorldObject rn = r.CreateWorldObject(r1.transform); rn.transform.localPosition = pos; } } return(r1); }
/// <summary> /// Generates a chunk that has no special land features /// </summary> /// <param name="x"></param> /// <param name="z"></param> /// <param name="cb"></param> /// <returns></returns> private ChunkData GenerateSimpleChunk(int x, int z, ChunkBase2 cb) { if (cb.Biome == ChunkBiome.ocean) { return(new ChunkData(x, z, (int[, ])OCEAN.Clone(), false)); }/* * if(cb.Biome == ChunkBiome.dessert) * { * return new ChunkData(x, z, (int[,])EMPTY_DESERT.Clone(), cb.IsLand, 1, (float[,])OCEAN_HEIGHT.Clone()); * }if(cb.Biome == ChunkBiome.mountain) * { * return new ChunkData(x, z, (int[,])MOUNTAIN.Clone(), cb.IsLand, 1, (float[,])OCEAN_HEIGHT.Clone()); * * }*/ int[,] tiles = (int[, ])(cb.Biome == ChunkBiome.dessert ? EMPTY_DESERT.Clone() : cb.Biome == ChunkBiome.mountain ? MOUNTAIN.Clone() : EMPTY_PLAINS.Clone()); GenerationRandom genRan = new GenerationRandom(Seed + x << 16 + z); //if(genRan.Random() > 0.1f) // return new ChunkData(x, z, tiles, cb.IsLand); Dictionary <int, WorldObjectData> obs = new Dictionary <int, WorldObjectData>(256); float[,] heights = new float[World.ChunkSize, World.ChunkSize]; for (int i = 0; i < World.ChunkSize; i++) { for (int j = 0; j < World.ChunkSize; j++) { if (genRan.Random() < 0.01f) { //obs.Add(WorldObject.ObjectPositionHash(i,j), new Tree(new Vec2i(x * World.ChunkSize + i, z * World.ChunkSize + j))); } else if (genRan.Random() < 0.3f) { //obs.Add(WorldObject.ObjectPositionHash(i, j), new Tree(new Vec2i(x * World.ChunkSize + i, z * World.ChunkSize + j))); } heights[i, j] = GameGen.TerGen.GetWorldHeightAt(x * World.ChunkSize + i, z * World.ChunkSize + j); } } ChunkData cd = new ChunkData(x, z, tiles, true, cb.Height, heightMap: heights); return(cd); }
/// <summary> /// Decides the tile placement in settlement coordinates of all entrances, based on /// the entrance direction data <see cref="Shell.LocationData"/> /// </summary> /// <returns></returns> private Vec2i DecideSettlementEntrance() { List <Vec2i> entrances = new List <Vec2i>(); for (int i = 0; i < 8; i += 2) { int ip1 = (i + 1) % 8; if (Shell.LocationData.EntranceDirections[i] || Shell.LocationData.EntranceDirections[ip1]) { Vec2i side = Vec2i.QUAD_DIR[i / 2]; float xAmount = GenerationRandom.Random(0.8f, 0.95f) * side.x; float zAmount = GenerationRandom.Random(0.8f, 0.95f) * side.z; Vec2i pos = Middle + new Vec2i((int)(xAmount * TileSize / 2), (int)(zAmount * TileSize / 2)); entrances.Add(pos); } } return(GenerationRandom.RandomFromList(entrances)); }
/// <summary> /// Generates a chunk that has no special land features /// </summary> /// <param name="x"></param> /// <param name="z"></param> /// <param name="cb"></param> /// <returns></returns> private ChunkData GenerateSimpleChunk(int x, int z, ChunkBase cb) { if (!cb.IsLand) { return(new ChunkData(x, z, (int[, ])OCEAN.Clone(), cb.IsLand)); } if (cb.Biome == ChunkBiome.dessert) { return(new ChunkData(x, z, (int[, ])EMPTY_DESERT.Clone(), cb.IsLand)); } int[,] tiles = (int[, ])EMPTY_PLAINS.Clone(); GenerationRandom genRan = new GenerationRandom(Seed + x << 16 + z); //if(genRan.Random() > 0.1f) // return new ChunkData(x, z, tiles, cb.IsLand); Dictionary <int, WorldObjectData> obs = new Dictionary <int, WorldObjectData>(256); for (int i = 0; i < World.ChunkSize; i++) { for (int j = 0; j < World.ChunkSize; j++) { if (genRan.Random() < 0.01f) { obs.Add(WorldObject.ObjectPositionHash(i, j), new Tree(new Vec2i(x * World.ChunkSize + i, z * World.ChunkSize + j))); } else if (genRan.Random() < 0.3f) { //obs.Add(WorldObject.ObjectPositionHash(i, j), new Tree(new Vec2i(x * World.ChunkSize + i, z * World.ChunkSize + j))); } } } ChunkData cd = new ChunkData(x, z, tiles, cb.IsLand, obs); return(cd); }
/// <summary> /// Calculates the total number of cities, towns, and villages this kingdom should have /// Number of each is partly random, partly based on total size of kingdom /// </summary> /// <param name="k"></param> /// <returns></returns> private KingdomTotalSettlements CalculateSettlementCount(Kingdom k) { //In the region of 200,000 int kingdomSize = k.ClaimedChunks.Count; int relSize = Mathf.CeilToInt(kingdomSize / 80000f); int cityCount = (int)(1 + GenRan.Random(0.5f, 1.5f) * relSize); int townCount = (int)(5 + GenRan.Random(2, 4) * relSize); int villageCount = (int)(4 + GenRan.Random(4, 7) * relSize); Debug.Log("Kingdom " + k + " has size " + relSize + " -> City: " + cityCount + ", Town: " + townCount + ", Village: " + villageCount); KingdomTotalSettlements kts = new KingdomTotalSettlements(); kts.CityCount = cityCount; kts.TownCount = townCount; kts.VillageCount = villageCount; return(kts); }
/// <summary> /// /// </summary> /// <param name="genRan">The RNG used for this building</param> /// <param name="obj">The object to place in the building</param> /// <param name="height">The height above the ground the object should sit</param> /// <param name="vox">The voxel data of this building. Only required if 'requireWallBacking' is true</param> /// <param name="build">The building to place the object in</param> /// <param name="distFromWall">The distance from a wall the object should be placed</param> /// <param name="wallIndex">Defines the wall we should attempt to place this object on. The 'i'th wall defines the wall /// connecting build.BoundingWall[i] -> build.BoundingWall[i+1]. If left as default value (-1), an index will be randomly generated</param> /// <param name="requireWallBacking">If true, then we will search for a point on the wall that is backed against a wall. /// Used to stop certain objects being placed in front of windows</param> /// <param name="forcePlace">If true, then we do not check for intersection with other objects in the building</param> /// <param name="attemptsCount">The number of attempts made to place the object on this wall</param> /// <returns>True if object is placed sucesfully, false otherwise</returns> public static bool PlaceObjectAgainstWall(GenerationRandom genRan, WorldObjectData obj, float height, BuildingVoxels vox, Building build, float distFromWall = 0, bool requireWallBacking = false, int distToEntr = 2, bool forcePlace = false, int attemptsCount = 5) { if (build.InsideWallPoints == null) { build.InsideWallPoints = FindInsideWallBoundryPoints(vox, build); } //Check attempts count attemptsCount--; if (attemptsCount == 0) { return(false); } float wallDisp = genRan.Random(); //Define the position as the start pos + lambda*dir //Select a random position from all wall points Vec2i pos = genRan.RandomFromArray(build.InsideWallPoints); //If too close to entrance, try another placement if (build.Entrance != null && pos.QuickDistance(build.Entrance) < distToEntr * distToEntr) { return(PlaceObjectAgainstWall(genRan, obj, height, vox, build, distFromWall, requireWallBacking, distToEntr, forcePlace, attemptsCount)); } //If the object is too close to any of the corners, try again foreach (Vec2i v in build.BoundingWall) { if (v.QuickDistance(pos) < 2) { return(PlaceObjectAgainstWall(genRan, obj, height, vox, build, distFromWall, requireWallBacking, distToEntr, forcePlace, attemptsCount)); } } Vec2i faceDirection = GetWallPointDirection(build, pos); //If we require a backing wall, we check the position if (requireWallBacking) { Vec2i wPos = pos - faceDirection; for (int y = 0; y < 4; y++) { VoxelNode vn = vox.GetVoxelNode(wPos.x, y, wPos.z); //If there is no node (nothing set) then this position is not valid if (!vn.IsNode) { return(PlaceObjectAgainstWall(genRan, obj, height, vox, build, distFromWall, requireWallBacking, distToEntr, forcePlace, attemptsCount)); } if (vn.Voxel == Voxel.glass || vn.Voxel == Voxel.none) { return(PlaceObjectAgainstWall(genRan, obj, height, vox, build, distFromWall, requireWallBacking, distToEntr, forcePlace, attemptsCount)); } } } Vector3 finPos = pos.AsVector3() + faceDirection.AsVector3() * distFromWall + Vector3.up * height; float rotation = Vec2i.Angle(Vec2i.Forward, faceDirection); obj.SetPosition(finPos).SetRotation(rotation); if (!forcePlace) { foreach (WorldObjectData obj_ in build.GetBuildingExternalObjects()) { if (obj.Intersects(obj_)) { return(PlaceObjectAgainstWall(genRan, obj, height, vox, build, distFromWall, requireWallBacking, distToEntr, forcePlace, attemptsCount)); } } } //obj.SetPosition(finPos); build.AddInternalObject(obj); return(true); }
private ChunkData GenerateRiverChunk(int x, int z, ChunkBase cb) { GenerationRandom genRan = new GenerationRandom(new Vec2i(x, z).GetHashCode() + Seed); int[,] tiles = new int[World.ChunkSize, World.ChunkSize]; WorldObjectData[,] data = new WorldObjectData[World.ChunkSize, World.ChunkSize]; RiverNode rn = cb.RiverNode; Vec2i exitDelta = rn.RiverExitDelta; Vec2i entrDelta = rn.RiverEntranceDelta; if (exitDelta == null) { exitDelta = new Vec2i(0, 0); } if (entrDelta == null) { entrDelta = new Vec2i(0, 0); } //Calculatee the tile position of the entrance and exit point of the river int entrX = (entrDelta.x == 1) ? 16 : ((entrDelta.x == 0) ? 8 : 0); int entrZ = (entrDelta.z == 1) ? 16 : ((entrDelta.z == 0) ? 8 : 0); int exitX = (exitDelta.x == 1) ? 16 : ((exitDelta.x == 0) ? 8 : 0); int exitZ = (exitDelta.z == 1) ? 16 : ((exitDelta.z == 0) ? 8 : 0); float dx = entrX - exitX; float dz = entrZ - exitZ; //If dx or dz is 0, then float a, b, c; bool angle = (dx != 0 && dz != 0); float divBy = angle ? 2 : 1; if (dx == 0) { a = 0; b = 1; c = -entrX; } else if (dz == 0) { a = 1; b = 0; c = -entrZ; } else { float m = dz / dx; c = -(entrZ - m * entrX); a = 1; b = -m; } float dem_sqr = (a * a + b * b); for (int tx = 0; tx < World.ChunkSize; tx++) { for (int tz = 0; tz < World.ChunkSize; tz++) { float dist_sqr = ((a * tz + b * tx + c) * (a * tz + b * tx + c)) / dem_sqr; if (dist_sqr < (cb.RiverNode.EntranceWidth * cb.RiverNode.EntranceWidth) / divBy) { Vector2 off = new Vector2(x * World.ChunkSize + tx, z * World.ChunkSize + tz); //Debug.Log("here"); tiles[tx, tz] = Tile.WATER.ID; if (!(data[tx, tz] is Water)) { data[tx, tz] = new Water(new Vec2i(x * World.ChunkSize + tx, z * World.ChunkSize + tz)); (data[tx, tz] as Water).SetUVOffset(off); } if (tx < World.ChunkSize - 1 && !(data[tx + 1, tz] is Water)) { data[tx + 1, tz] = new Water(new Vec2i(x * World.ChunkSize + tx + 1, z * World.ChunkSize + tz)); (data[tx + 1, tz] as Water).SetUVOffset(off + new Vector2(1, 0)); } if (tz < World.ChunkSize - 1 && !(data[tx, tz + 1] is Water)) { data[tx, tz + 1] = new Water(new Vec2i(x * World.ChunkSize + tx, z * World.ChunkSize + tz + 1)); (data[tx, tz + 1] as Water).SetUVOffset(off + new Vector2(0, 1)); } if (tx < World.ChunkSize - 1 && tz < World.ChunkSize - 1 && !(data[tx + 1, tz + 1] is Water)) { data[tx + 1, tz + 1] = new Water(new Vec2i(x * World.ChunkSize + tx + 1, z * World.ChunkSize + tz + 1)); (data[tx + 1, tz + 1] as Water).SetUVOffset(off + new Vector2(1, 1)); } if (tx > 0 && !(data[tx - 1, tz] is Water)) { data[tx - 1, tz] = new Water(new Vec2i(x * World.ChunkSize + tx - 1, z * World.ChunkSize + tz)); (data[tx - 1, tz] as Water).SetUVOffset(off + new Vector2(-1, 0)); } if (tz > 0 && !(data[tx, tz - 1] is Water)) { data[tx, tz - 1] = new Water(new Vec2i(x * World.ChunkSize + tx, z * World.ChunkSize + tz - 1)); (data[tx, tz - 1] as Water).SetUVOffset(off + new Vector2(0, -1)); } if (tx > 0 && tz > 0 && !(data[tx - 1, tz - 1] is Water)) { data[tx - 1, tz - 1] = new Water(new Vec2i(x * World.ChunkSize + tx - 1, z * World.ChunkSize + tz - 1)); (data[tx - 1, tz - 1] as Water).SetUVOffset(off + new Vector2(-1, -1)); } if (tx > 0 && tz < World.ChunkSize - 1 && !(data[tx - 1, tz + 1] is Water)) { data[tx - 1, tz + 1] = new Water(new Vec2i(x * World.ChunkSize + tx - 1, z * World.ChunkSize + tz + 1)); (data[tx - 1, tz + 1] as Water).SetUVOffset(off + new Vector2(-1, +1)); } if (tz > 0 && tx < World.ChunkSize - 1 && !(data[tx + 1, tz - 1] is Water)) { data[tx + 1, tz - 1] = new Water(new Vec2i(x * World.ChunkSize + tx + 1, z * World.ChunkSize + tz - 1)); (data[tx + 1, tz - 1] as Water).SetUVOffset(off + new Vector2(1, -1)); } } else if (dist_sqr < (cb.RiverNode.EntranceWidth * cb.RiverNode.EntranceWidth) * 2 / divBy) { tiles[tx, tz] = Tile.SAND.ID; } else { tiles[tx, tz] = Tile.GRASS.ID; if (genRan.Random() < 0.25f) { data[tx, tz] = new Grass(new Vec2i(x * World.ChunkSize + tx + 1, z * World.ChunkSize + tz - 1)); } } } } if (cb.RiverNode.Bridge != null) { GenerateRiverBridge(cb, data); } data[0, 0] = new Tree(new Vec2i(x * World.ChunkSize, z * World.ChunkSize)); data[2, 2] = new RockFormation(new Vec2i(x * World.ChunkSize + 2, z * World.ChunkSize + 2)); Dictionary <int, WorldObjectData> data_ = new Dictionary <int, WorldObjectData>(); for (int i = 0; i < World.ChunkSize; i++) { for (int j = 0; j < World.ChunkSize; j++) { if (data[i, j] != null) { data_.Add(WorldObject.ObjectPositionHash(i, j), data[i, j]); } } } return(new ChunkData(x, z, tiles, cb.IsLand, data_)); }
public void GenerateChunkBases() { ChunkBases = new ChunkBase[World.WorldSize, World.WorldSize]; LandChunks = new List <Vec2i>(); Vec2i mid = new Vec2i(World.WorldSize / 2, World.WorldSize / 2); float r_sqr = (World.WorldSize / 3) * (World.WorldSize / 3); WorldRad = (World.WorldSize / 2.1f) * (World.WorldSize / 2.1f); Texture2D t = new Texture2D(World.WorldSize, World.WorldSize); Texture2D hum = new Texture2D(World.WorldSize, World.WorldSize); Texture2D temp = new Texture2D(World.WorldSize, World.WorldSize); float[,] humdity = new float[World.WorldSize, World.WorldSize]; Vec2i offset = GenRan.RandomVec2i(World.WorldSize / 8, World.WorldSize / 4); Vec2i humMid = mid + offset; float humRadSqr = GenRan.Random(World.WorldSize / 4, World.WorldSize / 2); humRadSqr *= humRadSqr; Vec2i tempMid = mid - offset; float tempRadSqr = GenRan.Random(World.WorldSize / 4, World.WorldSize / 2); tempRadSqr *= tempRadSqr; float[,] temperature = new float[World.WorldSize, World.WorldSize]; for (int x = 0; x < World.WorldSize; x++) { for (int z = 0; z < World.WorldSize; z++) { float c = WorldHeightChunk(x, z); humdity[x, z] = 0.4f + 0.6f * Mathf.PerlinNoise(4000 + x * 0.02f, 4000 + z * 0.02f); humdity[x, z] /= (((x - humMid.x) * (x - humMid.x) + (z - humMid.z) * (z - humMid.z)) / humRadSqr); humdity[x, z] = Mathf.Clamp(humdity[x, z], 0, 1); //temperature[x, z] = Mathf.PerlinNoise(700 + x * 0.02f, 700 + z * 0.02f); temperature[x, z] = 0.4f + 0.6f * Mathf.PerlinNoise(700 + x * 0.02f, 700 + z * 0.02f); temperature[x, z] /= (((x - tempMid.x) * (x - tempMid.x) + (z - tempMid.z) * (z - tempMid.z)) / tempRadSqr); temperature[x, z] = Mathf.Clamp(temperature[x, z], 0, 1); hum.SetPixel(x, z, new Color(humdity[x, z], humdity[x, z], humdity[x, z])); temp.SetPixel(x, z, new Color(temperature[x, z], temperature[x, z], temperature[x, z])); //c /= (((x - mid.x) * (x - mid.x) + (z - mid.z) * (z - mid.z)) / r_sqr); t.SetPixel(x, z, new Color(c / World.ChunkHeight, c / World.ChunkHeight, c / World.ChunkHeight)); Vec2i v = new Vec2i(x, z); //if ((x - mid.x) * (x - mid.x) + (z - mid.z) * (z - mid.z) < r_sqr) if (c > 40 && !(x == 0 || z == 0 || x == World.WorldSize - 1 || z == World.WorldSize - 1)) { //If point within this radius of middle ChunkBases[x, z] = new ChunkBase(v, Mathf.FloorToInt(c), true); LandChunks.Add(v); if (c > 100) { ChunkBases[x, z].SetBiome(ChunkBiome.mountain); } else if (temperature[x, z] > 0.7f && humdity[x, z] < 0.4f) { ChunkBases[x, z].SetBiome(ChunkBiome.dessert); } else if (humdity[x, z] > 0.4f && temperature[x, z] > 0.5f) { ChunkBases[x, z].SetBiome(ChunkBiome.forrest); } else { ChunkBases[x, z].SetBiome(ChunkBiome.grassland); } /* * if(temperature[x, z] > 0.7f && humdity[x,z] < 0.4f) * { * ChunkBases[x, z].SetBiome(ChunkBiome.dessert); * }else if(temperature[x, z] < 0.7f && humdity[x, z] > 0.6f) * if (temperature[x, z] < 0.25f) * { * ChunkBases[x, z].SetBiome(ChunkBiome.forrest); * } * else * { * ChunkBases[x, z].SetBiome(ChunkBiome.grassland); * }*/ } else { ChunkBases[x, z] = new ChunkBase(v, 1, false); } } } t.Apply(); hum.Apply(); temp.Apply(); /* * GameManager.Game.toDrawTexts[0] = t; * GameManager.Game.toDrawTexts[1] = hum; * GameManager.Game.toDrawTexts[2] = temp;*/ }
private void FromRiverSource(Vec2i source, Vec2i end, float startHeight = -1, int distSinceFork = 0, bool riverRaySearch = true) { int i = 0; if (startHeight < 0) { startHeight = ChunkBases[source.x, source.z].Height; } i = 0; Vec2i last = source; Vec2i mainDir = CalcDir(source, end); Vec2i current = source + mainDir; bool isDone = false; Vector2 exactCurrent = current.AsVector2(); List <RiverPoint> river = new List <RiverPoint>(); ChunkRiverNode previousNode = null; while (!isDone) { int distToEnd = end.QuickDistance(current); i++; /* * if(i%16 == 0 && riverRaySearch) * { * bool success = false; * //search up to 16 chunks away * for(int j=1; j<16; j++) * { * if (success) * break; * //search all 8 directions * foreach(Vec2i v in Vec2i.OCT_DIRDIR) * { * Vec2i p = current + v * j; * * if(ChunkBases[p.x,p.z].Biome == ChunkBiome.ocean || ChunkBases[p.x, p.z].ChunkFeature is ChunkRiverNode) * { * end = p; * success = true; * break; * } * } * } * }*/ // Vector2 currentFlow = FlowField[current.x, current.z]; float fx = PerlinNoise(current.x, current.z, 36) * 2 - 1; float fz = PerlinNoise(current.x, current.z, 37) * 2 - 1; Vector2 noiseFlow = new Vector2(fx, fz); Vector2 flowField = FlowField[current.x, current.z]; Vector2 targetFlow = (end - current).AsVector2().normalized; float targetFlowMult = distToEnd < 400 ? 4 * Mathf.Exp((400f - distToEnd) / 200f) : 4; Vector2 flow = (noiseFlow + targetFlowMult * targetFlow + 3.5f * flowField).normalized; exactCurrent += flow; current = Vec2i.FromVector2(exactCurrent); int check = Mathf.Min(river.Count, 5); bool isValid = true; for (int j = 0; j < check; j++) { if (river[river.Count - j - 1].Pos == current) { isValid = false; } } if (!isValid) { current += mainDir; exactCurrent = current.AsVector2(); } if (ChunkBases[current.x, current.z].Biome == ChunkBiome.ocean) { isDone = true; } if (ChunkBases[current.x, current.z].ChunkFeature is ChunkRiverNode) { isDone = true; //Shouldn't be null, but lets do a check anyway if (previousNode != null) { //Get the river node ChunkRiverNode endNode = ChunkBases[current.x, current.z].ChunkFeature as ChunkRiverNode; //Inform river nodes of flow endNode.FlowIn.Add(previousNode); previousNode.FlowOut.Add(endNode); ModifyRiverHeight(endNode); } if (GenRan.Random() < 0.5f) { PlaceLake(current, 8); } } if (current == end) { isDone = true; } ChunkRiverNode nextNode = new ChunkRiverNode(current); ChunkBases[current.x, current.z].SetChunkFeature(nextNode); if (previousNode != null) { nextNode.FlowIn.Add(previousNode); previousNode.FlowOut.Add(nextNode); } previousNode = nextNode; //If this chunk is too high, we modify it and the surrounding area if (ChunkBases[current.x, current.z].Height > startHeight) { ModifyRiverValleyHeight(current, startHeight); } else if (ChunkBases[current.x, current.z].Height < startHeight) { startHeight = ChunkBases[current.x, current.z].Height; } RiverPoint rp = new RiverPoint(); rp.Pos = current; rp.Flow = flow; river.Add(rp); if (i > 4096) { PlaceLake(current, 12); return; } /* * distSinceFork++; * * if(distSinceFork > 256) * { * * float p = Mathf.Exp(-distSinceFork/200f); * if(p < GenRan.Random()) * { * * Vec2i delta = GenRan.RandomVec2i(-64, 64); * * FromRiverSource(current + delta, current); * distSinceFork = 0; * } * * }*/ mainDir = CalcDir(current, end); } return; if (river.Count > 128) { int forkCount = Mathf.CeilToInt(river.Count / 128f); int jump = (int)(((float)river.Count) / forkCount); int index = GenRan.RandomInt(0, jump); for (int j = 0; j < forkCount; j++) { if (index > river.Count - 30) { return; } RiverPoint rp = river[index]; Vec2i forkEnd = rp.Pos; Vec2i delta = GenRan.RandomVec2i(-32, 32); Vector2 endToForkDir = (forkEnd - end).AsVector2().normalized; Vec2i forkStart = Vec2i.FromVector2(forkEnd.AsVector2() + endToForkDir * GenRan.Random(64, 128)) + delta; FromRiverSource(forkStart, forkEnd); index += jump + GenRan.RandomInt(-32, 32); } } }
public void GenerateChunkBases() { ChunkBases = new ChunkBase[World.WorldSize, World.WorldSize]; LandChunks = new List <Vec2i>(); GenerationRandom genRan = new GenerationRandom(0); Vec2i mid = new Vec2i(World.WorldSize / 2, World.WorldSize / 2); float r_sqr = (World.WorldSize / 3) * (World.WorldSize / 3); Texture2D t = new Texture2D(World.WorldSize, World.WorldSize); Texture2D hum = new Texture2D(World.WorldSize, World.WorldSize); Texture2D temp = new Texture2D(World.WorldSize, World.WorldSize); float[,] humdity = new float[World.WorldSize, World.WorldSize]; Vec2i offset = genRan.RandomVec2i(World.WorldSize / 8, World.WorldSize / 4); Vec2i humMid = mid + offset; float humRadSqr = genRan.Random(World.WorldSize / 4, World.WorldSize / 2); humRadSqr *= humRadSqr; Vec2i tempMid = mid - offset; float tempRadSqr = genRan.Random(World.WorldSize / 4, World.WorldSize / 2); tempRadSqr *= tempRadSqr; float[,] temperature = new float[World.WorldSize, World.WorldSize]; for (int x = 0; x < World.WorldSize; x++) { for (int z = 0; z < World.WorldSize; z++) { float c = 1 - Mathf.Pow(Mathf.PerlinNoise(x * 0.01f, z * 0.01f), 2); humdity[x, z] = 0.4f + 0.6f * Mathf.PerlinNoise(4000 + x * 0.02f, 4000 + z * 0.02f); humdity[x, z] /= (((x - humMid.x) * (x - humMid.x) + (z - humMid.z) * (z - humMid.z)) / humRadSqr); humdity[x, z] = Mathf.Clamp(humdity[x, z], 0, 1); //temperature[x, z] = Mathf.PerlinNoise(700 + x * 0.02f, 700 + z * 0.02f); temperature[x, z] = 0.4f + 0.6f * Mathf.PerlinNoise(700 + x * 0.02f, 700 + z * 0.02f); temperature[x, z] /= (((x - tempMid.x) * (x - tempMid.x) + (z - tempMid.z) * (z - tempMid.z)) / tempRadSqr); temperature[x, z] = Mathf.Clamp(temperature[x, z], 0, 1); hum.SetPixel(x, z, new Color(humdity[x, z], humdity[x, z], humdity[x, z])); temp.SetPixel(x, z, new Color(temperature[x, z], temperature[x, z], temperature[x, z])); c /= (((x - mid.x) * (x - mid.x) + (z - mid.z) * (z - mid.z)) / r_sqr); t.SetPixel(x, z, new Color(c, c, c)); Vec2i v = new Vec2i(x, z); //if ((x - mid.x) * (x - mid.x) + (z - mid.z) * (z - mid.z) < r_sqr) if (c > 0.5) { //If point within this radius of middle ChunkBases[x, z] = new ChunkBase(v, true); LandChunks.Add(v); //Deserts if its hot and dry if (temperature[x, z] > 0.7f && humdity[x, z] < 0.4f) { ChunkBases[x, z].SetBiome(ChunkBiome.dessert); } else if (humdity[x, z] > 0.4f && temperature[x, z] > 0.5f) { ChunkBases[x, z].SetBiome(ChunkBiome.forrest); } else { ChunkBases[x, z].SetBiome(ChunkBiome.grassland); } /* * if(temperature[x, z] > 0.7f && humdity[x,z] < 0.4f) * { * ChunkBases[x, z].SetBiome(ChunkBiome.dessert); * }else if(temperature[x, z] < 0.7f && humdity[x, z] > 0.6f) * if (temperature[x, z] < 0.25f) * { * ChunkBases[x, z].SetBiome(ChunkBiome.forrest); * } * else * { * ChunkBases[x, z].SetBiome(ChunkBiome.grassland); * }*/ } else { ChunkBases[x, z] = new ChunkBase(v, false); } } } t.Apply(); hum.Apply(); temp.Apply(); GameManager.Game.toDrawTexts[0] = t; GameManager.Game.toDrawTexts[1] = hum; GameManager.Game.toDrawTexts[2] = temp; }
private Vec3i ChooseNextPosition(List <Vec3i> tunnel, int radius = 3) { Vec3i basePoint = tunnel[tunnel.Count - 1]; int length = GenRan.RandomInt(3, 8); Vec3i direction = basePoint - tunnel[tunnel.Count - 2]; if (GenRan.Random() < 0.5f) { int axis = GenRan.RandomInt(0, 3); if (axis == 0) { direction.x = direction.x == 0 ? GenRan.RandomInt(-1, 2) : 0; } if (axis == 1) { direction.y = direction.y == 0 ? (int)Mathf.Clamp(GenRan.GaussianFloat(0, 0.2f), -1, 1) : 0; } if (axis == 2) { direction.z = direction.z == 0 ? GenRan.RandomInt(-1, 2) : 0; } } int xEnd = basePoint.x + direction.x * length; int yEnd = basePoint.y + direction.y * length; int zEnd = basePoint.z + direction.z * length; if (xEnd < radius) { direction.x = GenRan.RandomInt(0, 2); } else if (xEnd >= TileSize.x - radius) { direction.x = GenRan.RandomInt(-1, 1); } if (yEnd < radius) { direction.y = GenRan.RandomInt(0, 2); } else if (yEnd >= World.ChunkHeight - radius) { direction.y = GenRan.RandomInt(-1, 1); } if (zEnd < radius) { direction.z = GenRan.RandomInt(0, 2); } else if (zEnd >= TileSize.z - radius) { direction.z = GenRan.RandomInt(-1, 1); } for (int i = 0; i < length; i++) { tunnel.Add(basePoint + direction * (i + 1)); } return(tunnel[tunnel.Count - 1]); }