im creating a game where the player needs to find and pick up a certain number of keys. Currently ive got a spawn container that generates 5 keys in given locations. I want to be able to generate the 5 keys in 5 random locations. So lets say i have 10 spawn points, i want it to randomly pick any of the 5 points and place a key there.
I have the following code so far
using UnityEngine;
using System.Collections;
public class KeySpawnManager : MonoBehaviour
{
// array to store spawnpoints
private Transform[] spawnTransformList;
// integer to store number of spawnpoints
private int numberOfSpawnpoints;
// the prefab we're going to spawn
public GameObject prefab;
private int collectedCount = 0;
private int currentTime = 0;
// Singleton Instance
public static KeySpawnManager Instance { get; private set; }
// AWAKE Function - fired on initialization
void Awake ()
{
if (Instance == null) Instance = this;
else Destroy( gameObject );
numberOfSpawnpoints = transform.childCount;
spawnTransformList = new Transform[numberOfSpawnpoints];
for (int i = 0; i < numberOfSpawnpoints; i++)
{
// add the spawn to the array
spawnTransformList[i] = transform.GetChild(i); // return transform Component of each child object
}
for (int j = 0; j < numberOfSpawnpoints; j++)
{
GameObject newPrefab = (GameObject) Instantiate(prefab, spawnTransformList[j].position, spawnTransformList[j].rotation);
newPrefab.transform.parent = transform.position;
}
}
}
Any ideas on how to do this?
thanks
create your ten empty gameobject in a list then shuffle the list using Fisher-Yates shuffle then use the first five elements of the list and spawn keys in them
Here is the implementation of a Fisher-Yates shuffle in c#
Create a random method using random class that you can pass in the total number of spawn points. Have it return then index for your spawn location.
Related
Context
Hello, currently creating a clone of "Crossy Road" and what I'm trying to do is to spawn my moving object called "Vehicle" at a random speed and rate of spawn. This is also applicable to "Plank", but I will start first with the vehicle. So far, everything is working fine as intended for the game mechanics, but I would like to finalize with this issue so it is fully functional in terms of playability.
Problem
My issue now is I 3 different spawns objects: grass, river, and road. Each object holds other objects (let's call it spawners) depending of what field is being spawn. For example, if grass field object is spawned, it will spawn trees depending in a random varied selection. Another example is with road field. When the road is spawned, a vehicle will be spawned from either left or right in its current initial position. This vehicle will moves as intended with a random speed, but not with the original spawn position and rate (as shown in the GIF. The vehicle spawns in the middle of the road and not in the beginning of the left/right road).
As far I'm aware, my rate is currently unused because it is not the main issue I want to solve. However, the issue now is with the transform position not working as I have pictured in my head. So what is happening is that when the road is spawned again, the vehicle is spawned in the middle of the trajectory instead of resetting to the beginning.
Also, I have noticed that when I print the vehicle object, the Z-axis has a weird number compared to the original position.
Attempts done
I have been thinking that maybe it is the way I have set everything up. I have 4 vehicle objects with a child object called "Tank". However, in each vehicle object, I'm using SetActive(...) only and not really reusing the object itself to the beginning. Later on, I want to organize this spaghetti code and optimize it (e.g ObjectPool to spawn my roads and other GameObjects after hitting a certain range, adding a player range detection to spawn a field to name a few).
To be honest, my whole code feels bloated for something simple. This will be fixed once everything is working accordingly.
Code (DISCLAIMER: there is the possibility that there are unused variables)
SpawnManager.cs (some links provided too from learning to make this)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*
** Weighted randomness: https://forum.unity.com/threads/random-numbers-with-a-weighted-chance.442190/
** Scriptable Object Weight spawn example: https://www.youtube.com/watch?v=FCksj9ofUgI&ab_channel=LlamAcademy
** From scratch loot tables with Scriptable Objects to make a loot table: https://www.youtube.com/watch?v=tX3RWsVLnzM&ab_channel=GregDevStuff
** Creating a random with an animation curve: https://www.youtube.com/watch?v=zw1OERK5xvU&ab_channel=HamzaHerbou
** Random Vehicle position spawn (maybe this can help me): https://stackoverflow.com/questions/51312481/move-spawn-object-to-random-position
*/
public class SpawnManager : MonoBehaviour
{
public GameObject Player;
public Spawn[] Field;
public GameObject[] SpawnObjectTrees;
public GameObject[] SpawnObjectVehicles; //different vehicles
public GameObject[] SpawnObjectPlanks; //3 sizes (small, medium, large)
private PlayerControl2 playerControlScript;
private int distancePlayer;
private int toggle;
private bool keepSpawning;
bool vehicleFlag = false;
bool plankFlag = false;
public float randomNumSpawn;
void Awake()
{
keepSpawning = true;
playerControlScript = GameObject.Find("PlayerObject").GetComponent<PlayerControl2>();
InvokeRepeating("Spawner", 3f, randomNumSpawn);
}
void Update()
{
if (Input.GetButtonDown("up") && !playerControlScript.gameOver)
SpawnField();
}
void Spawner()
{
bool activeLeft = false;
bool activeRight = false;
if (vehicleFlag)
{
print(initialObjectSpawn);
for (int i = 0; i < SpawnObjectVehicles.Length; i++)
{
print($"{SpawnObjectVehicles[i]}: {SpawnObjectVehicles[i].transform.position}"); //Here I get the weird position.z values pretty wonky
toggle = Random.Range(0, 2);
if (toggle == 1 && !activeLeft)
{
activeLeft = true;
SpawnObjectVehicles[i].SetActive(true);
}
if (toggle == 0 && !activeRight)
{
activeRight = true;
SpawnObjectVehicles[i].SetActive(true);
}
else
SpawnObjectVehicles[i].SetActive(false);
}
}
}
void SpawnField()
{
//I want to spawn the vehicles, planks, and trees in sets accordingly to the field (grass, river, road)
//For vehicles and planks, they can move horizontally from either -z or z boundaries
//NOTE: keepSpawning may be useless if i have a playerControlScript.gameOver already in here
if (keepSpawning)
{
distancePlayer += 3;
Vector3 intPos = new Vector3(0, 0, 0);
int i = Random.Range(0, 1000);
for (int j = 0; j < Field.Length; j++)
{
if (i >= Field[j].minProbabilityRange && i <= Field[j].maxProbabilityRange)
{
intPos = new Vector3(distancePlayer, -1f, 0);
GameObject Surface = Instantiate(Field[j].spawnField);
if (Surface.CompareTag("Grass"))
TreeToggle();
if (Surface.CompareTag("Road"))
{
vehicleFlag = true;
VehicleToggle();
}
// if (Surface.CompareTag("River")) this will be the same as vehicle
// {
// plankFlag = true;
// PlankToggle();
// }
//Add spawn for vehicles and planks with given spawnrate/spawn intervals
Surface.transform.position = intPos;
vehicleFlag = false;
plankFlag = false;
}
}
}
}
void TreeToggle()
{
int counter = 0;
for (int i = 0; i < SpawnObjectTrees.Length; i++)
{
int toggle = Random.Range(0, 2); //[0, 2)
if (toggle == 1 && counter < 5) //True and when there are already 5-4 trees to toggle
{
counter++;
SpawnObjectTrees[i].SetActive(true);
}
else //fills the rest to inactive Trees
SpawnObjectTrees[i].SetActive(false);
}
}
void VehicleToggle()
{
// I have Left and Right with 2 vehicles in each. My goal is to setActive one of them each side at a time with a different interval spawnrate and speed
Spawner();
}
void PlankToggle()
{
Spawner();
}
}
[System.Serializable]
public class Spawn
{
public GameObject spawnField;
public float minProbabilityRange = 0.0f;
public float maxProbabilityRange = 0.0f;
}
Hierarchy/Inspector
If there is any information you want to know, feel free to ask and I will make a quick edit to fulfill these goals. Again, thank you for your time and appreciate it :D I hope you are having a good day!
I am attempting to implement some basic functionality in Unity using this script attached to an empty which has 6 child objects. I have tried various fixes in my code, but cant seem to achieve the desired result.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectSelector : MonoBehaviour
{
// This array stores the Prefabs
public GameObject[] ObjectArray;
// Integer for stepping through array Prefabs
private int selectedObject = 0;
// Location to instantiate the prefab
private Transform locator;
// Holds the currently instantiated Prefab in the scene
private GameObject tempMesh;
void Start()
{
// Instantiate initial Prefab
tempMesh = Instantiate(ObjectArray[selectedObject], locator.position, locator.rotation);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow))
{
// Step to next position in array
ObjectArray++;
// Remove previously instantiated prefab from hierarchy
Destroy(tempMesh);
// Instantiated next Prefab
tempMesh = Instantiate(ObjectArray[selectedObject], locator.position, locator.rotation);
// Print Counter for debug
print(ObjectArray);
// If Array count excedes Array index then go back to first position in array
if (ObjectArray >= ObjectArray.Length)
{
ObjectArray = 0;
}
}
}
}
You are incrementing an array, you need to increment your counter
void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow))
{
// Step to next position in array
selectedObject++;
// If Array count exceeds Array index then go back to first position in array
if (selectedObject >= ObjectArray.Length)
{
selectedObject = 0;
}
// Remove previously instantiated prefab from hierarchy
Destroy(tempMesh);
// Instantiated next Prefab
tempMesh = Instantiate(ObjectArray[selectedObject], locator.position, locator.rotation);
// Print Counter for debug
print(selectedObject);
}
}
I'm stuck with this one for quite some time now. I am trying to create spheres via script and update their position based on the position of points. Their position is updating on the Debug.Log() but they are not moving in Game View.
Here is my code:
void createSpheres(int objCount, float xPointsPos, float yPointsPos){
var sphereCreator = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphereCreator.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
sphereCreator.transform.position = new Vector3(xPointsPos, yPointsPos, 0);
sphereCreator.AddComponent<Rigidbody>();
sphereCreator.GetComponent<Rigidbody>().useGravity = false;
sphereCreator.AddComponent<SphereCollider>();
//ADD THE SPHERES TO THE SPHERELIST
sphereList = new List<Sphere>();
for(int loop = 0; loop < objCount-1; loop++){
Sphere temp = new Sphere();
temp.sphereName = "sphere"+sphereNameCount;
temp.sphereObj = sphereCreator;
temp.sphereXPos = xPointsPos;
temp.sphereYPos = yPointsPos;
sphereList.Add(temp);
}
sphereNameCount++;
}
void UpdateSpheres()
{
for(int i = 0; i < sphereList.Count - 1; i++){
sphereList[i].sphereXPos = points[i].position.x;
sphereList[i].sphereYPos = points[i].position.y;
Debug.Log($"{sphereList[i].sphereXPos}" + " -- " + $"{points[i].position.x}");
}
}
public class Sphere{
public string sphereName;
public float sphereXPos;
public float sphereYPos;
public GameObject sphereObj;
}
The createSpheres() method is called inside a loop containing how many points are spawned to match it.
I also tried checking if the ArrayList is empty or not using Debug.Log() and it returned all the Sphere gameObjects that I added.
Any help or hint will be highly appreciated. Thanks!
You do not create a number of spheres according to the code. You create just a single sphere and assign it to all your Sphere instances. To create and move your spheres:
create GameObject object for every object instead of assigning the same object for each Sphere class instance
use .transform.position of the created object, assigned to Sphere class instance to move the corresponding GameObject instance
I bought astarpathfinding project pro recently.
I am making enemy ai and I want it to move randomly before it finds its target.
My project is in 2d.
How do I pick a random position on the grid?
if you can, can you show me some example of it?
Not really sure about the plugin that you bought, but If you are using tilemaps, you could iterate over every tile in each tilemap in your grid(only once on level loading) and generate a random number of x and y to choose a valid tile position from and make the Ai move there using the A* algorithm.
Edit: May not be the best way to write this, but this is how I iterated over them in the past, you can add or change things in it for your future needs if you want.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Tilemaps;
public class TileMapCollision : MonoBehaviour
{
[SerializeField] private GameObject gridObject;
private TileBase tile;
public List<Vector3> StoredStaticSolidTilePositions;
private void Awake()
{
TileSearch();
}
private void TileSearch()
{
List<Tilemap> tilemaps = gridObject.GetComponentsInChildren<Tilemap>().ToList();
foreach (Tilemap tilemap in tilemaps)
{
tilemap.CompressBounds();
BoundsInt TilemapBounds = tilemap.cellBounds;
for (int x = TilemapBounds.xMin; x < TilemapBounds.xMax; x++)
{
for (int y = TilemapBounds.yMin; y < TilemapBounds.yMax; y++)
{
Vector3Int localPos = new Vector3Int(x,y, (int)tilemap.transform.position.z);
Vector3 worldPos = tilemap.CellToWorld(localPos);
if (tilemap.HasTile(localPos))
{
if (tilemap.CompareTag("Solid"))
{
StoredStaticSolidTilePositions.Add(worldPos);
}
}
}
}
}
}
}
I don't know what tool you are using or how exactly your "grid" looks like but from the comments it sounds like you have a TileMap.
In general for picking a random number from a value there is Random.Range. (Note that the second (upper) parameter is exclusive for int.)
Then you can get TileMap.size and simply generate a position from three random indices within the TileMap's dimensions
var gridSize = tilemap.size;
var randomPos = new Vector3Int(Random.Range(0, gridSize.x), Random.Range(0, gridSize.y), Random.Range(0, gridSize.z));
var randomTile = tilemap.GetTile(randomPos);
I have game for multiple players where each user selects their hero before game starts and that loads the selected heroes into the battle arena.
I have small issue with getting the instantiation to spawn in correct numbers of players
The method that I have for Spawning the characters:
private void Placement()
{
for (int i = 0; i < SelectedCards.Count; i++)
{
for (int t = 0; t < AvailableHeroes.Count; t++)
{
if (AvailableHeroes[t].name == SelectedCards[i].name)
{
Debug.Log(AvailableHeroes[t]);
// Instantiate(AvailableHeroes[t], PlayerSpawnLocation[t].transform.position, transform.rotation);
}
{
}
}
}
}
This script checks for amount of selected hero cards and puts it against my list that has all the available heroes to choose from(prefabs).
The debug.log shows that only the correct heroes get called.
Instantiate ends up spawning a loot of heroes instead of the selected amount.
For clarity I attach full class:
{
private int playerSize; //amount of choices for card selection
private GameManager GM;
[Header("Lists for Spawning in Heroes")]
public List<GameObject> SelectedCards;
public List<GameObject> AvailableHeroes;
public List<Transform> PlayerSpawnLocation;
[Header("Canvas used for the game")]
public Transform GameCanvas;
public Transform CharacterCanvas;
//When scene starts it takes how many players will be picking a card.
void Start()
{
//connects this script with gamenmanager to be able to manipulate the cameras
GM = GameObject.Find("GameManager").GetComponent<GameManager>();
//gets playersize information from main menu selection
PlayerPrefs.GetInt("PlayerSize");
playerSize = PlayerPrefs.GetInt("PlayerSize");
SelectedCards = new List<GameObject>();
//enables/disables correct canvas not to cause any problems when we initiate this scene
GameCanvas.gameObject.SetActive(false);
CharacterCanvas.gameObject.SetActive(true);
}
// Update is called once per frame
void Update()
{
if (playerSize <= 0)
{
Placement();
GM.CharacterSelectionCamera.enabled = false;
GameCanvas.gameObject.SetActive(true);
CharacterCanvas.gameObject.SetActive(false);
GM.BattleCamera.enabled = true;
}
}
public void PlayerSelected(int cardPicked)
{
playerSize -= cardPicked;
}
private void Placement()
{
for (int i = 0; i < SelectedCards.Count; i++)
{
for (int t = 0; t < AvailableHeroes.Count; t++)
{
if (AvailableHeroes[t].name == SelectedCards[i].name)
{
Debug.Log(AvailableHeroes[t]);
// Instantiate(AvailableHeroes[t], PlayerSpawnLocation[t].transform.position, transform.rotation);
}
{
}
}
}
}
}
I hope someone can explain where I am going wrong with this.
Thanks,
I got the answer, I guess I was just being tired from working and could not see the obvious.
For those who wonder what solution is
The method gets called each frame thus it continues to endlessly spawn objects
There are 2 ways to fix it
1 Make coroutine and then return after you make your initial batch
2 Use a boolean at update so not only it checks player size but also whenever it can spawn it or not, you set the boolean to false after method get called.
I did not even notice the update function part.
Just a heads up, in your start function, PlayerPrefs.GetInt("PlayerSize"); is not doing anything since the value is not saved anywhere.