Gameobjects are getting randomly spawned on some platform objects. I want to avoid these two different gameobject to be spawned at the same exact location (money2 should change it's position).
Here is the code:
void Start()
{
int randMoney = Random.Range(0, 8);
Vector3 moneyPos = transform.position;
moneyPos.y += 0.5f;
if (randMoney < 1)
{
GameObject moneyInstance = Instantiate(money, moneyPos, money.transform.rotation);
moneyInstance.transform.SetParent(gameObject.transform);
}
int randMoney2 = Random.Range(0, 8);
Vector3 money2Pos = transform.position;
money2Pos.y += 0.5f;
if (randMoney2 < 1)
{
GameObject money2Instance = Instantiate(money2, money2Pos, money2.transform.rotation);
money2Instance.transform.SetParent(gameObject.transform);
}
if (money2Pos == moneyPos)
{
//where gameobject2s position should change
}
}
Thank you for taking your time!
You're trying to do the same thing multiple times here. First the code block for money1 and money2 are similar if not exactly the same. Consider creating a function like this one:
public GameObject SpawnMoney(GameObject moneyPrefab, int spawnProbability, Vector3 spawnOffset)
{
int spawnPourcentage = Random.Range(0, 101); // 101 because the random is exclusive
bool willSpawn = spawnPourcentage <= spawnProbability;
//if not supposed to spawn, then do nothing
if(!willSpawn) return null;
//Spawn effectively the money with position offset
Vector3 plateformePosition = transform.position;
GameObject moneyInstance = Instantiate(moneyPrefab, plateformePosition + spawnOffset, Quaternion.identity, transform);
//return the instance if you want to manipulate it later ;)
return moneyInstance;
}
Now you have your function ready to go, you may spawn your coins in a start or even in a loop if you want to !
If you have questions, please ask
Related
In my flappy bird game, I want to create a loop that works through instantiated pipes, altering the pipe prefab (to reduce space between pipes + increase respawn rate) to increase difficulty based on player score.
The code below works, but I'd like to implement difficulty differently. I don't know how to add newly instantiated objects to a list.
I'd also like to add an event animation that plays after each level (before new pipe prefab instantiated), pausing the loop as the "new level" text flies across the screen.
private void Start()
{
InvokeRepeating("SpawnPipe", .05f, respawnRate);
}
private void SpawnPipe()
{
int levelOne = 8;
int levelTwo = 18;
int levelThree = 29;
if(GameManager.score < levelOne)
{
GameObject pipes1 = Instantiate(pipePrefab1, transform.position, Quaternion.identity);
pipes1.transform.position += Vector3.up * Random.Range(minHeight, maxHeight);
}
if (GameManager.score > levelOne && GameManager.score < levelTwo)
{
GameObject pipes2 = Instantiate(pipePrefab2, transform.position, Quaternion.identity);
pipes2.transform.position += Vector3.up * Random.Range(minHeight, maxHeight);
}
if (GameManager.score > levelTwo && GameManager.score < levelThree)
{
GameObject pipes3 = Instantiate(pipePrefab3, transform.position, Quaternion.identity);
pipes3.transform.position += Vector3.up * Random.Range(minHeight, maxHeight);
}
}
Not sure if this is what you are asking for but Lists are used like this:
List<GameObject> pipes = new List<>();
pipes.add(pipes1);
Using InvokeRepeating to spawn the pipes is probably a bad idea because it gives you little control. I would recommend to check in your Update loop if a certain time passed or distance was traveled and spawn pipes accordingly;
I am busy building a platformer(2d). And I really want procedural generation. I think that I came up with a smart system for spawning the platforms randomly(up for debate). But my platforms keep on picking to randomly spawn down. No matter how many times I restart the game
eg :
here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformSpawner : MonoBehaviour
{
public GameObject[] platform;
public Vector2 SpawnGap;
public float randomspawn;
public float VerticalRandomness;
public float queueTime = 1.5f;
private float time = 0;
public Vector2 startspawn;
public GameObject flag;
Vector2 nextspawn;
Vector2 nextspawncentre;
Vector3 prevspawn;
public int platamount;
int amountspawned;
bool alreadyspawnedflag;
// Start is called before the first frame update
void Start()
{
platamount = Random.Range(5, 15); // random amount of platforms in level
alreadyspawnedflag = false;
}
// Update is called once per frame
void Update()
{
if (time > queueTime) // just for quality of life(so that if I maybe have 10000 platforms I can delay the spawning)
{
if(platamount >= amountspawned) // random amount of platform
{
GameObject go = Instantiate( platform[ Random.Range (0, platform.Length) ] ); // instantiating random object from list, to make it even more random(objects assigned in inspector)
Vector2 randomrange = new Vector2(Random.Range(1f, -1f) * randomspawn, Random.Range(2f, -0.25f/*perferes up, but stil not working*/) * VerticalRandomness);//calculates random position
Debug.Log(randomrange);// it is indeed choosing random pos, even on y but still giong down
nextspawncentre = new Vector2(prevspawn.x + SpawnGap.x + startspawn.x, prevspawn.y + SpawnGap.y + startspawn.y); // calculates next spawn without randomness
nextspawn = new Vector2(nextspawncentre.x + randomrange.x, nextspawncentre.y + randomrange.y);// adds randomness
go.transform.position = transform.position + new Vector3(nextspawn.x, nextspawn.y, 0);// places object at nextspawn variable
time = 0;// resets timer
prevspawn = go.transform.position;
amountspawned += 1;
randomrange = new Vector2(0, 0);// tried reseting the random, but to no success
}
else if(!alreadyspawnedflag)// almost same logic as before just for the flag to reload scene(handeled in another script)
{
GameObject spawnedflag = Instantiate(flag);
spawnedflag.transform.position = new Vector3(nextspawncentre.x + 1, 15, 0f);
alreadyspawnedflag = true;
}
}
time += Time.deltaTime;
}
}
look at comments explained, everything there.
In advance thanks for the help.
ps: Platforms do not have rigidbodies, just sprite renderer and boxcollider2d.
I think the issue is related to your startSpawn being a negative value (at least it looks like that from your first platform). So even if the Y is positive in the randomrange it might be overruled by the negative startSpawn Y value.
Actually I think you don't even add your startSpawn every time at all but rather only using it as the initial position
I think you should simply do e.g.
private bool firstPlatform = true;
and
...
if(firstPlatform)
{
nextspawn = startspawn;
firstPlatform = false;
}
else
{
nextspawncentre = prevspawn + SpawnGap;
nextspawn = nextspawncentre + randomrange;
}
...
while working in a 3d endless runner game in unity I came across this issue. I have a List of platforms(segments/roads) that lay in front of the player while the player runs in z direction. I have downloaded a new asset package called Dreamteck splines. So each platform has a spline component attached to it. Once a platform is laid the player grabs the spline and runs according to the pattern of the spline.
Let's say that the player is on the first platform. When the player reaches the end of the first platform's spline, the OnEndReached() event handler is called, which basically says what you want to happen when the spline's endpoint is reached. So I want to know how to I get the next spline once the end is reached.
P = player
As seen in the image above this is what I am trying to accomplish. As a brief description of how platforms are laid is that once the player goes to the next road the one he just passed gets disabled so next time he can reuse the road in front of the player in random manner.
The code: track manager script.
public Segment[] tilePrefabs;
public static Segment newSegment;
public static List<Segment> m_Segments;
public static List<Segment> m_PastSegements;
private int m_SafeSegmentLeft;
private int m_PreSegments = -1;
private float startingSegmentDistance = 4f;
private int startingSafeSegments = 2;
private int amtSegmentsOnScreen = 10;
private float segmentRemovalDistace = -40f;
private float m_TotalWorldDistance;
private float m_CurrentSegmentDistance;
void Update ()
{
while (m_Segments.Count < amtSegmentsOnScreen)
{
SpawnNewSegment();
}
m_TotalWorldDistance += scaledSpeed;
m_CurrentSegmentDistance += scaledSpeed;
if (m_CurrentSegmentDistance > m_Segments[0].worldLength)
{
m_CurrentSegmentDistance -= m_Segments[0].worldLength;
m_PastSegements.Add(m_Segments[0]);
m_Segments.RemoveAt(0);
}
Vector3 currentPos;
Quaternion currentRot;
Transform playerTransform = playerMotor.transform;
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
bool needRecenter = currentPos.sqrMagnitude > floatingOriginThreshold;
if (needRecenter)
{
int count = m_Segments.Count;
for (int i = 0; i < count; i++)
{
m_Segments[i].transform.position -= currentPos;
}
count = m_PastSegements.Count;
for (int i = 0; i < count; i++)
{
m_PastSegements[i].transform.position -= currentPos;
}
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
}
playerTransform.rotation = currentRot;
playerTransform.position = currentPos;
for (int i = 0; i < m_PastSegements.Count; i++)
{
if ((m_PastSegements[i].transform.position - currentPos).z < segmentRemovalDistace)
{
m_PastSegements[i].Cleanup();
m_PastSegements.RemoveAt(i);
i--;
}
}
}
public void SpawnNewSegment()
{
int useSegment = Random.Range(0, tilePrefabs.Length);
if (useSegment == m_PreSegments)
{
useSegment = (useSegment + 1) % tilePrefabs.Length;
}
Segment segmentToUse = tilePrefabs[useSegment];
newSegment = Instantiate(segmentToUse, Vector3.zero, Quaternion.identity);
Vector3 currentExitPoint;
Quaternion currentExitRotation;
if (m_Segments.Count > 0)
m_Segments[m_Segments.Count - 1].GetPointAt(1.0f, out currentExitPoint, out currentExitRotation);
else
{
currentExitPoint = transform.position;
currentExitRotation = transform.rotation;
}
newSegment.transform.rotation = currentExitRotation;
Vector3 entryPoint;
Quaternion entryRotation;
newSegment.GetPointAt(0.0f, out entryPoint, out entryRotation);
Vector3 pos = currentExitPoint + (newSegment.transform.position - entryPoint);
newSegment.transform.position = pos;
newSegment.manager = this;
newSegment.transform.localScale = new Vector3((Random.value > 0.5f ? -1 : 1), 1, 1);
newSegment.objectRoot.localScale = new Vector3(1.0f / newSegment.transform.localScale.x, 1, 1);
if (m_SafeSegmentLeft <= 0)
SpawnObstacle(newSegment);
else
m_SafeSegmentLeft -= 1;
m_Segments.Add(newSegment);
}
The player script
//Current tile segment;
private Segment currentSegment;
//Spline Follower
private SplineFollower follower
//For Dreamteck spline -->
private Segment nextSegment;
void Start()
{
playerCollider = GetComponent<CapsuleCollider>();
anim = GetComponent<Animator>();
follower = GetComponent<SplineFollower>();
moveLane = currentLane;
follower.onEndReached += Follower_onEndReached;
}
private void Follower_onEndReached()
{
currentSegment = nextSegment;
follower.computer = currentSegment.spline;
}
void OnTriggerEnter(Collider col)
{
nextSegment = col.GetComponentInParent<Segment>();
}
The segment script : Attached to each road/ platform
public SplineComputer spline;
public static Segment next;
SplinePoint[] points;
void Start()
{
spline = GetComponentInChildren<SplineComputer>();
spline.space = SplineComputer.Space.Local;
points = spline.GetPoints();
if (points.Length == 0)
return;
}
At the moment I use colliders, each road has a box collider component. Once the player reach end of the platform it does get the next spline component. It works but sometimes it fails to recognize the next spline and use the same spline which causes the player to run the same platform that he passed again and again.
So I'm out of ideas. So came here to find a solution or advice. Help would be appreciated.
In this case I would simply store my possible segments in a List then when I reached the end get the next segment and move the current 1st segment to the end or where ever you want to move it in the list.
That is
public Segment currentSegment;
public List<Segment> segments;
void OnEndReached()
{
//Put the completed segmetn back in the list ... probably at the end or randomized anywhere but at the start
segments.Insert(segments.Count-1, currentSegment);
//Now get the next segment
currentSegment = segments[0];
segments.RemoveAt(0);
}
In this model you have a simple List which represents the order your segments will appear in, you always set the current segment to the next in the list e.g. index 0 and you put them back in the list when done... putting them at the end or if you want to randomize order slotting them in anyware except index 0 e.g.
segments.Insert(UnityEngine.Random.Range(1, segments.Count), currentSegment);
Note that I removed the segment I am on from the list ... the list just represents the upcoming order which in runner games I find it handy to know that e.g. so I can reset things, change attributes of the segments based on performance, score, etc.
Using OnTriggerEnter, or OnTriggerEnter2D if you're dealing with 2D colliders, should do the job. But as you say you already are working with colliders, I assume this is what you have tried.
You could try:
OnTriggerStay
A Raycast down to the ground object. In this link is a 2D Example: https://kylewbanks.com/blog/unity-2d-checking-if-a-character-or-object-is-on-the-ground-using-raycasts
You can also raycast with 3D objects.
What you would be using it for in this case is basically to shoot a "laser" into the ground below your player and grab an object there based on which layers you tell it to hit. So if you have a layer called "Ground" which your platform is part of, then it can only return objects from that layer.
Just remember to raycast often so it's updated to reflect the game.
So, I am making this little game, where the character constantly moves upwards with an autoscroll camera. The character jumps from platform to platform and as soon as a platform or a background tile is nolonger visible, i loop it back up. I assigned a range to my plattforms in which a randomizer choses a value from so that the player gets an individual set of plattforms every time he or she starts the game. the problem is the looping: since i do the randomizing in the start() functions, the random poision of the plattforms is only assigned once and then looped and looped again and again. so the game gets kinda boring after a few loops with is like after 20 seconds :D
Here is my code:
private float randomFloat = 0;
private int subOrAdd = 0;
// Use this for initialization
void Start () {
subOrAdd = Random.Range(1, 10);
randomFloat = Random.Range(0f, 1.4f);
// randomly add or subtract height of object
if (subOrAdd < 6)
{
this.transform.position = new Vector2(transform.position.x, transform.position.y - randomFloat);
}
else if (subOrAdd >= 6)
{
this.transform.position = new Vector2(transform.position.x, transform.position.y + randomFloat);
}
}
Basically, I am having a hardcoded range and then randomly decide to either add or subtract the number that came out of the range. so how would i make it so, that the objects that get looped always ask for a new position? Because start is only called once as you know and even after looping, the position remains the same. I hope I made myself clear here :)
Any help would be awesome!
Here is the the code that loops the platforms:
public class PlattformLooper : MonoBehaviour {
public float spacingBetweenLoops = 0f;
private void OnTriggerEnter2D(Collider2D collider)
{
if (collider.gameObject.tag == "Plattform")
{
Debug.Log("TRIGGERED Plattform!");
float heightOfBGObj = ((BoxCollider2D)collider).size.y;
Vector3 pos = collider.transform.position;
pos.y += heightOfBGObj * (5*5)+spacingBetweenLoops;
collider.transform.position = pos;
}
}
Just extract your randomization logic into a separate method.
void Start () {
RandomizeHeight()
}
public void RandomizeHeight() {
subOrAdd = Random.Range(1, 10);
randomFloat = Random.Range(0f, 1.4f);
// randomly add or subtract height of object
if (subOrAdd < 6)
{
this.transform.position = new Vector2(transform.position.x, transform.position.y - randomFloat);
}
else if (subOrAdd >= 6)
{
this.transform.position = new Vector2(transform.position.x, transform.position.y + randomFloat);
}
}
Then you can call it whenever you want:
public class PlattformLooper : MonoBehaviour {
public float spacingBetweenLoops = 0f;
private void OnTriggerEnter2D(Collider2D collider)
{
if (collider.gameObject.tag == "Plattform")
{
Debug.Log("TRIGGERED Plattform!");
float heightOfBGObj = ((BoxCollider2D)collider).size.y;
Vector3 pos = collider.transform.position;
pos.y += heightOfBGObj * (5*5)+spacingBetweenLoops;
collider.transform.position = pos;
collider.GetComponent<YourComponent>().RandomizeHeight();
}
}
I am making an endless runner style game in unity where the floor tiles spawn randomly and endlessly in front of the player as they run and delete themselves after a certain distance behind the player this is all working fine and as intended however the individual tiles spawn about half way inside each other and as much as I try to debug my code I can't seem to effect them. Ideally, I want the code to do exactly what it's doing, but the tiles spawn end to end rather than inside each other.
Any help would be greatly appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tile_Manager : MonoBehaviour
{
public GameObject[] tilePrefabs;
private Transform playerTransform;
private float spawnZ = 5.0f;
private float tileLength = 5.0f;
private float safeZone = 7.0f;
private int amtTilesOnScreen = 10;
private int lastPrefabIndex = 0;
private List<GameObject> activeTiles;
// Use this for initialization
void Start () {
activeTiles = new List<GameObject>();
playerTransform = GameObject.FindGameObjectWithTag ("Player").transform;
for (int i = 0; i < amtTilesOnScreen; i++)
{
if (i < 2)
SpawnTile(0);
else
SpawnTile();
}
}
// Update is called once per frame
void Update () {
if (playerTransform.position.z - safeZone > (spawnZ - amtTilesOnScreen * tileLength))
{
SpawnTile();
DeleteTile();
}
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
if (prefabIndex == -1)
go = Instantiate(tilePrefabs[RandomPrefabIndex()]) as GameObject;
else
go = Instantiate(tilePrefabs[prefabIndex]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
activeTiles.Add (go);
}
private void DeleteTile()
{
Destroy(activeTiles [0]);
activeTiles.RemoveAt (0);
}
private int RandomPrefabIndex()
{
if (tilePrefabs.Length <= 1)
return 0;
int randomIndex = lastPrefabIndex;
while (randomIndex == lastPrefabIndex)
{
randomIndex = Random.Range(0, tilePrefabs.Length);
}
lastPrefabIndex = randomIndex;
return randomIndex;
}
}
stacked tiles
You need to take the length of a tile into account. Try changing this
go.transform.position = Vector3.forward * spawnZ;
to this
go.transform.position = Vector3.forward * (spawnZ + tileLength / 2);
to add half the tile length to the spawn position.
Wouldn't you want
go.transform.Translate(Vector3.forward * spawnZ);
not position?
As you're spawning things relative to the world coordinate system.
https://docs.unity3d.com/ScriptReference/Transform.Translate.html