/// <summary>
        /// main method that should be called to populate biomes
        /// </summary>
        public void start(int level)
        {
            // create biomes
            int numBiomes = (int)Mathf.Max(WIDTH, HEIGHT) / 30;

            Debug.Log("Creating biomes");

            // ocean
            for (int i = 0; i < numBiomes; i++)
            {
                switch (level)
                {
                case 1:
                case 2:
                default:
                    // Ocean
                    createBiome(Terrain.TerrainTypes.Ocean);
                    // Desert
                    createBiome(Terrain.TerrainTypes.DesertHill);
                    // GrassHill
                    createBiome(Terrain.TerrainTypes.GrassHill);
                    // Mountain
                    createBiome(Terrain.TerrainTypes.Grass);
                    break;

                case 3:
                    // Desert
                    createBiome(Terrain.TerrainTypes.DesertHill);
                    createBiome(Terrain.TerrainTypes.DesertHill);
                    createBiome(Terrain.TerrainTypes.DesertHill);
                    createBiome(Terrain.TerrainTypes.DesertHill);
                    break;
                }
            }

            // Populate none biom areas with grass
            Debug.Log("Creating non biomes");
            for (int i = 0; i < WIDTH; i++)
            {
                for (int j = 0; j < HEIGHT; j++)
                {
                    MapTile tile = ScriptableObject.CreateInstance <MapTile>();

                    // A vector used for hex position
                    Vector3Int vector = new Vector3Int(-i + WIDTH / 2, -j + HEIGHT / 2, 0);

                    int[] pos = new int[2];
                    pos[0] = i;
                    pos[1] = j;
                    if (getTileTerrain(pos).Equals(Terrain.TerrainTypes.NotSet))
                    {
                        if (nextToOcean(pos))
                        {
                            tile.Terrain = new Terrain(Terrain.TerrainTypes.Beach);
                        }
                        else if (surroundedByDessert(pos) || level == 3)
                        {
                            tile.Terrain = new Terrain(Terrain.TerrainTypes.Desert);
                        }
                        else
                        {
                            tile.Terrain = new Terrain(Terrain.TerrainTypes.Grass);
                        }

                        SetTileTo(vector, tile);
                        // Refresh the tile whenever its sprite changes.
                        tile.SpriteChange += () => map.RefreshTile(vector);
                    }
                }
            }

            if (level != 3)
            {
                // creates rivers until a sufficient length has been found.
                // max 4 attempts.
                for (int i = 0; i < 4 || riverLength >= Mathf.Max(WIDTH, HEIGHT) / 2; i++)
                {
                    createRiver();
                    riverLength = 0;
                }
            }

            NormalizeWaterTiles();
        }
        /// <summary>
        /// grows a biome
        /// </summary>
        private void growBoime(int[] anchor, int biomHalfLength, Terrain.TerrainTypes terrain)
        {
            // constants that will be used further down the line
            float k = Mathf.Sqrt(Mathf.Pow(biomHalfLength, 2) * 2);
            float a = (float)2.0f / Mathf.Log10(Mathf.Pow(k, 2) - 2.0f);

            // growing biom
            for (int i = 0; i < biomHalfLength * 3; i++)
            {
                for (int j = 0; j < biomHalfLength * 3; j++)
                {
                    // current position array where index 0 is X and index 1 is Y coordinate
                    int[] curPos = new int[2];
                    curPos[0] = anchor[0] - biomHalfLength + i;
                    curPos[1] = anchor[1] - biomHalfLength + j;

                    // check if X and Y values are within the map
                    if (curPos[0] < WIDTH && curPos[0] >= 0 && curPos[1] < HEIGHT && curPos[1] >= 0)
                    {
                        MapTile tile = ScriptableObject.CreateInstance <MapTile>();
                        // A vector used for hex position
                        Vector3Int vector = new Vector3Int(-curPos[0] + WIDTH / 2, -curPos[1] + HEIGHT / 2, 0);
                        // Find the real position (the position on the screen)
                        Vector3 mappedVector = map.CellToWorld(vector);

                        tile.Canvas         = parent;
                        tile.ScreenPosition = mappedVector;

                        // check if terrain is vacant
                        if (getTileTerrain(curPos).Equals(Terrain.TerrainTypes.NotSet))
                        {
                            // Debug.Log("Cur: X: " + curX + ", Y: " + curY);

                            // weighted random terrain allocation depending on distance from anchor
                            int value = random.Next(0, 100);

                            // weighting is done in such a that:
                            // P = (k^2 - d^2)^a,
                            //    where:
                            //       P = probability of the biome tile being set
                            //       k = a constant (calculated earlier before the nested for loops)
                            //       d = absolute distance between the anchor and any potential biome tile
                            //       a = a constant (calculated earlier before the nested for loops)
                            //
                            // the formula satisfies the two equations below:
                            //    100 = (k^2 - 2)^a, this ensures that the biome is atleast 3x3
                            //    0 = (k^2 - d^2)^a, where d is a distance for an arbitrary square just outside the biome half length
                            //
                            // so if biome half length is 7 then k = 11.3 and a = 0.484
                            float  dist = Mathf.Sqrt(Mathf.Pow(anchor[0] - curPos[0], 2) + Mathf.Pow(anchor[1] - curPos[1], 2));
                            double prob = Mathf.Pow(Mathf.Pow(k, 2) - Mathf.Pow(dist, 2), a);
                            if (value < prob)
                            {
                                occupiedBiomSpots[curPos] = terrain;
                                tile.Terrain = new Terrain(terrain);
                                SetTileTo(vector, tile);

                                // add mountains if the biome is just grass
                                if (terrain.Equals(Terrain.TerrainTypes.Grass))
                                {
                                    StructureFactory mountainFactory = new MountainFactory();
                                    if (mountainFactory.CanBuildOnto(tile, out _))
                                    {
                                        mountainFactory.BuildOnto(tile);
                                    }
                                }

                                // Refresh the tile whenever its sprite changes.
                                tile.SpriteChange += () => map.RefreshTile(vector);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// main iteration for the river
        /// should be called twice at same anchor
        /// </summary>
        private void extendRiverBiome(int[] anchor, float riverGradient)
        {
            // 5a) initialise variables
            int[] curPos = new int[2];
            curPos[0] = anchor[0];
            curPos[1] = anchor[1];

            // this is to make the river a bit more wavy
            float   riverGradientBuffer    = 5f;
            int     counter                = 0;
            Boolean negativeGradientBuffer = true;

            float overallRiverGradient = riverGradient - riverGradientBuffer;

            Debug.Log("river iteration");

            // main iteration
            // continue as long as river tiles are within map
            while (curPos[0] < WIDTH && curPos[0] > 0 && curPos[1] < HEIGHT && curPos[1] > 0 && counter < 300)
            {
                Debug.Log("curPos: X: " + curPos[0] + ", Y: " + curPos[1]);
                // get neighbouring tiles to current and iterate through them
                int[,] adjPos = getNeighbouringTiles(curPos);
                float gradientDifference = WIDTH * HEIGHT * 10000;
                int[] prevPos            = new int[2];
                prevPos[0] = curPos[0];
                prevPos[1] = curPos[1];
                // iterate through the neighbouring tiles
                for (int i = 0; i < adjPos.GetLength(0); i++)
                {
                    int[] temp = new int[2];
                    temp[0] = adjPos[i, 0];
                    temp[1] = adjPos[i, 1];

                    // give river gradient a bit of buffer variance
                    if (counter % 15 == 0)
                    {
                        anchor[0] = curPos[0];
                        anchor[1] = curPos[1];
                        if (negativeGradientBuffer)
                        {
                            negativeGradientBuffer = false;
                            overallRiverGradient  += riverGradientBuffer;
                        }
                        else
                        {
                            negativeGradientBuffer = true;
                            overallRiverGradient  -= riverGradientBuffer;
                        }
                    }

                    // if the neighbouring tile is within the map
                    // set the tile to river
                    if (temp[0] < WIDTH && temp[0] >= 0 && temp[1] < HEIGHT && temp[1] >= 0 && getTileTerrain(temp).Equals(Terrain.TerrainTypes.NotSet))
                    {
                        float gradient;

                        // calculate gradient of the adjPos to anchor
                        try
                        {
                            gradient = (anchor[1] - temp[1]) / (anchor[0] - temp[0]);
                        }
                        catch (DivideByZeroException)
                        {
                            gradient = WIDTH * HEIGHT * 10000; // some large number of signify inifinity
                        }

                        // get tile with smallest gradient change to be next current position
                        if (Mathf.Abs(overallRiverGradient - gradient) <= gradientDifference)
                        {
                            gradientDifference = Mathf.Abs(overallRiverGradient - gradient);
                            curPos[0]          = temp[0];
                            curPos[1]          = temp[1];

                            // make sure the river does not get too close to other biomes besides oceans
                            int biomeLengthValue = (int)(Mathf.Max(WIDTH, HEIGHT) / 5);
                            // int biomeHalfLength = random.Next(biomeLengthValue - 2, biomeLengthValue + 2);
                            if (!riverTileTooCloseToBiome(temp, biomeLengthValue))
                            {
                                float gradientToClosestBiome = getGradientOfClosestBiomeTile(curPos, biomeLengthValue);
                                int[] closestBiometile       = getClosestBiomeAnchorTile(curPos);
                                float prevDist = Mathf.Sqrt(Mathf.Pow(prevPos[0] - closestBiometile[0], 2) + Mathf.Pow(prevPos[1] - closestBiometile[1], 2));
                                float curDist  = Mathf.Sqrt(Mathf.Pow(curPos[0] - closestBiometile[0], 2) + Mathf.Pow(curPos[1] - closestBiometile[1], 2));

                                Debug.Log("prevDist: " + prevDist);
                                Debug.Log("curDist: " + curDist);

                                // check if the river is heading towards a biome and if is change gradient of the river
                                if (curDist < prevDist)
                                {
                                    overallRiverGradient = -1f / gradientToClosestBiome;
                                }
                            }
                        }

                        // set tile to River
                        MapTile tile = ScriptableObject.CreateInstance <MapTile>();
                        // A vector used for hex position
                        Vector3Int vector = new Vector3Int(-temp[0] + WIDTH / 2, -temp[1] + HEIGHT / 2, 0);
                        // Find the real position (the position on the screen)
                        Vector3 mappedVector = map.CellToWorld(vector);

                        tile.Canvas         = parent;
                        tile.ScreenPosition = mappedVector;
                        tile.Terrain        = new Terrain(Terrain.TerrainTypes.River);

                        occupiedBiomSpots[temp] = Terrain.TerrainTypes.River;
                        SetTileTo(vector, tile);
                        // Refresh the tile whenever its sprite changes.
                        tile.SpriteChange += () => map.RefreshTile(vector);
                    }
                }
                if (getTileTerrain(curPos).Equals(Terrain.TerrainTypes.Ocean))
                {
                    // reached ocean
                    break;
                }
                if (prevPos[0].Equals(curPos[0]) && prevPos[1].Equals(curPos[1]))
                {
                    // current position did not change so stop loop.
                    break;
                }
                counter++;
                riverLength++;
            }
        }
        /// <summary>
        /// creates a river
        /// 1) first the centre is determined
        /// 2) radius of the circle around origin related values are calculated, ie the base value which is proportional to map dimensions,
        ///       a random nonce that dictates the possible range of the radius
        /// 3) anchor on the circumference of the circle is randomly made
        /// 4) gradient of the river is calculated, it will be tangential to the circle, ie gradient of river = -1/(gradient of line origin to anchor)
        /// 5) get neighbouring tiles of current position and set tile with gradient of river as water, repeat until next tile is water or out of map
        /// <summary>
        private void createRiver()
        {
            Debug.Log("creating river");

            // 1) get coordinate of origin
            int[] centre = new int[2];
            centre[0] = WIDTH / 2;
            centre[1] = HEIGHT / 2;
            Debug.Log("centre: X: " + centre[0] + ", Y: " + centre[1]);

            // 2) calculate radius related value of circle around origin
            int radiusBaseValue = Mathf.Max(WIDTH, HEIGHT) / 4;
            int radiusNonce     = random.Next(0, Mathf.Max(WIDTH, HEIGHT) / 30);

            Debug.Log("radiusBaseValue: " + radiusBaseValue);
            Debug.Log("radiusNonce: " + radiusNonce);

            // 3) anchor of the river
            int[] anchor = new int[2];
            anchor[0] = random.Next(centre[0] - (radiusBaseValue + radiusNonce), centre[0] + (radiusBaseValue + radiusNonce));
            anchor[1] = random.Next(centre[1] - (radiusBaseValue + radiusNonce), centre[1] + (radiusBaseValue + radiusNonce));

            Debug.Log("anchor: X: " + anchor[0] + ", Y: " + anchor[1]);

            // adding anchor to screen
            MapTile    anchorTile         = ScriptableObject.CreateInstance <MapTile>();
            Vector3Int anchorVector       = new Vector3Int(-anchor[0] + WIDTH / 2, -anchor[1] + HEIGHT / 2, 0);
            Vector3    anchorMappedVector = map.CellToWorld(anchorVector);

            Debug.Log("anchor Vector: X: " + (-anchor[0] + WIDTH / 2) + ", Y: " + (-anchor[1] + HEIGHT / 2));

            anchorTile.Canvas         = parent;
            anchorTile.ScreenPosition = anchorMappedVector;

            anchorTile.Terrain = new Terrain(Terrain.TerrainTypes.River);

            biomeAnchors[anchor] = Terrain.TerrainTypes.River;

            // 4) calculate gradient of the river
            float riverGradient;

            try
            {
                riverGradient = -(anchor[0] - centre[0]) / (anchor[1] - centre[1]);
            }
            catch (DivideByZeroException)
            {
                riverGradient = 1000; // some large number to signify infinity
            }

            Debug.Log("river gradient: " + riverGradient);

            // 5) expand river in bidirectionally
            extendRiverBiome(anchor, riverGradient);

            //while (riverLength < Mathf.Max(WIDTH, HEIGHT) / 2)
            //{
            int[,] adjPos = getNeighbouringTiles(anchor);
            int value = random.Next(0, 6);

            int[] anchor2 = new int[2];
            anchor2[0] = adjPos[value, 0];
            anchor2[1] = adjPos[value, 1];

            extendRiverBiome(anchor2, riverGradient);
            //}

            // add anchor to occupiedBiomeSpots so that it won't get overwritten when non biomes are made
            occupiedBiomSpots[anchor] = Terrain.TerrainTypes.River;
        }
Example #5
0
        /// <summary>
        /// Generates the particle effect when a demolision occurs
        /// </summary>
        /// <param name="tile"></param>
        /// <returns></returns>
        IEnumerator GenerateDestructionParticles(MapTile tile)
        {
            GameObject copyOfGameObject = GameObject.Instantiate(tile.Structure.GameObject);

            copyOfGameObject.name = "CopyStructures";
            copyOfGameObject.transform.SetParent(tile.Structure.GameObject.transform.parent.gameObject.transform);
            GameObject customParticleSystem = new GameObject("CustomDemolishParticle");

            customParticleSystem.transform.SetParent(copyOfGameObject.transform.parent, false);
            customParticleSystem.transform.position = copyOfGameObject.transform.position;
            ParticleSystem particles = customParticleSystem.AddComponent <ParticleSystem>();

            Particles.InitParticleSystem(particles);
            particles.Stop();

            copyOfGameObject.AddComponent <ShakerBehaviour>();

            ParticleSystem.MainModule mainModule = particles.main;
            mainModule.startColor    = Color.white;
            mainModule.startLifetime = 1;
            mainModule.startSize     = new ParticleSystem.MinMaxCurve(0.3f, 0.5f);
            mainModule.startSpeed    = 0.6f;
            mainModule.maxParticles  = 100;
            mainModule.loop          = false;
            mainModule.duration      = 1;

            ParticleSystem.ShapeModule shapeModule = particles.shape;
            shapeModule.shapeType = ParticleSystemShapeType.Sphere;
            shapeModule.angle     = 25;
            shapeModule.radius    = 0.8f;
            shapeModule.scale     = new Vector3(0.3f, 0.1f, 1);
            shapeModule.position  = new Vector3(0, -0.4f, 0);

            var emission = particles.emission;

            emission.rateOverTime = 30;
            emission.enabled      = true;

            var      colorOverLifetime = particles.colorOverLifetime;
            Gradient gradientLifetime  = new Gradient();

            GradientColorKey[] colorKeys = new GradientColorKey[2];
            colorKeys[0].color = Color.white;
            colorKeys[0].time  = 0.0f;
            colorKeys[1].color = Color.white;
            colorKeys[1].time  = 1.0f;
            GradientAlphaKey[] alphaKeys = new GradientAlphaKey[2];
            alphaKeys[0].alpha = 1;
            alphaKeys[0].time  = 0.5f;
            alphaKeys[1].alpha = 0;
            alphaKeys[1].time  = 1.0f;
            gradientLifetime.SetKeys(colorKeys, alphaKeys);
            colorOverLifetime.color   = gradientLifetime;
            colorOverLifetime.enabled = true;

            var sizeOverLifetime = particles.sizeOverLifetime;

            sizeOverLifetime.size = new ParticleSystem.MinMaxCurve(1,
                                                                   new AnimationCurve(new Keyframe(0.0f, 0.0f), new Keyframe(0.5f, 1.0f, 0, 0)));
            sizeOverLifetime.enabled = true;

            var limit = particles.limitVelocityOverLifetime;

            limit.separateAxes = true;
            limit.limitX       = 1;
            limit.limitY       = 0;
            limit.limitZ       = 0;
            limit.dampen       = 1;
            limit.enabled      = true;

            ParticleSystem.TextureSheetAnimationModule textureSheetAnimationModule = particles.textureSheetAnimation;
            textureSheetAnimationModule.enabled = true;
            textureSheetAnimationModule.mode    = ParticleSystemAnimationMode.Sprites;
            textureSheetAnimationModule.SetSprite(0, Resources.Load <Sprite>("Textures/CloudParticle"));

            Renderer renderer = particles.GetComponent <Renderer>();

            renderer.sortingLayerName = "Structure";
            renderer.sortingOrder     = 100;

            particles.Play();

            var existingSmoker = tile.Structure.GameObject.GetComponentInChildren <ParticleSystem>();

            if (existingSmoker != null)
            {
                var position = existingSmoker.transform.localPosition;
                var scale    = existingSmoker.transform.localScale;

                // Wait until structure does its unrendering.
                yield return(new WaitForEndOfFrame());

                // Copy over existing smoker and make them stay at their place relative to the world even when the building is falling.
                existingSmoker.transform.SetParent(copyOfGameObject.transform);
                existingSmoker.transform.localScale    = scale;
                existingSmoker.transform.localPosition = position;

                // Stop any existing smokers. Let their trail stay though.
                foreach (var smoker in copyOfGameObject.GetComponentsInChildren <ParticleSystem>())
                {
                    smoker.Stop();
                }
            }

            yield return(new WaitForSeconds(10));

            GameObject.Destroy(copyOfGameObject);
            GameObject.Destroy(customParticleSystem);
        }
Example #6
0
        private void GenerateWindEffect(MapTile tile)
        {
            GameObject air = new GameObject();

            air.transform.SetParent(tile.Structure.GameObject.transform);
            air.transform.localPosition = new Vector3(1f, -0.5f, -4.5f);
            air.transform.localScale    = new Vector3(0.5f, 0.5f, 1);

            ParticleSystem particles = air.AddComponent <ParticleSystem>();

            Particles.InitParticleSystem(particles);

            ParticleSystem.MainModule mainParticle = particles.main;
            mainParticle.startLifetime = 1.5f;
            mainParticle.startSpeed    = 0;
            mainParticle.startSize     = 0.1f;
            mainParticle.maxParticles  = 5;


            ParticleSystem.TrailModule trailMode = particles.trails;
            trailMode.enabled          = true;
            trailMode.lifetime         = new ParticleSystem.MinMaxCurve(0.8f);
            trailMode.dieWithParticles = false;
            AnimationCurve trailCurve = new AnimationCurve();

            trailCurve.AddKey(0, 0);
            trailCurve.AddKey(0.5f, 1);
            trailCurve.AddKey(1, 0);
            trailMode.widthOverTrail = new ParticleSystem.MinMaxCurve(1, trailCurve);


            ParticleSystem.EmissionModule emissionModule = particles.emission;
            emissionModule.rateOverTime = 3;
            ParticleSystemRenderer particleRenderer = particles.GetComponent <ParticleSystemRenderer>();

            particleRenderer.trailMaterial = Resources.Load <Material>("Wind");


            ParticleSystem.ShapeModule shapeModule = particles.shape;
            shapeModule.shapeType = ParticleSystemShapeType.Box;
            shapeModule.rotation  = new Vector3(0, 0, 180);
            shapeModule.scale     = new Vector3(0.1f, 1, 1);

            ParticleSystem.VelocityOverLifetimeModule velocityOverLifetimeModule = particles.velocityOverLifetime;
            velocityOverLifetimeModule.enabled = true;

            AnimationCurve curveX = new AnimationCurve();

            curveX.AddKey(0, 1);
            curveX.AddKey(1, 1);

            AnimationCurve curveY = new AnimationCurve();

            curveY.AddKey(0, 0);
            curveY.AddKey(0.33f, 1);
            curveY.AddKey(0.66f, -1);
            curveY.AddKey(1, 0);

            AnimationCurve curveZ = new AnimationCurve();

            curveZ.AddKey(0, 0);
            curveZ.AddKey(1.0f, 0);

            //velocityOverLifetimeModule.space = ParticleSystemSimulationSpace.Local;
            velocityOverLifetimeModule.x = new ParticleSystem.MinMaxCurve(1, curveX);
            velocityOverLifetimeModule.y = new ParticleSystem.MinMaxCurve(1, curveY);
            velocityOverLifetimeModule.z = new ParticleSystem.MinMaxCurve(0, curveZ);

            ParticleSystem.ColorOverLifetimeModule colorOverLifetimeModule = particles.colorOverLifetime;
            colorOverLifetimeModule.enabled = true;
            Gradient gradient = new Gradient();

            gradient.SetKeys(new [] { new GradientColorKey(new Color32(167, 232, 242, 255), 0.0f),
                                      new GradientColorKey(Color.white, 1.0f) },
                             new [] { new GradientAlphaKey(1.0f, 0.0f),
                                      new GradientAlphaKey(0.0f, 1.0f) });
            colorOverLifetimeModule.color = gradient;
        }
Example #7
0
 public TileClickArgs(MapTile tile)
 {
     this.Tile = tile;
 }