- Home /
Random Note Generation
Hello! I would like to know a simple way to generate a specific amount of notes like in the game Slender. I have no idea how I can code this and I can't find any tutorials. I would like it to be like like you can place out duplicates of a note all around the map and you can like place 20 notes but when you set the amount of notes you want to be placed into the map, you only get that amount. So I can place 20 notes and set the amount to 2. Then only two of the 20 notes will be active and all other notes will be disabled.
So if anyone would know how to do this, please tell me. :)
Answer by vexe · Oct 06, 2013 at 06:13 AM
It doesn't make sense to place them at random locations, instead you place them at pre-set locations and then choose between them in random if you want.
1- Place the notes in your scene, wherever you want.
2- Make a NoteManager
or something... just to manage (de)activating the notes.
3- To set this up right, create an empty gameObject
call it NoteManager
, attach the NoteManager
script to it, and then have all your notes be children to it, like so:
NoteManager.cs
using System.Collections.Generic;
using UnityEngine;
public class NoteManager : MonoBehaviour
{
// this will maintain references to your notes after you set their locations in your scene.
public Transform[] notes { private set; get; }
// how many notes you wanna be activated at first
public int nNotesToActivate;
// whether you wanna activate your notes randomly, or from the start of the notes array
public bool random;
void Start()
{
InitNotes();
ActivateRequiredNotes();
}
void InitNotes()
{
int nNotes = transform.childCount;
notes = new Transform[nNotes];
for (int i = 0; i < nNotes; i++) {
notes[i] = transform.GetChild(i);
}
}
void ActivateRequiredNotes()
{
// make sure we stay within the bounds of the array
nNotesToActivate = Mathf.Clamp(nNotesToActivate, 0, notes.Length);
// We'll activate 'nNotesToActivate' notes, and disable the rest
if (random) {
// choose random locations for our notes (from our preset locations)
List <int> randLocations = new List<int>();
for (int i = 0; i < nNotesToActivate; i++)
{
int rand = Random.Range(0, notes.Length);
// Make sure we don't get the same rand (Thanks to @tanoshimi for reminding me about it)
while (randLocations.Contains(rand))
rand = Random.Range(0, notes.Length);
randLocations.Add(rand);
}
// activate the notes at those random locations and disable the rest
for (int i = 0, len = notes.Length; i < len; i++)
{
if (randLocations.Contains(i)) {
if (!notes[i].gameObject.activeSelf)
notes[i].gameObject.SetActive(true);
randLocations.Remove(i);
}
else notes[i].gameObject.SetActive(false);
}
}
else {
// if you chose to activate only 2 notes, then only the first and second note will be active, the rest gets disabled
for (int i = 0, len = notes.Length; i < len; i++)
notes[i].gameObject.SetActive(i < nNotesToActivate);
}
}
}
Thanks to the InitNotes
method, now you don't have to bother yourself and assign the notes to the notes
array, it happens automatically for you. If you want to manually assign the notes
(not sure why would you wanna do that), remove the call InitNotes
and make your notes
a normal public variable, instead of a property to make it visible in the inspector.
Here's something extra: If you wanted to change the number of notes at runtime and update them, add:
public bool updateNotes = false;
void Update()
{
if (updateNotes)
{
updateNotes = false;
ActivateRequiredNotes();
}
}
Now you can change the number of nNotesToActivate
from your inspector, once you're satisfied just tick updateNotes
and it will update it for you :)
This is a better answer, but note that...
for (int i = 0; i < nNotesToActivate; i++)
randLocations[i] = notes[Random.Range(0, notes.Length);
...will lead to an array of the appropriate length for the number of notes to be activated, but it may contain the same note in that array several times, so the end result is unlikely to have the correct number of activated notes. You should add a test to ensure randLocations does not already contain this element before incrementing the counter.
ahhh yes, you're more than correct I actually thought of that but forgot to do it. +1 thanks for re$$anonymous$$ding me. And btw, it should be randLocations[i] = Random.Range...
(I corrected that before your comment) because it's an array of int
, not Transform
@Real$$anonymous$$TG: don't forget to tick the answer as correct.
Answer by RyanZimmerman87 · Oct 06, 2013 at 03:39 AM
It really depends do you want the note locations to be random? Or do you want to have 20 preset locations and then assign 2 of them for the note randomly?
It's not the prettiest solution but would work.
If you want preset locations you could do something like:
//RandomNoteScript
//set the prefab
public GameObject noteObject;
//set all your note positions
//easy way to do this is just to create a bunch of empty game objects in the scene where you want the notes to be able to spawn.
//transform positions of notes (set from the scene to these variables)
public Transform noteSpawnPosition1;
public Transform noteSpawnPosition2;
//do this for all 20 and set them from the scene to the script in inspector
//float for how many notes to be spawned.
float totalNotesSpawned;
//float for how many spawn points in scene
float totalSpawnPositions;
//note counter;
int noteCounter;
//random number float
float randomNumber;
//do one of these for each possible spawned note
//this example needs 2
float firstNoteLocation;
float secondNoteLocation;
void Start()
{
//example of setting the note count for specific levels
if (currentLevel == 1)
{
totalNotesSpawned = 2;
totalSpawnPositions = 20;
}
noteCounter = 0;
noteSpawnFunction();
}
void noteSpawnFunction()
{
if (noteCounter < totalNotesSpawned)
{
//generate random number 1-20
randomNumber = Random.Range(1, totalSpawnPositions + 1);
//detect if you need to roll number again since you already spawned one at that random spawn point
if ((randomNumber == firstNoteLocation) || (randomNumber == secondNoteLocation))
{
//call function again
noteSpawnFunction();
return;
}
if (randomNumber == 1)
{
Instantiate(noteObject, noteSpawnPosition1.position, Quaternion.identity);
}
else if (randomNumber == 2)
{
Instantiate(noteObject, noteSpawnPosition2.position, Quaternion.identity);
}
//remainder of possible rolls...
if (noteCounter == 0)
{
firstNoteLocation = randomRoll;
}
else if (noteCounter == 1)
{
secondNoteLocation = randomRoll;
}
//add to counter after succesfully spawning Note
++noteCounter;
}
//if you need to spawn another note... calls the function again
if (noteCounter < totalNotesSpawned)
{
noteSpawnFunction();
}
}
[MOD] Work on your formatting skills [/MOD]
Why are you using separate variables? - ever heard of arrays?
I've only been program$$anonymous$$g for about a year, at this point I still feel a lot more comfortable with unique variables for stuff ins$$anonymous$$d of arrays. $$anonymous$$akes it easier for me to keep track of all the logic, that's why I said "It's not the prettiest solution but would work."
I will look over your answer though, contains lots of stuff I have never used yet. About time to start learning new methods for more efficiency.
If you really wanna learn some new badass tools, take a look at Jamie $$anonymous$$ing's, prime31 and UnityGems.
Sorry if I sounded like an asshole, but your code kinda re$$anonymous$$ded me of Design3's code (I dislike them lol)
Arrays will make your code shorter, easier to read and maintain, especially if you had parallel things going on, like you needed 'n' gameObjects, 'n' Transforms and 'n' X. If you were using separate variables, anytime you wanted to add a new Transform/gameObject/X, you'd have to modify and add a gameObject and X variables as well, while if you were using arrays, you'd just increment the size variable that you're using (assu$$anonymous$$g you got init logic), list goes on. :)