I'm making an endless runner game, and I have 4 types of terrain I want to auto-generate.
the auto-generation works, but I want to select a random terrain but I can't figure out how.
I have tried this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformManager: MonoBehaviour
{
[SerializeField]
private GameObject[] _platformPrefabs;
[SerializeField]
private int _zedOffset;
// Start is called before the first frame update
void Start()
{
randomTerrain();
}
public void RecyclePlatform(GameObject Platform)
{
Platform.transform.position = new Vector3(0, 0, _zedOffset);
_zedOffset += 12;
}
public void randomTerrain()
{
for (int i = 0; i < _platformPrefabs.Length; i++)
{
Instantiate(_platformPrefabs[Random.Range(0, 3)], new Vector3(0, 0, i * 12), Quaternion.Euler(0, 90, 0));
_zedOffset += 12;
}
}
}
but it only selects the first game object every time except the first initiation. how can I make it select randomly?
The random number generator is not really random; it only generates a pseudo-random stream based on a given seed. If this seed isn't initialized, that could explain why you're getting the same result each time. To initialize the random state you'll need to use Random.InitState.
A typical approach is to use the system clock for random number generators. Games like Minecraft actually let you specify this seed so that the same seed = the same randomly generated content.
Random.InitState(System.DateTime.Now.Ticks);
Related
I add my codes below. What is my fault, can anyone help me?
I want to when SpawnRandomBall function run two times, spawnInternal turn into spawnInternal2. So I create a new variable, called 'check'. The variable increase when SpawnRandomBall function run. I set the variable as a public. In this way I can see that 'check' variable increase or doesn't increase. 'Check' variable is increasing without problem. When the veriable value equal to 3, it must be 'else if' run. But unfortunately it doesn't work.
I guess problem is I run my codes in Start() function. But I don't know how can I do properly.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnManagerX : MonoBehaviour
{
public GameObject[] ballPrefabs;
private float spawnLimitXLeft = 14.5f;
private float spawnLimitXRight = 24;
private float spawnPosY = 10;
private float startDelay = 1.0f;
private float spawnInterval = 4.0f;
private float spawnInterval2 = 2.0f;
public int check;
// Start is called before the first frame update
void Start()
{
if (check <= 2)
{
InvokeRepeating("SpawnRandomBall", startDelay, spawnInterval);
}
else if (check > 2)
{
InvokeRepeating("SpawnRandomBall", startDelay, spawnInterval2);
}
}
// Spawn random ball at random x position at top of play area
void SpawnRandomBall ()
{
// Generate random ball index and random spawn position
Vector3 spawnPos = new Vector3(-21, spawnPosY, Random.Range(spawnLimitXLeft, spawnLimitXRight));
int ballIndex = Random.Range(0, 3);
// instantiate ball at random spawn location
Instantiate(ballPrefabs[ballIndex], spawnPos, ballPrefabs[ballIndex].transform.rotation);
check += 1;
}
}
I want to change SpawnInternal variable into SpawnInternal2
The situation you describe is that you want a method to run. And for the first two iterations, you’d like one particular delay interval, and then after that you’d like a different delay interval. With that being the case, I would suggest you’re using the wrong tools. I would personally start a Coroutine like this:
public class SpawnManagerX : MonoBehaviour
{
public GameObject[] ballPrefabs;
private float spawnLimitXLeft = 14.5f;
private float spawnLimitXRight = 24;
private float spawnPosY = 10;
private float startDelay = 1.0f;
private float spawnInterval = 4.0f;
private float spawnInterval2 = 2.0f;
public int check;
void Start()
{
StartCoroutine ( SpawnRandomBall () );
}
// Spawn random ball at random x position at top of play area
IEnumerator SpawnRandomBall ()
{
while ( true )
{
// yielding here will produce an initial delay when the coroutine is run.
yield return new WaitForSeconds (
(check++ < 2) ? spawnInterval : spawnInterval2 );
// Generate random ball index and random spawn position
var spawnPos = new Vector3(-21, spawnPosY, Random.Range(spawnLimitXLeft, spawnLimitXRight));
var ballIndex = Random.Range(0, 3);
// instantiate ball at random spawn location
Instantiate( ballPrefabs[ballIndex], spawnPos, ballPrefabs[ballIndex].transform.rotation );
}
}
}
It’s also possible to run the Start method as a coroutine as well, but I feel, especially for newcomers, that the intent can sometimes be lost.
What this is saying is, start a coroutine, and in that coroutine, keep looping while (true) (which means “forever”). The line that change the amount of time that it waits is the last line, the “return”. We WaitForSeconds and the number of seconds we wait is based on the current count value. NB: written on phone but not tested.
Edited to create an initial delay in the coroutine.
If Start gets called once then the else if is never run. The code doesn't just wait at the else if waiting for check to update.
You just need to manage what code gets run in the SpawnRandomBall method.
You're going to need to try something like this this:
// Start is called before the first frame update
void Start()
{
InvokeRepeating("SpawnRandomBall", startDelay, spawnInterval2);
}
// Spawn random ball at random x position at top of play area
void SpawnRandomBall()
{
check++;
if (check == 1 || check == 3)
return;
// Generate random ball index and random spawn position
Vector3 spawnPos = new Vector3(-21, spawnPosY, Random.Range(spawnLimitXLeft, spawnLimitXRight));
int ballIndex = Random.Range(0, 3);
// instantiate ball at random spawn location
Instantiate(ballPrefabs[ballIndex], spawnPos, ballPrefabs[ballIndex].transform.rotation);
}
I'm using a dice rolling script to roll through different faces of my classmates as a sort of wheel of fortune system and I'm completely new to both unity and C#. I've done some googling and come up with the following code that works.
using System.Collections;
using UnityEngine;
public class Dice : MonoBehaviour
{
// Array of dice sides sprites to load from Resources folder
public Sprite[] diceSides;
// Reference to sprite renderer to change sprites
private SpriteRenderer rend;
// Use this for initialization
private void Start()
{
// Assign Renderer component
rend = GetComponent<SpriteRenderer>();
// Load dice sides sprites to array from DiceSides subfolder of Resources folder
diceSides = Resources.LoadAll<Sprite>("DiceSides/");
}
// If you left click over the dice then RollTheDice coroutine is started
public void OnMouseDown()
{
StartCoroutine("RollTheDice");
}
// Coroutine that rolls the dice
private IEnumerator RollTheDice()
{
// Variable to contain random dice side number.
// It needs to be assigned. Let it be 0 initially
int randomDiceSide = 0;
// Final side or value that dice reads in the end of coroutine
int finalSide = 0;
// Loop to switch dice sides ramdomly
// before final side appears. 20 itterations here.
Random rnd = new Random();
for (int i = 0; i <= 20; i++)
{
// Pick up random value from 0 to 5 (All inclusive)
randomDiceSide = Random.Range(0, 9);
// Set sprite to upper face of dice from array according to random value
rend.sprite = diceSides[randomDiceSide];
diceSides = Resources.LoadAll<Sprite>("DiceSides/");
// Pause before next itteration
yield return new WaitForSeconds(0.2f);
}
// Assigning final side so you can use this value later in your game
// for player movement for example
finalSide = randomDiceSide;
// Show final dice value in Console
Debug.Log(finalSide);
}
}
What I want to do is to make it so that when someone's face is selected they are taken out of the array. I don't really know how to do it. If you need any more details to help me out feel free to ask. Thanks in advance.
At the top, add this using statement: using System.Collections.Generic;
Have a private List<Sprite> finalSideSprites variable.
In Start: finalSideSprites = new List<Sprite>(Resources.LoadAll<Sprite>("DiceSides/"));
In RollTheDice: Replace finalSide = randomDiceSide; with
finalSide = (int)Random.Range(0, finalSideSprites.Count - 1);
rend.sprite = finalSideSprites[finalSide];
finalSideSprites.RemoveAt(finalSide);
if(finalSideSprites.Count <= 0){
finalSideSprites = new List<Sprite>(Resources.LoadAll<Sprite>("DiceSides/"));
}
Essentially, this keeps a list of eligible "final side" values, picks one at random, removes it from the eligible values list, then resets the list when they've all been used.
I am trying to use ParticleSystem.Emit() to dynamically emit particles, and am unable to get Emit() to work outside of the Start() method.
I am able to emit particles in the Start() method, either directly or with a separate method. When attempting to do the same in Update(), LateUpdate(), or FixedUpdate() it no longer works.
There are no errors reported. I reported the number of particles in the update methods to make sure that the code there was actually running, and it reports however many particles I emit in the Start() method.
When trying to emit particles in the update methods I have tried:
Using Emit() directly in the update loop.
Using a separate method called directly in the update loop.
Using a separate method called using Invoke() in the update loop.
I'm able to check and alter particle position in Update(), so I don't think I'm having some kind of scope problem.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
public ParticleSystem aSystem;
private ParticleSystem.Particle[] aParticle;
void Start()
{
aParticle = new ParticleSystem.Particle[aSystem.main.maxParticles];
// Emit particle.
DoEmit();
Debug.Log("Particle Count: " + aSystem.particleCount);
}
void Update()
{
int numParticles = aSystem.GetParticles(aParticle);
Debug.Log("Number of particles: " + numParticles);
for (int i = 0; i < numParticles; i++)
{
if (aParticle[i].position.z > 1)
{
aParticle[i].position = new Vector3(0f,0f,0f);
}
aParticle[i].velocity = new Vector3(0f, 0f, 1f);
}
aSystem.SetParticles(aParticle);
// Emit particle.
DoEmit();
}
void DoEmit()
{
// Configure render settings for particle.
ParticleSystemRenderer aRenderer = aSystem.GetComponent<ParticleSystemRenderer>();
aRenderer.renderMode = ParticleSystemRenderMode.Mesh;
aRenderer.mesh = Resources.Load<Mesh>("vector");
// Configure rest of settings and emit.
var emitParams = new ParticleSystem.EmitParams();
emitParams.startLifetime = 120;
emitParams.position = new Vector3(0.0f, 0.0f, 0.0f);
emitParams.velocity = new Vector3(0.0f, 0.0f, 0.0f);
emitParams.startSize = 1;
aSystem.Emit(emitParams, 1);
aSystem.Play(); // Continue normal emissions
Debug.Log("DoEmit() called!");
}
}
Expected result: A stream of particles moving in the +z direction.
Actual result: One particle moving in the +z direction.
I needed to do something similar and found this answer in the Unity forums gave me a great, reliable solution that I've reused and modified to my purposes many times. It's a particle pool from which you can emit one or more particles as needed, killing off the last particle made and reusing it once it reaches its max number of particles. I think it achieves exactly what you're looking for. I'll include the relevant script here for reference (complete with #Artifact-Jesse's colorful commentary for clarity).
Note that this script was written for Unity 2018.x, but still works great as of 2020.2.1
Artifact-Jesse's complete, functional example of a fairly generic particle pool:
using UnityEngine;
[RequireComponent(typeof(ParticleSystem))]
public class ParticlePool : MonoBehaviour
{
private int lastParticleIndex = 0; // keeps track of our oldest particle (for deletion)
// these will all be inited in Initialize() on Start()
private ParticleSystem particleSys; // our object's particle system
private ParticleSystem.Particle[] particles; // our reusable array of particles
private ParticleSystem.EmitParams emitParams; // reusable emitparams
private int maxParticles = 0; // total number of particles in our scene before re-using
private void Awake()
{
Initialize(); // initialize all of our member variables
}
public void CreateParticle(Vector3 position, float size, Vector3 velocity, float angularVelocity)
{
// if we're at our particle count limit, kill our oldest particle.
int activeParticles = particleSys.GetParticles(particles);
/// this thing sucks. Read the Unity docs VERY carefully to understand...
/// basically the parameter (particles) is an out parameter which will
/// write out the existing particles in the particle system to our
/// reusable array. After that, we can directly modify the particles
/// and then when we're done, write the particles back into the
/// particle system with ParticleSystem.SetParticles( particles, count )
if (activeParticles >= maxParticles)
{
// set lifetime to -1 to kill the particle
particles[lastParticleIndex].remainingLifetime = -1;
// we need to reset start lifetime to a normal value, too or the particle will still have infinite lifetime
particles[lastParticleIndex].startLifetime = 1;
lastParticleIndex++; // keep track of oldest particle
if (lastParticleIndex >= maxParticles) lastParticleIndex = 0;
// have to re-write
particleSys.SetParticles(particles, particles.Length); // write those pesky particles back into our ParticleSystem
}
// set up params for this particle, you can use whatever you want (see unity docs for EmitParams for what's available)
emitParams.angularVelocity = angularVelocity;
emitParams.position = position;
emitParams.startSize = size;
emitParams.velocity = velocity;
emitParams.startLifetime = float.MaxValue; // float.MaxValue makes the particle's lifetime infinite
particleSys.Emit(emitParams, 1);
particleSys.Play();
}
void Initialize()
{
if (particleSys == null)
particleSys = GetComponent<ParticleSystem>();
maxParticles = particleSys.main.maxParticles;
if (particles == null || particles.Length < particleSys.main.maxParticles)
particles = new ParticleSystem.Particle[particleSys.main.maxParticles];
}
}
Note: the particle system in this example is just bare mininum with a renderer. Not looping, no start on awake, simulation space set to World. MaxParticles is respected by the above script.
I have a spawner object. Every time a gameobject is spawned, I want that object to move randomly (wandering). The problem in my script is that the gameobject movement is very random (jittery). How can I solve this?
void Start ()
{
InvokeRepeating("SpawnNPC", Spawntime, Spawntime);
}
// Update is called once per frame
void Update () {
population = GameObject.FindGameObjectsWithTag("NPCobject");
for (int i = 0; i < population.Length; i++)
{
getNewPosition();
if (population[i].transform.position != pos)
{
population[i].transform.position = Vector3.MoveTowards(population[i].transform.position, pos, .1f);
}
}
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
I made the New randomize vector in different method, because I plan to change it with pathfinder function and make it in different thread/task.
You are choosing a new direction every single frame. That will always be very jittery. You wan't to only change direction after, at least, a small interval. Here is a link to one way to do that from Unity's website. https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
What about using Navigation? As you said wandering, I thought it would give you a nice result and also make your code simple.
The following screenshot is a sample with Navigation. The moving game objects are also changing their direction nicely, although it cannot be seen in the sample because the game object is capsule...
Ground game object in the sample program has NavMesh. See here to build NavMesh.
Agent game object has NavMeshAgent Component. See here to set it up.
Th Behaviour class below is for Agent game object.
using UnityEngine;
using UnityEngine.AI;
public class NavAgentBehaviour : MonoBehaviour {
public Transform[] Destinations { get; set; }
// Use this for initialization
void Start ()
{
InvokeRepeating("changeDestination", 0f, 3f);
}
void changeDestination()
{
var agent = GetComponent<NavMeshAgent>();
agent.destination = Destinations[Random.Range(0, Destinations.Length)].position;
}
}
The next Behaviour class is just for spawning the Agent and setting up the destinations. On Unity, set it to whatever game object in the scene, and allocate game objects to the fields.
using UnityEngine;
public class GameBehaviour : MonoBehaviour {
public GameObject Agent;
public Transform SpawnPoint;
public Transform Destination1;
public Transform Destination2;
public Transform Destination3;
// Use this for initialization
void Start()
{
Agent.SetActive(false);
InvokeRepeating("Spawn", 0f, 2f);
}
void Spawn() {
var newAgent = Instantiate(Agent);
newAgent.GetComponent<NavAgentBehaviour>().Destinations = new[] { Destination1, Destination2, Destination3 };
newAgent.transform.position = SpawnPoint.position;
newAgent.SetActive(true);
}
}
Increase the number of destination, to make the moves look more random. By the way, the destinations do not need to be specified by game objects, which is only for making it easy to see the sample program's behaviour.
The source of the jitteriness comes from the fact that you are updating the position to move every frame so your objects never have a consistent location to move to. I would instead suggest attaching a new script to each of your objects that individually handles their movement. In that script you could do something like the following, which has a delay to keep the target position for more than 1 frame.
float delaytimer;
Vector3 pos;
void Start () {
getNewPosition(); // get initial targetpos
}
void Update () {
delaytimer += Time.deltaTime;
if (delaytimer > 1) // time to wait
{
getNewPosition(); //get new position every 1 second
delaytimer = 0f; // reset timer
}
transform.position = Vector3.MoveTowards(transform.position, pos, .1f);
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
You are changing the direction they are moving in every frame, thats what is causing the jitter.
You could wait a few moments before you change the direction, perhaps something like this.
// Outside update
float betweenChanges = 2;
float lastChange = 0;
// Inside update
if(Time.realtimeSinceStartup > lastChange)
{
// Change directions
// ...
lastChange = Time.realTimeSinceStart + betweenChanges;
}
You could also solve this by using InvokeRepeating or a Coroutine.
If you dont want all the NPC's to change direction at the same time and still plan on controlling every NPC from the same class like in your example, you should perhaps add a timer for each NPC instance instead and use that to decide when to change its direction.
A better idea would be to let each NPC have its own Movement-script.
using System;
using UnityEngine;
using System.Collections;
using UnityStandardAssets.Characters.ThirdPerson;
public class Multiple_objects : MonoBehaviour {
public GameObject prefab;
public GameObject[] gos;
public int NumberOfObjects;
private ThirdPersonCharacter[] thirdPersonCharacter;
private Animator[] _animator;
private int count = 0;
void Awake()
{
Vector3 v3 = prefab.transform.position;
_animator = new Animator[NumberOfObjects];
gos = new GameObject[NumberOfObjects];
for(int i = 0; i < gos.Length; i++)
{
count = count + 2;
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos [i] = clone;
gos [i].transform.position = new Vector3 (v3.x - count, v3.y, v3.z);
_animator [i] = gos[i].GetComponent<Animator> ();
Math.Round(Random
When i type point after the Random like: Random.
I have only Equals and ReferenceEquals
And if i create a variable of Random for example:
Random _random;
Then i type _random.
I get more propeties but not Range.
You are using both the UnityEngine and System namespace. Both of these namespaces contain a Random class, so Visual Studio/Unity doesn't know which one you want to use. To specify which random you want to use, you would simply do this:
UnityEngine.Random.Range(0.0f, 5.0f);
Write UnityEngine.Random.Range
You have to clarify the namespace.
Unless you want the .net Random ( in that case look at the other answer)
Why the property Range not exist in Random class in unity?
Random _random;
_random.Rand...
Range is a static function in the Random class. You don't need to create instance of the class to use static functions inside then. You call static functions directly.
This should do it: Random.Range(0f,3f);
If you are getting the Random' is an ambiguous reference betweenSystem.Random' and UnityEngine.Random' error then that's because you haveusing System;`(which you did in your code) and therefore you must use the full namespace to access Unity random function.
UnityEngine.Random.Range(0f, 3f);