MINING QUEST: ‘Food For Thought’ - Solutions

Quest: Mining Quest
Challenge: Food For Thought

Feel free to share your solutions, ideas and creations below. If you get stuck, you can find some ideas here for completing this challenge.

Hi there,

I started on this quest because I wanted to see if I can handle a two diamond quest and dabble with procedural generation.

It took me a while to see how everything works. On my first attempts, the enemies were poking holes in the map. So the rocks tiles need to be layered over the ground tiles. The other option would probably to change the attack method, but that was not the challenge.

I changed the InstantiateFloorTile method to take in a prefab and array, so I could use it for different cases.

So here is the solution for quest 1/2:

private void GenerateFloor()
    {
        for (int x = 0; x < xSize; x++)
        {
            for (int y = 0; y < ySize; y++)
            {
                // Put down ground tiles everywhere
                InstantiateFloorTile(x, y, groundTilePrefab, groundSprites);

                // Challenge 1: Put rocks on some of them
                if (Random.Range(0f, 100f) < percentRocks)
                {
                    InstantiateFloorTile(x, y, rockTilePrefab, rockSprites);
                } 
                else 
                {
                    // Challenge 2: Put down food on some empty tiles
                    if (Random.Range(0f, 100f) < percentFoodOnEmpty)
                    {
                        InstantiateFloorTile(x, y, foodPrefab, foodSprites);
                    }
                }
            }
        }
    }

I’m going with 50% rocks and 10% food on empty tiles.

1 Like

Here’s what I have so far. Lots of magic numbers that need to be assigned to variables, I think.

private void GenerateFloor()
    {
        for (int x = 0; x < 20; x++)
        {
            for (int y = 0; y < 20; y++)
            {
                InstantiateFloorTile(x, y);
                int randomNumber = Random.Range(0, 100);
                if (randomNumber <= 50)
                {
                    InstantiateRocks(x, y);
                }
                else if (randomNumber >= 90)
                {
                    InstantiateFood(x, y);
                }
                else if (randomNumber >= 85 && randomNumber <= 88)
                {
                    SpawnEnemies(x, y);
                }
            }
        }
    }

@mystic_mill - u may find you end up (often?) with no enemies = no danger and the very off chance of lots/all enemies…

I’m usually averaging around 6 or so enemies. Tbh I wrote this code a while back and haven’t tested it in a while. It’s basically just testing each floor tile to see whether an enemy will spawn there (hence the low percent chance). The biggest problem so far is that the enemies mostly spawn in the left lower quadrant so I need to figure that out. :slight_smile:

1 Like

NM, make that 4 enemies on average.

This is what I ended up with at the end of the challenge. My main action was to create a list of Vector2s that would capture the x/y coordinates of any tile that had already had an item placed on it. This prevents food from dropping on rock, etc. and can scale with however many other items I need to incorporate into the level. I end up using this list when I spawn the enemies later on to ensure that none drop directly on top of a rock or food spawn:

    void Awake() 
    {
        gridLength = Random.Range(gridMin,gridMax);
        playerSpawn = gridLength / 2;
        PlacePlayer();
        blockedCoordinates = new List<Vector2>();
    }
    void Start()
    {
        SetPlayerSafeZone();
        GenerateEnviron();
        SpawnEnemies();
    }

    private void PlacePlayer()
    {
        playerPrefab.transform.position = new Vector2(playerSpawn,playerSpawn);
    }
    private void SetPlayerSafeZone()
    {
        int playerXPosOffset = (int)playerPrefab.transform.position.x -1;
        int playerYPosOffset = (int)playerPrefab.transform.position.y -1;

        for (int x = (int)playerPrefab.transform.position.x - 1; x < playerXPosOffset + 3; x++)
        {
            for (int y = (int)playerPrefab.transform.position.y - 1; y < playerYPosOffset + 3; y++)
            {
                BlockCoordinates(x,y);
            }
        }
    }

    private void BlockCoordinates(int x, int y)
    {
        blockedCoordinates.Add(new Vector2(x,y));
    }

    private bool CanSpawn(int x, int y)
    {
        Vector2 spawnLocation = new Vector2(x,y);
        if(!blockedCoordinates.Contains(spawnLocation))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private void GenerateEnviron()
    {
        SpawnExit();
        for (int x = 0; x < gridLength; x++)
        {
            for (int y = 0; y < gridLength; y++)
            {
                LayFloorTile(x,y);
                RollRockSpawn(x,y);
                RollFoodSpawn(x,y);
            }
        }
    }

    private void LayFloorTile(int x, int y)
    {
        InstantiateEnvironmentObject(x,y,groundTilePrefab,groundSprites);
    }

    private void InstantiateEnvironmentObject(int x, int y, GameObject gameObject, Sprite[] sprite)
    {
        GameObject newObject = Instantiate(gameObject, new Vector2(x,y),Quaternion.identity);
        newObject.transform.parent = this.transform;
        newObject.GetComponent<SpriteRenderer>().sprite = sprite[Random.Range(0,sprite.Length)];
    }

    private void RollRockSpawn(int x, int y)
    {
        if(CanSpawn(x,y))
        {
            int rockBool = Random.Range(0, 101);
            if (rockBool <=rockSpawnChance)
            {
                InstantiateEnvironmentObject(x,y,rockTilePrefab,rockSprites);
                BlockCoordinates(x,y);
            } 
        }
    }
    private void RollFoodSpawn(int x, int y)
    {
        if(CanSpawn(x,y))
        { 
            int foodBool = Random.Range(0, 101);
            if (foodBool <= foodSpawnChance)
            {
                InstantiateEnvironmentObject(x,y,foodPrefab,foodSprites); 
            } 
        }
    }

This is what I made for spawning food i even added text to tell you how much there is.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class GenerateEnvironment : MonoBehaviour
{
    [SerializeField] private GameObject groundTilePrefab;
    [SerializeField] private Sprite[] groundSprites;
    [SerializeField] private GameObject rockTilePrefab;
    [SerializeField] private Sprite[] rockSprites;
    [SerializeField] private GameObject foodPrefab;
    [SerializeField] private Sprite[] foodSprites;
    [SerializeField] private GameObject exitPrefab;
    [SerializeField] private int amountOfEnemies;
    [SerializeField] private GameObject enemyPrefab;

    [SerializeField] private TextMeshProUGUI foodTxt;
    private int exitLocation;


    void Start()
    {
        GenerateFloor();
        SpawnExit();
        SpawnEnemies();
        GenerateRock();
        GenerateFood();
        foodTxt.enabled = true;
        foodTxt.text = "There is " + GameObject.FindGameObjectsWithTag("Food").Length + " Food";

        StartCoroutine("DisableText");
    }

    private void GenerateFloor()
    {
        for (int x = 0; x < 20; x++)
        {
            for (int y = 0; y < 20; y++)
            {
                InstantiateFloorTile(x, y);
            }
        }
    }

    private void GenerateRock()
    {
        for (int x = 0; x < 20; x++)
        {
            for (int y = 0; y < 20; y++)
            {
                if (ShouldSpawnRockTile(x, y))
                {
                    InstantiateRockTile(x, y);
                }
            }
        }
    }

    private void GenerateFood()
    {
        for (int x = 0; x < 20; x++)
        {
            for (int y = 0; y < 20; y++)
            {
                if (IsFreeTile(x, y) && ShouldSpawnFoodTile(x,y))
                {
                    InstantiateFoodTile(x, y);
                }
            }
        }
    }

    private bool IsFreeTile(int x, int y)
    {
        Collider2D[] colliders = Physics2D.OverlapPointAll(new Vector2(x, y));
        foreach (Collider2D collider in colliders)
        {
            if (collider.CompareTag("Rock") || collider.CompareTag("Food"))
            {
                return false; // Tile is occupied by a rock or food object
            }
        }
        return true; // Tile is free
    }

    private void InstantiateFoodTile(int x, int y)
    {
        GameObject newFoodTile = Instantiate(foodPrefab, new Vector2(x, y), Quaternion.identity);
        newFoodTile.GetComponent<SpriteRenderer>().sprite = foodSprites[Random.Range(0, foodSprites.Length)];
    }
    private bool ShouldSpawnFoodTile(int x, int y)
    {

        float foodSpawnProbability = Random.Range(0.15f, 0.35f);


        return Random.value < foodSpawnProbability;
    }

    private bool ShouldSpawnRockTile(int x, int y)
    {
        
        float rockSpawnProbability = Random.Range(0.55f,0.88f);

        
        return Random.value < rockSpawnProbability;
    }

    private void InstantiateRockTile(int x, int y)
    {
        GameObject newRockTile = Instantiate(rockTilePrefab, new Vector2(x, y), Quaternion.identity);
        newRockTile.GetComponent<SpriteRenderer>().sprite = rockSprites[Random.Range(0, rockSprites.Length)];
    }

    private void InstantiateFloorTile(int x, int y)
    {
        GameObject newFloorTile = Instantiate(groundTilePrefab, new Vector2(x, y), Quaternion.identity);
        newFloorTile.GetComponent<SpriteRenderer>().sprite = groundSprites[Random.Range(0, groundSprites.Length)];
    }

    private void SpawnExit()
    {
        // Challenge 2
    }

    private void SpawnEnemies()
    {
        for (int i = 0; i < amountOfEnemies; i++)
        {
            Vector2 spawnLocation = new Vector2(Random.Range(0, 19), Random.Range(0, 19));
            GameObject newEnemy = Instantiate(enemyPrefab, spawnLocation, Quaternion.identity);
        }
    }

    IEnumerator DisableText()
    {
        yield return new WaitForSeconds(5f);
        foodTxt.enabled = false;
    }
}

Privacy & Terms