Instantiate 4 prefabs at 4 locations, shuffled, Unity Engine C# - c#

What I have are 4 platforms that I instantiate at 4 locations. What I want is for the platforms to be shuffled every time. My code so far:
using UnityEngine;
public class PlatformCreator : MonoBehaviour {
public GameObject[] platforms;
public Transform[] points;
private void Start()
{
for (int i = 0; i < points.Length; i++)
{
Instantiate(platforms[i], points[i].position, Quaternion.identity);
}
}
}
For example the platforms now always spawn in the same order - pink, yellow, blue, purple
I want them to be spawned in different order every time, for example - yellow, blue, purple, pink. I've tried creating an int index with random.range, but I am messing something up

You could add the points to a List instead of an array, which will help you to "shuffle" the values. Taking the shuffle function from This SO post, you could do something like this:
public class PlatformCreator : MonoBehaviour {
public GameObject[] platforms;
public List<Transform> points;
private Random rng;
public void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
private void Start()
{
rng = new Random();
points.Shuffle();
for (int i = 0; i < points.Count; i++)
{
Instantiate(platforms[i], points[i].position, Quaternion.identity);
}
}
}

Related

Unable to reference 2D array of game objects in unity

I'm creating chess in unity, and I want to create the chess board using a 2D array of cells. I was able to create an array of pieces and could reference them with their respective prefabs in Unity. But I have no option to reference the 2D array.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class gameManager : MonoBehaviour
{
public GameObject[,] cell = new GameObject[7,7];
public GameObject[] Wpawn = new GameObject[7];
public GameObject[] Bpawn = new GameObject[7];
public GameObject[] Wrook = new GameObject[1];
public GameObject[] Brook = new GameObject[1];
public GameObject[] Wknight = new GameObject[1];
public GameObject[] Bknight = new GameObject[1];
public GameObject[] Wbishop = new GameObject[1];
public GameObject[] Bbishop = new GameObject[1];
public GameObject Wking;
public GameObject Bking;
public GameObject Wqueen;
public GameObject Bqueen;
bool[] Bpawn2 = new bool[7];
bool[] Wpawn2 = new bool[7];
short turn = 0;
// Start is called before the first frame update
void Start()
{
#region Board
for (float y = 0F; y <= 7; y+= 1)
{
for (float x = 0F; x <= 7F; x+= 1)
{
if (y % 2 == 0)
{
if (x % 2 == 0)
{
cell[(int)x,(int)y].GetComponent<SpriteRenderer>().color = Color.gray;
} else if (x % 2 == 1)
{
cell[(int)x,(int)y].GetComponent<SpriteRenderer>().color = Color.white;
}
} else if (y % 2 == 1)
{
if (x % 2 == 0)
{
cell[(int)x,(int)y].GetComponent<SpriteRenderer>().color = Color.white;
} else if (x % 2 == 1)
{
cell[(int)x,(int)y].GetComponent<SpriteRenderer>().color = Color.gray;
}
}
Instantiate(cell[(int)x,(int)y], new Vector3(x - 3.5F, y -3.5F), transform.rotation);
}
}
#endregion
}
}
As you can see I've properly initialized my 2D array. I only showed you the setboard Region because there were no problems in the rest.
Here is the gamemanager script component and what it is showing me.
There is no option to reference the Cell Prefab
TL;DR
The default Inspector can only show variables that are Serializable by Unity's Serializer, i.e. files which can be written-to and read-from a file by Unity for saving/loading purposes. 2D arrays are not Serializable, thus they're not visible in the Inspector.
Please familiarize yourself with the Serialization rules in Unity. Here's an answer about what Serialization is.
Alternative
Even List<List> or T[][] type 2D arrays are not Serializable. This has to do with limitations regarding generic serialization in Unity. A simple alternative to define a concrete class as follows:
[Serializable]
public class GameObjectList //not a monobehavior
{
public List<GameObject> gameObjects;
}
using it later on as field
public GameObjectList[] pieces;
This should show up a list of lists in the Inspector, letting you reference your GameObjects. Of course, this method is a bit cumbersome, having to define a new class every time, but it works.
Notes:
A lot of the limitations surrounding generic serialization have been lifted in Unity 2020.2 update, but you still can't serialize 2D arrays/lists directly.

How to transfer information between objects in Unity C#?

I'm new to unity and component-based programming. I'm not doing anything visual yet (animation, graphics, etc.) just the logic.
I'm creating a unique card game as a hobby and need some help with lists. I created a PlayerDeck class for the PlayerDeck Game Object in the Hierarchy that loads, shuffles, and deals cards from a database to four players. Here's the code:
public class PlayerDeck : MonoBehaviour
{
public int d;
public int w;
public int x;
public int y;
public int z;
public List<Card> deck;
public List<Card> container;
public List<Card> player1hand;
public List<Card> player2hand;
public List<Card> player3hand;
public List<Card> player4hand;
public List<Card> distributed;
public List<Card> discarded;
public int decksize;
void Start()
{
LoadVariables();
LoadDeck();
ShuffleDeck();
DealCards();
}
public void LoadVariables()
{
d = 0;
w = 0;
x = 0;
y = 0;
z = 0;
decksize = 64;
}
public void LoadDeck()
{
for (int i = 0; i < 64; i++)
{
d = i;
deck[d] = CardDatabase.cardList;
}
}
public void ShuffleDeck()
{
for (int i=0; i<decksize; i++)
{
container[0] = deck;
int rnd = Random.Range(i, decksize);
deck = deck[rnd];
deck[rnd] = container[0];
}
}
public void DealCards()
{
// deal to player one
for (int i = 0; i < 7; i++)
{
w = i;
player1hand[w] = deck;
distributed = player1hand[w];
deck.Remove(deck);
}
//deal to player two
for (int i=7; i < 14; i++)
{
player2hand[x] = deck;
distributed = player2hand[x];
deck.Remove(deck);
x++;
}
//deal to player three
for (int i = 14; i < 21; i++)
{
player3hand[y] = deck;
distributed = player3hand[y];
deck.Remove(deck);
y++;
}
//deal to player four
for (int i = 21; i < 28; i++)
{
player4hand[z] = deck;
distributed = player4hand[z];
deck.Remove(deck);
z++;
}
}
}
So far the logic seems legit. I debugged it and I can see each player getting the right cards from the deck.
Now, I created a Game Object called Player1 and wanted to create a class where Player1 has access to the player1hand list in the Player Deck. Basically, how can I transfer this info to Player1 in code? Here's what I did but I get blanks for Player 1:
[RequireComponent(typeof (PlayerDeck))]
public class PlayerHand : MonoBehaviour
{
PlayerDeck playerhand;
public List<Card> dealthand;
public int a;
private void Start()
{
playerhand = GetComponent<PlayerDeck>();
for (int i = 0; i < 7; i++)
{
a = 0;
dealthand[a] = playerhand.player1hand;
a++;
}
}
Any ideas? Any help will be appreciated.
Your design doesn't make much sense. Why does the deck own the cards in the hands of the players? Let the hands manage their own cards, and the deck should just tell the hands to take the cards and do whatever they do with them.
I would add a method to PlayerHand called Add which takes a Card and adds it to their hand.
Each PlayerHand should be on a different gameobject. Also, your example code makes it seem like every player draws from the same deck and discards to the same pile, so there is no need for requiring a PlayerDeck on the same component as PlayerHand
In case the hand needs a reference to the deck at some other point, also add a method called RegisterDeck to give the deck reference to the PlayerHand.
public class PlayerHand : MonoBehaviour
{
[SerializeField] List<Card> dealtHand;
[SerializeField] PlayerDeck myDeck;
private void Awake()
{
dealtHand = new List<Card>();
}
public void Add(Card addedCard)
{
dealtHand.Add(addedCard);
}
public void RegisterDeck(PlayerDeck myDeck)
{
this.myDeck = myDeck;
}
}
Then in PlayerDeck, replace your playerXhand fields with a single List<PlayerHand> playerHands field, and then deal to those players using AddToHand. I'm not sure what you were trying to do by assigning a List as an item in a List like player1hand[w] = deck; but just remove cards from the top of the list.:
public class PlayerDeck : MonoBehaviour
{
// ...
[SerializeField] List<PlayerHand> playerHands;
// ...
public void DealCards()
{
// deal 7 to each player
foreach (PlayerHand hand in playerHands)
{
hand.RegisterDeck(this);
for (int i = 0; i < 7; i++)
{
Debug.Assert(deck.Count > 0, "Ran out of cards while dealing");
Card drawnCard = deck[0];
deck.RemoveAt(0);
hand.Add(drawnCard);
}
}
}
}
Then, in the editor, I would specify the length of Player Hands to be 4, and drag the gameObjects that each PlayerHand is attached to into that list so that PlayerDeck can get to them. Each PlayerHand should definitely be in a different gameObject, by the way.

Instantiate multiple prefabs

I am making a ball game, So the ball pass the columns and it instantiate itself base on the columnPoolSize,
I want to intantiate multiple prefab in here. It only intantiate one prefabs base on the column size...I need to create another array for columnPrefab GameObject. However I tried to do that but it did not success...
public class ColumbPool : MonoBehaviour
{
public int columnPoolSize = 5;
public GameObject[] columns;
public GameObject columnPrefab;
private Vector2 objectPoolPosition = new Vector2(-15f,-25f);
private float timeSinceLastSpawn;
public float spawnRate = 4f;
public float columnMin = -1f;
public float columnMax = 3.5f;
private float spawnXPosition = 10f;
private int currentColumn = 0;
void Start()
{
columns = new GameObject[columnPoolSize];
for (int i = 0; i < columnPoolSize; i++)
{
columns[i] = (GameObject)Instantiate(columnPrefab, objectPoolPosition, Quaternion.identity);
}
}
void Update()
{
timeSinceLastSpawn += Time.deltaTime;
if (GameController.instance.gameOver==false && timeSinceLastSpawn>=spawnRate)
{
timeSinceLastSpawn = 0;
float spawnYPosition = Random.Range(columnMin, columnMax);
columns[currentColumn].transform.position = new Vector2(spawnXPosition,spawnYPosition);
currentColumn++;
if (currentColumn>=columnPoolSize)
{
currentColumn = 0;
}
}
}
}
void Start()
{
columns = new GameObject[columnPoolSize];
for (int i = 0; i < columnPoolSize; i++)
{
columns[i] = (GameObject)Instantiate(columnPrefab, objectPoolPosition, Quaternion.identity);
}
}
becomes
private GameObject[] instantiatedColumns;
public GameObject[] columnPrefabs;
void Start()
{
instantiatedColumns= new GameObject[columnPrefabs.Length];
for (int i = 0; i < columnPrefabs.Length; i++)
{
instantiatedColumns[i] = Instantiate(columnPrefabs[i], objectPoolPosition, Quaternion.identity);
}
}
This way you will instantiate each prefab in prefab columns array (populated in the inspector) and save references into a new array "instantiatedColumns", then you may use this array in Update()

How to structure terrain generator in unity?

I am learning C# with Unity and I am trying to create terrain generator. I made a class called Terrain which contains an array of class called Chunk. Chunk should be an array of square GameObjects.
The code looks like this:
public class Terrain : MonoBehaviour {
public Chunk[] terrain;
// Use this for initialization
void Start () {
terrain[0] = new Chunk(0, 0);
}
}
and class Chunk looks like this:
public class Chunk : MonoBehaviour {
public int size;
public GameObject tile;
private GameObject[] chunk;
private int xCoord, yCoord;
public void Create(int chunkX, int chunkY){
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
int xCoord = x + chunkX*size;
int yCoord = y + chunkY*size;
chunk[x + y*size] = GameObject.Instantiate(tile, new Vector3(xCoord, yCoord), Quaternion.identity);
x = chunkX;
y = chunkY;
}
}
}
//Constructor
public Chunk(int chunkX, int chunkY) {
xCoord = chunkX;
yCoord = chunkY;
}
}
I get 1 error and 1 warning:
You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent().
IndexOutOfRangeException: Array index is out of range.
How can I fix this and can you explain in newbie's terms why I can't use new for creating a new chunk. Also, why is array index out of range? Last question, is this structuring any good and how would you improve it or implement it differently?
Simply remove : MonoBehaviour from your Chunk class as you do not any behaviour for that. This is just a holder class for your data, and it does not follow (does not need to either) start-update routine that MonoBehaviour extending classes do.

How can I add properties for each element in array of gameObject?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[ExecuteInEditMode]
public class InstantiateObjects : MonoBehaviour
{
public GameObject[] objectsToInstantiate;
public Terrain terrain;
public float yOffset = 0.5f;
public int numberOfObjectsToCreate;
public bool parent = true;
public bool randomScale = false;
public float setRandScaleXMin, setRandScaleXMax;
public float setTandScaleYMin, setTandScaleYMax;
public float setTandScaleZMin, setRandScaleZMax;
private float terrainWidth;
private float terrainLength;
private float xTerrainPos;
private float zTerrainPos;
private GameObject objInstance;
private GameObject[] createdObjects;
private string objname;
public void Start()
{
//Get terrain size
terrainWidth = terrain.terrainData.size.x;
terrainLength = terrain.terrainData.size.z;
//Get terrain position
xTerrainPos = terrain.transform.position.x;
zTerrainPos = terrain.transform.position.z;
for(int i = 0; i < objectsToInstantiate.Length; i++)
{
objname = objectsToInstantiate[i].name;
MyCustomEditor.TagsAndLayers.AddTag(objname);
}
generateObjectOnTerrain();
}
public void Update()
{
}
public void DestroyObjects()
{
UpdateList(true);
if (createdObjects != null && createdObjects.Length > 0)
{
for (int i = 0; i < createdObjects.Length; i++)
{
DestroyImmediate(createdObjects[i]);
}
createdObjects = new GameObject[0];
}
}
public void generateObjectOnTerrain()
{
for (int i = 0; i < objectsToInstantiate.Length; i++)
{
//Generate random x,z,y position on the terrain
float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
//Generate random x,y,z scale on the terrain
float randScaleX = Random.Range(setRandScaleXMin, setRandScaleXMax);
float randScaleY = Random.Range(setTandScaleYMin, setTandScaleYMax);
float randScaleZ = Random.Range(setTandScaleYMax, setRandScaleZMax);
//Apply Offset if needed
yVal = yVal + yOffset;
//Generate the Prefab on the generated position
objInstance = Instantiate(objectsToInstantiate[i], new Vector3(randX, yVal, randZ), Quaternion.identity);
if (randomScale == true)
objInstance.transform.localScale = new Vector3(randScaleX, randScaleY, randScaleZ);
if (parent)
objInstance.transform.parent = this.transform;
objInstance.tag = objname;
}
createdObjects = GameObject.FindGameObjectsWithTag(objname);
if (objname == "Teleportation Booth")
UpdateList(false);
}
private void UpdateList(bool destroy)
{
GameObject go = GameObject.Find("Main Camera");
var list = go.GetComponent<PatrolOverTerrain>().Targets;
if (destroy == false)
{
list.AddRange(createdObjects);
go.GetComponent<PatrolOverTerrain>().Targets = list;
}
if (destroy == true)
{
list.Clear();
go.GetComponent<PatrolOverTerrain>().Targets.Clear();
}
}
}
Instead attaching the script to every gameObject I will want to clone I want to use array of objects.
For example let's say I have in the array 2 objects, now I want in the inspector to have under each element its own properties.
And when I will set the properties as children for each element it will take effect for the specific element only.
Instead general properties for all the elements to make this properties from Y Offset until the last Set Rand Scale Z Max to be children for each element.
So i can set the properties per element.
So I can expand each element and see his properties.
Btw: How can I change the random for example that: Set Rand Scale X Min and Set Rand Scale X Min will be on one line and not two lines ? Like:
Set Rand Scale X Min Max
Set Rand Scale Y Min Max
Set Rand Scale Z Min Max
Good way to do this would be creating custom class and then making array of instances of that class but it require a bit more lines. If you like to read more about this search ISerializationCallbackReceiver.
Simplest would be probably wrapping your variables in struct like below. You can then "unwrap" them to your classes or not. That depends on what you do want.
using UnityEngine;
using System;
public class InstantiateObjects : MonoBehaviour {
public ObjectsToInst[] objectsToInstantiate;
[Serializable]
public struct ObjectsToInst
{
public GameObject objectToInstantiate;
public Terrain terrain;
public float yOffset;
public int numberOfObjectsToCreate;
public bool parent;
public bool randomScale;
public float setRandScaleXMin, setRandScaleXMax;
public float setTandScaleYMin, setTandScaleYMax;
public float setTandScaleZMin, setRandScaleZMax;
}
}

Categories