Quest: Mining Quest
Challenge: Rock N Roll
Feel free to share your solutions, ideas and creations below. If you get stuck, you can find some ideas here for completing this challenge.
Quest: Mining Quest
Challenge: Rock N Roll
Feel free to share your solutions, ideas and creations below. If you get stuck, you can find some ideas here for completing this challenge.
Ended up doing some pretty heavy refactoring, and I didn’t settle on my final solution until after going through multiple challenges in this quest.
Originally, all I did was create a method to instantiate rocks that would be called after each floor tile was instantiated. Before instantiating a rock, I’d “roll a dice” using Random.Range. If the number was higher than a preset threshold, a rock would spawn, otherwise it would return. I’ll paste the code for my final solution into the Food challenge, since that’s where things started to get more interesting.
So, my post is not a Solution to this challenge, but could probably be applied to work(I haven’t attempted yet, but will try it out)…
Inspired by this challenge, I have been experimenting with randomly generating a “level” map, for an idea for a strategy game I have. It took about a day and a half to get working the way I want, but I am really happy with it.
It involves 3 classes, A Tile class, a Terrain Generator class, and a TileMapGenerator class.
The TileMapGenerator
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class TileMapGenerator : MonoBehaviour
{
/// ---------------------------------------
/// Width and Height of the generated map.
/// Note: TO-DO: add Game/Level manager, which will ultimately be responcible for setting level size;
/// ---------------------------------------
[Header("Set Grid Hieght & Width in Inspector")]
[SerializeField] int rows = 10;
[SerializeField] int columns = 10;
/// --------------------------------------
/// Grid Map References and List of generated tiles.
/// --------------------------------------
[Header("References")]
[Header("=Serialized for Debug Reference=")]
[SerializeField] Tile[] levelTiles;
[SerializeField] List<Vector2> spawnPoints;
[SerializeField]Vector2 zeroCell;
public int NumberOfTiles;
/// -------------------------------------
/// Terrain Generator Prefab
/// -------------------------------------
[SerializeField] TerrainGenerator terrainOneGenerator;
[SerializeField] TerrainGenerator terrainTwoGenerator;
[SerializeField] TerrainGenerator terrainThreeGenerator;
[SerializeField] TerrainGenerator terrainFourGenerator;
///-------------------------------------------------------------------------
/// Tile Holders, to orgnize Tiles in the inspector for better organization
///-------------------------------------------------------------------------
GameObject tileHolder;
void Start()
{
//Sets reference to the total amount of tiles in the Level
NumberOfTiles = rows * columns;
//initializes references
levelTiles = new Tile[(rows * columns)];
tileHolder = new GameObject();
tileHolder.name = "Tile Holder";
//Generates Map Tiles
GenerateSpawnPoints();
GenerateTerrainOne();
GenerateTerrainTwo();
GenerateTerrainThree();
GenerateTerrainFour();
}
/// -------------------------------------------------------------------------
/// Creates a grid of Coordinates, which form the foundation of the game map.
/// -------------------------------------------------------------------------
#region Map Cooridinate Generation
///<summary>
/// Creates a grid of Coordinates, which form the foundation of the game map.
///</summary>
public void GenerateSpawnPoints()
{
zeroCell = new Vector2(0,0);
int tileNumber = 0;
for (int z = 0; z < columns; z++)
{
//if z is even, it will instantiate each row, starting at x = 0 to align with HexGrid
if (z % 2 == 0)
{
for (int x = 0; x < rows; x++)
{
tileNumber++;
var tileSpawnpoint = new Vector2((zeroCell.x + x), 0f + (z * .7625f));
spawnPoints.Add(tileSpawnpoint);
}
}
else
// if z is odd will instantiate row at x = .5 to align with HexGrid.
{
for (int y = 0; y < rows; y++)
{
tileNumber++;
var offSetTileSpawnPoint = new Vector2((zeroCell.x + y + .5f), 0f + (z * .7625f));
spawnPoints.Add(offSetTileSpawnPoint);
}
}
}
}
#endregion
/// ------------------------------------------------------------------------------------
/// Uses the attached Terrain Generator Prefabs to generate the Map
/// Note: TO-DO: Switch the TerrainGenerators to an Array/List to add flexibility
/// ------------------------------------------------------------------------------------
#region Terrain Generation
private void GenerateTerrainOne()
{
for (int i = 0; i < NumberOfTiles * .4; i++)
{
terrainOneGenerator.GenerateTerrain(levelTiles, spawnPoints, 1, tileHolder);
}
}
private void GenerateTerrainTwo()
{
for (int i = 0; i < NumberOfTiles * .3; i++)
{
terrainTwoGenerator.GenerateTerrain(levelTiles, spawnPoints, 2, tileHolder);
}
}
private void GenerateTerrainThree()
{
for (int i = 0; i < NumberOfTiles * .2; i++)
{
terrainThreeGenerator.GenerateTerrain(levelTiles, spawnPoints, 3, tileHolder);
}
}
private void GenerateTerrainFour()
{
for (int i = 0; i < NumberOfTiles * .1; i++)
{
terrainFourGenerator.GenerateTerrain(levelTiles, spawnPoints, 4, tileHolder);
}
}
#endregion
}
The TerrainGenerator
using System.Collections.Generic;
using UnityEngine;
public class TerrainGenerator : MonoBehaviour
{
[SerializeField] List<Tile> terrainOnePrefabs;
public int prefabSelection;
public int locationSelection;
/// <summary>
/// Grabs a refence to a tile prefab and spawning location, then checks if the tile at that location
/// has already been set by a previous round of generation, once the program has located an empty cell, it creates a new tile,
/// and adds it to levelTiles array.
/// </summary>
/// <param name="_levelTiles"></param>
/// <param name="spawnPoints"></param>
/// <param name="rotationNumber"></param>
/// <param name="_tileHolder"></param>
public void GenerateTerrain(Tile[] _levelTiles, List<Vector2> spawnPoints, int rotationNumber, GameObject _tileHolder)
{
//Grabs a refence to a tile prefab and a spawning location;
prefabSelection = Random.Range(0, terrainOnePrefabs.Count);
locationSelection = Random.Range(0, _levelTiles.Length);
//Checks if the tile at locationSelection has already been set by a previous round of generation.
while(_levelTiles[locationSelection] != null)
{
locationSelection = Random.Range(0, _levelTiles.Length);
}
// Once the program has located an empty cell, it creates a new tile, and adds it to levelTiles array.
if (_levelTiles[locationSelection] == null)
{
Tile terrainOneTile = Instantiate(terrainOnePrefabs[prefabSelection], spawnPoints[locationSelection], Quaternion.identity, _tileHolder.transform);
terrainOneTile.tileType = terrainOnePrefabs[prefabSelection].tileType;
terrainOneTile.name = $"Tile {locationSelection}" + $"(terrain {rotationNumber})";
terrainOneTile.tileNumber = locationSelection;
_levelTiles[locationSelection] = terrainOneTile;
}
}
}
and the Tile class…
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum TileType { Empty, Plains, Hills, Woodlands, Forest, Mountain, Dirt, Dunes, Marsh, Ocean, Scrublands, Void }
public class Tile : MonoBehaviour
{
/// --------------------------------------------
/// Public Variables;
/// --------------------------------------------
public int tileNumber { get; set; }
public Vector2 spawnPoint { get; set; }
public Tile tilePrefab { get; set; }
public TileType tileType;
/// -------------------------------------------------------------------
/// Clicking on a tile will report relevent information about that tile
/// -------------------------------------------------------------------
private void OnMouseDown()
{
Debug.Log($"({transform.position.x}, {transform.position.y})");
Debug.Log($"{tileType}");
}
}
Overall, I am really happy with the results, its Scalable ( though there are some grid dimensions that that break the equation, and leave Unity hanging, so i still have a few bugs… but it workings really well, atm.
Looks great and I like the fact you made it scalable. I like the helpful comments in the code as well.
So, This is my solution to Generating rocks. There is even probability (works like charm)
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;
private int exitLocation;
void Start()
{
GenerateFloor();
SpawnExit();
SpawnEnemies();
GenerateRock();
}
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 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);
}
}
}