Instatiated objects from the same prefab share a position? - unity3d

I'm starting a simple JRPG: generating a party from a group of prefabs, then moving each into position by changing their transform. But seeing a strange one where 3 goblins spawned from the same prefab are moving to exactly the same position. The other objects instantiated from different objects are moving correctly.
In the player, I can move the goblins by changing their transform, and they are separate. However the prefab itself is changing position to match the 'last' goblin spawned
Any hints? Am I somehow instantiating them to a common object?
Objects created and positioned in PartyManager:
public bool playerParty = true;
public GameObject[] party=new GameObject[4];
// Start is called before the first frame update
void Start()
{
for (int i=0; i<party.Length; i++){
GameObject character = Instantiate(party[i],this.gameObject.transform);
}
positionCharacters();
}
void positionCharacters(){
float facing = -1.0f;
if(playerParty) facing=1.0f;
for (int i=0; i<party.Length; i++){
party[i].transform.localPosition = new Vector3(-0.3f*i*facing, -0.05f*i,-0.1f*i);
print(party[i]+" moved to "+party[i].transform.localPosition);
}
}

First of all you don't seem to be storing references to the instantiated characters, GameObject character just goes out of scope immediately. If you put three references to the same object into GameObject[] party you shouldn't be surprised only the last set position is used.

Related

Best way to create a 2d top down race track procedurally

I am attempting to create a 2d top-down car racing game. This game will have a random road map each time the player plays the game. I have thought about doing this in two different ways: A tilemap, or just generate the roads by placing different prefabs (straight roads, turns, etc). I have decided to go with the prefab route.
The way I believe it should work is to have prefab square "tiles" which have their own colliders set on the edges so I can tell if a player goes off the track in which case they blow up. I would have a MapGenerator Script which will generate an initial random map by keeping track of the last tile placed (including its location and road type: left turn, straight, right, etc). This script will then keep adding onto the road randomly as the player gets closer and closer to the end which makes it an infinite road.
I just want to know if this is just not efficient or if I am thinking of this completely wrong.
Here are a couple of images showing my road tiles which I made in photoshop and then one prefab for a straight road (take note of the colliders on its edges).
A similar game to one I want to make is Sling Drift which I can provide the link if you want. I don't know the policy on adding links to forum chat.
Also, here is my code for the map generator:
//Type of tyle, types are normal (straight road or horizontal road) and turns
public enum MapTileType
{
NORMAL,
N_E,
N_W,
S_E,
S_W
}
//structure for holding the last tile location and its type.
public struct TypedTileLocation
{
public TypedTileLocation(Vector2 pos, MapTileType tyleType)
{
m_tileType = tyleType;
m_position = pos;
}
public Vector2 m_position;
public MapTileType m_tileType;
}
public class MapGenerator : MonoBehaviour
{
//Map Tiles
public GameObject m_roadTile;
public GameObject m_turnNorthWestTile;
//holds all the tiles made in the game
private List<GameObject> m_allTiles;
//Map Tile Widths and Height
private float m_roadTileWidth, m_roadTileHeight;
//Used for generating next tile
TypedTileLocation m_lastTilePlaced;
private void Awake()
{
//store the initial beginning tile location (0,0)
m_lastTilePlaced = new TypedTileLocation(new Vector2(0,0), MapTileType.NORMAL);
//set height and width of tiles
m_roadTileWidth = m_roadTile.GetComponent<Renderer>().bounds.size.x;
m_roadTileHeight = m_roadTile.GetComponent<Renderer>().bounds.size.y;
m_allTiles = new List<GameObject>();
}
// Start is called before the first frame update
void Start()
{
SetupMap();
}
void SetupMap()
{
//starting at the beginning, just put a few tiles in straight before any turns occur
for (int i = 0; i < 6; ++i)
{
GameObject newTempTile = Instantiate(m_roadTile, new Vector2(0, m_roadTileHeight * i), Quaternion.identity);
m_lastTilePlaced.m_tileType = MapTileType.NORMAL;
m_lastTilePlaced.m_position.x = newTempTile.transform.position.x;
m_lastTilePlaced.m_position.y = newTempTile.transform.position.y;
m_allTiles.Add(newTempTile);
}
//now lets create a starter map of 100 road tiles (including turns and straigt-aways)
for (int i = 0; i < 100; ++i)
{
//first check if its time to create a turn. Maybe I'll randomly choose to either create a turn or not here
//draw either turn or straight road, if the tile was a turn decide which direction we are now going (N, W, E, S).
//this helps us determine which turns we can take next
//repeat this process.
}
}
void GenerateMoreMap()
{
//this will generate more map onto the already existing road and then will delete some of the others
}
// Update is called once per frame
void Update()
{
}
private void OnDrawGizmos()
{
}
}
Thanks!
Have you tried splines? They let you make curvy paths like race tracks easily. If not, here is a video that might help: https://www.youtube.com/watch?v=7j_BNf9s0jM.

Instantiate GameObjects and change Material when it hits on the ground

There is a code for instantiate cube into the list and change a Material of each clone when it hits on the ground
The following code works but not in Real-Time. Update function works like a Start function for a Foreach method
How to get a value of item.transform.position.y in the Update function Real-Time?
public GameObject cubePrefab;
public Material RedMat;
public float GroudLevel = 0.5f;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
List<GameObject> cloneList = new List<GameObject>();
//instantiate clones into the list
for (int i = 0; i < 10; i++)
{
var clone = Instantiate (cubePrefab,new Vector3(Random.Range(-3f, 3f),
Random.Range(4f, 10.5f),Random.Range(-3f, 3f)), Quaternion.identity);
cloneList.Add(clone);
}
//if clone is grounded change a Material for each clone
foreach (var item in cloneList)
{
//Debug.Log(item.transform.position.y);
//check if clone is on the ground
if(item.transform.position.y < GroudLevel)
{
item.GetComponent<Renderer>().material = RedMat;
}
}
}
}
There is a screenshot for a GroudLevel = 7
The reason this function isn't working is because the pivot (transform.position) of your item is always in the center of the object. This isn't something you can change in Unity (nor would you necessarily want to).
On top of this, you're checking if the item is under the ground, and not on it when you use < insteaad of <=, because the position needs to be less than groundLevel to return true.
There are several solutions here.
The simplest would involve moving all of this logic to an OnCollisionEnter or OnTriggerEnter method. For more information on this, check the Unity documentation.
Another solution would be to find a way tthe size of the object, divide it by two, and check if
item.transform.position - halfSize <= groundLevel;
This seems really cumbersome and overly complex, however. You'd be better off using Unity's built-in collision system, unless you have a reason not to.

A SpriteRenderer is not displaying the sprite when set the second time

I have a cell prefab in which I have a GameObject with SpriteRenderer. I am instantiating multiple instances of a prefab and feeding a sprite for every instance. The first cell instance is displaying the given sprite but the second one is not.
Here I am creating two instances of a Cell and feeding the Cell with a sprite from mIconManager:
private void PopulateCells()
{
for (var i = 0; i < 2; i++)
{
GameObject goPrefab = Instantiate(goRawPrefab, CalculateCellPosition(i), Quaternion.identity, trBoard);
CellController cellController = goPrefab.GetComponent<CellController>();
cellController.FillContent(mIconManager.getSprite(i), i);
}
}
This is a snippet from CellController script attached to a Cell:
private void Awake()
{
mIcon = GameObject.Find("IconSprite").GetComponent<SpriteRenderer>();
mCardBg = GameObject.Find("CardBg").GetComponent<SpriteRenderer>();
}
public void FillContent(Sprite sprite, int index)
{
mSprite = sprite;
mIcon.sprite = mSprite;
}
This is my Cell prefab:
When I run the code, the second instance of a prefab is not getting the sprite.
I checked the first prefab's SpriteRenderer in the inspector while the game is running, and it said anchovies for the sprite - the one I gave via FillContent() method. Interestingly, the second prefab's SpriteRenderer component shows EMPTY sprite.
First Prefab's SpriteRenderer while game is running:
Second Prefab's SpriteRenderer while game is running:
Am I missing something? I searched through the internet and found some posts saying "SpriteRenderer is not loading sprites the second time" but they were not answered and the ones that were answered didn't solve my problem.
P.S. I also tried setting the SAME sprite for both instances of the Cell prefab - still no success.
P.S.2. I logged the sprites that are given to the both instances of the Cell prefab and they were not null or empty. I even did Debug.Log(mIcon.sprite) which both instances logged the name of sprites I provided.
Edit:
Logging when giving the same sprite to both instances:
public void FillContent(Sprite sprite, int index)
{
Debug.Log("hop, spriteName : " + sprite + ", prefabIndex : " + index);
mSprite = sprite;
mIcon.sprite = sprite;
}
Try Filling It with the same sprite twice the code seems fairly normal
This will Help me debug it al lil more ie.
private void PopulateCells()
{
for (var i = 0; i < 2; i++)
{
GameObject goPrefab = Instantiate(goRawPrefab, CalculateCellPosition(i), Quaternion.identity, trBoard);
CellController cellController = goPrefab.GetComponent<CellController>();
cellController.FillContent(mIconManager.getSprite(1), i);
}
}
Okay, it turns out the problem was not related to the SpriteRenderer. SpriteRenderer was working as expected. However, the way I got a reference to the SpriteRenderer was not correct.
In CellController, I was getting a reference to the SpriteRenderer with GameObject.Find..., the thing is it was not giving me a reference to a instantiated prefab clone, but to the first prefab clone.
private void Awake()
{
mIcon = GameObject.Find("IconSprite").GetComponent<SpriteRenderer>();
mCardBg = GameObject.Find("CardBg").GetComponent<SpriteRenderer>();
}
Instead, I used tranform.Find and everything worked as expected.

Spawn sprites on action

I'm trying to make a Pinata GameObject, that when clicked bursts and gives a variable number of Gift GameObjects with various images and behaviors in them.
I'm also not sure what the unity vocabulary for this is so as to look this up in unity docs.
Can anyone please lend me a hand here? Thanks!
There are several ways to handle this.
The simple way is to use Object.Instantiate, Object Instantiation is the vocab you're after.
This will create a copy of a predefined Unity object, this can be a gameobject or any other object derived from UnityEngine.Object, check the docs for more info https://docs.unity3d.com/ScriptReference/Object.Instantiate.html.
In your case, your Pinata would have an array, or list, of prefabs. These prefabs are created by you with a certain behaviour and sprite for each one. When the Pinata bursts, you instantiate random prefabs at random positions surrounding the Pinata, up to you how to position these objects.
Something along these lines should do the trick:
class Pinata : Monobehaviour
{
public GameObject[] pickupPrefabs;
public int numberOfItemsToSpawn; //This can be random
//any other variables that influence spawning
//Other methods
public void Burst()
{
for(int i = 0; i < numberOfItemsToSpawn; i++)
{
//Length - 1 because the range is inclusive, may return
//the length of the array otherwise, and throw exceptions
int randomItem = Random.Range(0, pickupPrefabs.Length - 1);
GameObject pickup = (GameObject)Instantiate(pickupPrefabs[randomItem]);
pickup.transform.position = transform.position;
//the position can be randomised, you can also do other cool effects like apply an explosive force or something
}
}
}
Bare in mind, if you want the game to be consistent, then each behaviour prefab would have there own predefined sprite, this would not be randomised. The only thing randomised would be the spawning and positioning.
If you did want to randomise the sprites for the behaviours then you'd have to add this to the Pinata class:
public class Pinata : Monobehaviour
{
//An array of all possible sprites
public Sprite[] objectSprites;
public void Burst()
{
//the stuff I mentioned earlier
int randomSprite = Random.Range(0, objectSprites.Length - 1);
SpriteRenderer renderer = pickup.GetComponent<SpriteRenderer>();
//Set the sprite of the renderer to a random one
renderer.sprite = objectSprites[randomSprite];
float flip = Random.value;
//not essential, but can make it more random
if(flip > 0.5)
{
renderer.flipX = true;
}
}
}
You can use Unity random for all your random needs, https://docs.unity3d.com/ScriptReference/Random.html
Hopefully this'll lead you in the right direction.

how to randomly initialize particles system in different places of scene in unity3d

I recently tried asking this question but I realized it was not a sufficient question. In my game the player is a fire fighter learner and i want to broke out fire randomly in my game (like not predictable by player), but i did not know how to implement this.
So far i have done this but nothing goes good.(I have a empty object called t in unity which have 3 to 5 particles systems, and all are set to dont awake at start)
code is here :
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
public ParticleSystem[] particles;
public int numOn = 3;
public int j;
void Start() {
for (int i = 0; i < particles.Length - 1; i++) {
j = Random.Range(i + 1, particles.Length - 1);
ParticleSystem t = particles[j];
particles[j] = particles[i];
particles[i] = t;
}
for (j = 0; j < numOn; j++ )
{
particles[j].Play();
}
}
}
help will be appreciated :-)
You could try using prefabs. Create a game object in the editor that has any particle systems and scripts your fire objects need. Once it's good, drag the object from the hierarchy into your project. This will create a prefab (you can now remove it from the scene). Now, on your spawning script, add a field of type GameObject and drag the prefab you made before into it. Now, when you need to create one, just call Instantiate(prefabVar) to create a copy of your prefab.
Edit:
For your specific case, since you only want one fire to be instantiated in a random location, you could have your spawning script look something like this:
public Transform[] SpawnPoints;
public GameObject FirePrefab;
void Start() {
Transform selectedSpawnPoint = SpawnPoints[(int)Random.Range(0, SpawnPoints.Count - 1)];
Instantiate(FirePrefab, selectedSpawnPoint.position, selectedSpawnPoint.rotation);
}
This solution would allow for you to potentially spawn more than one fire object if you needed. An alternative would be if you will only ever have exactly one fire object in the scene at all. Instead of instantiating from a prefab, the object is already in the scene and you just move it to one of your spawn points at the start of the scene. An example script on the fire object itself:
public Transform[] SpawnPoints;
void Start() {
Transform selectedSpawnPoint = SpawnPoints[(int)Random.Range(0, SpawnPoints.Count - 1)];
transform.position = selectedSpawnPoint.position;
transform.rotation = selectedSpawnPoint.rotation;
}