/// <summary>
        /// Initializes everything.
        /// </summary>
        void Start()
        {
            prevCameraPosition = MainCamera.transform.position;
            TilesToRemove      = new List <OSPObject>();
            PlacementHistory   = new List <OSPRandomHistory>();
            ParallaxPools      = new List <OSPPool>();

            // Grab the camera's bounds
            CamBounds = MainCamera.OrthographicBounds();

            // Loops through the parallax layers and assigns an ID to each one, finds the maximum Z distance from the camera,
            // and sets the object pool amount.
            // This loop is mainly to determine the maximum Z distance because our parallax speed doe each layer depends on it.
            for (int i = 0; i < ParallaxObjects.Count; ++i)
            {
                var obj = ParallaxObjects[i];
                obj.ObjectID = i;

                if (GetDistance(obj.transform.position.z, MainCamera.transform.position.z) > maxZ)
                {
                    maxZ = obj.transform.position.z - MainCamera.transform.position.z;
                }

                obj.DeterminePoolAmount(CamBounds);
            }

            // Store the previous random state so we can go back to it after setting our seed in case your game has another seeded random state.
            Random.State oldState = Random.state;
            if (UseSeedForRandomization)
            {
                Random.InitState(RandomSeed);
            }

            // Loop through the parallax objects again
            foreach (var obj in ParallaxObjects)
            {
                // Set the parallax speed for the layer
                obj.ParallaxScalar = GetParallaxScalar(obj);

                // If we're randomizing anything, we create a new history object and initialize the offset values
                if (obj.RandomizeHorizontalDistance || obj.RandomizeVerticalDistance)
                {
                    var newHist = new OSPRandomHistory
                    {
                        ID                   = obj.ObjectID,
                        MaxHistorySize       = obj.MaxPlacementHistorySize,
                        UseRandomX           = obj.RandomizeHorizontalDistance,
                        UseRandomY           = obj.RandomizeVerticalDistance,
                        UseRandomHorizontalY = obj.RandomizeHorizontalYAxis,
                        UseRandomVerticalX   = obj.RandomizeVerticalXAxis,
                        MinXOffset           = obj.HorizontalDistanceMin,
                        MaxXOffset           = obj.HorizontalDistanceMax,
                        MinYOffset           = obj.VerticalDistanceMin,
                        MaxYOffset           = obj.VerticalDistanceMax,
                        MinVerticalXOffset   = obj.VerticalXMin,
                        MaxVerticalXOffset   = obj.VerticalXMax,
                        MinHorizontalYOffset = obj.HorizontalYMin,
                        MaxHorizontalYOffset = obj.HorizontalYMax
                    };

                    // Only initialize the offset lists if we're actually storing history
                    if (obj.UseRandomHistory)
                    {
                        newHist.InitializeOffsetHistory();
                    }

                    PlacementHistory.Add(newHist);
                }

                // Create object pools for each layer and add them to the pool list
                if (obj.AutoTileX || obj.AutoTileY)
                {
                    var pool = new OSPPool(obj);
                    pool.CanGrow = false;
                    ParallaxPools.Add(pool);
                }
            }

            // Restore the original random state
            Random.state = oldState;

            // Remove the pooled objects from the parallax list since we don't need to perform the same functionality on tiled and non-tiled objects
            foreach (var pool in ParallaxPools)
            {
                ParallaxObjects.Remove(pool.PooledObject);
            }
        }
        /// <summary>
        /// Creates a new tile in the given direction.
        /// </summary>
        /// <param name="tile">The parallax tile object.</param>
        /// <param name="direction">The direction we're creating a new tile in.</param>
        /// <param name="historyList">The history list for that layer.</param>
        void CreateNewTile(OSPObject tile, TileDirection direction, OSPRandomHistory historyList)
        {
            // Grab the object pool for this layer
            OSPPool pool = ParallaxPools.SingleOrDefault(p => p.ObjectID == tile.ObjectID);

            // Sometimes, a large image will have a slight gap that shows up on camera movement. This isn't the best fix, but it works.
            float gapOffset = 0.05f;

            // Figure out the scale of the sprite so we can figure out how far to move it in order to tile
            float spriteScaleX = tile.transform.localScale.x;

            if (tile.transform.parent != null)
            {
                spriteScaleX *= tile.transform.parent.localScale.x;
            }

            float spriteScaleY = tile.transform.localScale.y;

            if (tile.transform.parent != null)
            {
                spriteScaleY *= tile.transform.parent.localScale.y;
            }

            if (direction == TileDirection.LEFT || direction == TileDirection.RIGHT)
            {
                #region Left/Right Tiles

                float posOffset = (tile.SpriteWidth * (direction == TileDirection.LEFT ? -1 : 1) * spriteScaleX);
                gapOffset *= direction == TileDirection.LEFT ? 1 : -1;

                Vector3 newPosition = new Vector3(
                    tile.transform.position.x + posOffset + gapOffset,
                    tile.transform.position.y,
                    tile.transform.position.z);

                if (direction == TileDirection.RIGHT)
                {
                    // If we're randomizing the horizontal distance, get the offset and add it to the position
                    if (tile.RandomizeHorizontalDistance)
                    {
                        newPosition.x += historyList.GetXOffsetForColumn(tile.col, tile.UseRandomHistory);

                        // If we're randomizing the Y offset, get the offset and add it to the position
                        if (tile.RandomizeHorizontalYAxis)
                        {
                            newPosition.y += historyList.GetYOffsetForColumn(tile.col, tile.UseRandomHistory);
                        }
                    }
                    else
                    {
                        newPosition.x += tile.HorizontalDistance;
                    }
                }
                else if (direction == TileDirection.LEFT)
                {
                    // If we're randomizing the horizontal distance, get the offset and add it to the position
                    if (tile.RandomizeHorizontalDistance)
                    {
                        newPosition.x -= historyList.GetXOffsetForColumn(tile.col - 1, tile.UseRandomHistory);

                        // If we're randomizing the Y offset, get the offset and add it to the position
                        if (tile.RandomizeHorizontalYAxis)
                        {
                            newPosition.y += historyList.GetYOffsetForColumn(tile.col - 1, tile.UseRandomHistory);
                        }
                    }
                    else
                    {
                        newPosition.x -= tile.HorizontalDistance;
                    }
                }

                // Get the new tile from the object pool. Abandon ship if it couldn't get a tile.
                var newTile = pool.GetPooledObject();
                if (newTile == null)
                {
                    return;
                }

                newTile.transform.position = newPosition;
                newTile.transform.rotation = tile.transform.rotation;

                // Set the new column for this tile
                if (direction == TileDirection.RIGHT)
                {
                    newTile.row = tile.row;
                    newTile.col = tile.col + 1;
                }
                else
                {
                    newTile.row = tile.row;
                    newTile.col = tile.col - 1;
                }

                // Set the left, right, top, and bottom tiles that connect to this one
                pool.SetSiblings(newTile);

                // Now that we're in position, set this tile active
                newTile.gameObject.SetActive(true);

                #endregion
            }
            else
            {
                #region Up/Down Tiles

                float posOffset = (tile.SpriteHeight * (direction == TileDirection.DOWN ? -1 : 1) * spriteScaleY);
                gapOffset *= direction == TileDirection.DOWN ? 1 : -1;

                Vector3 newPosition = new Vector3(
                    tile.transform.position.x,
                    tile.transform.position.y + posOffset + gapOffset,
                    tile.transform.position.z);

                if (direction == TileDirection.UP)
                {
                    // If we're randomizing the vertical distance, get the offset and add it to the position
                    if (tile.RandomizeVerticalDistance)
                    {
                        newPosition.y += historyList.GetYOffsetForRow(tile.row, tile.UseRandomHistory);

                        // If we're randomizing the X offset, get the offset and add it to the position
                        if (tile.RandomizeVerticalXAxis)
                        {
                            newPosition.x += historyList.GetXOffsetForRow(tile.row, tile.UseRandomHistory);
                        }
                    }
                    else
                    {
                        newPosition.y += tile.VerticalDistance;
                    }
                }
                else if (direction == TileDirection.DOWN)
                {
                    // If we're randomizing the vertical distance, get the offset and add it to the position
                    if (tile.RandomizeVerticalDistance)
                    {
                        newPosition.y -= historyList.GetYOffsetForRow(tile.row - 1, tile.UseRandomHistory);

                        // If we're randomizing the X offset, get the offset and add it to the position
                        if (tile.RandomizeVerticalXAxis)
                        {
                            newPosition.x += historyList.GetXOffsetForRow(tile.row - 1, tile.UseRandomHistory);
                        }
                    }
                    else
                    {
                        newPosition.y -= tile.VerticalDistance;
                    }
                }

                // Get the new tile from the object pool. Abandon ship if it couldn't get a tile.
                var newTile = pool.GetPooledObject();
                if (newTile == null)
                {
                    return;
                }

                newTile.transform.position = newPosition;
                newTile.transform.rotation = tile.transform.rotation;

                // Set the new row for this tile
                if (direction == TileDirection.DOWN)
                {
                    newTile.row = tile.row + 1;
                    newTile.col = tile.col;
                }
                else
                {
                    newTile.row = tile.row - 1;
                    newTile.col = tile.col;
                }

                // Set the left, right, top, and bottom tiles that connect to this one
                pool.SetSiblings(newTile);

                // Now that we're in position, set this tile active
                newTile.gameObject.SetActive(true);

                #endregion
            }
        }