- Home /
Random Generation Algorithm Error
I am creating a 2d game where i am instantiating(Infinite Terrain Generation) blocks as "Rock". I have a script attached to my rocks, which identifies whether the rock will be an normal rock or ore, the attached script is -
#pragma strict
var ores : Sprite[]; // ore sprites
var oreDat : int[]; // not relevant
var oreHeal : int[]; // not relevant
var oreChance : float[]; // stores chances for each ore
var sel : float;
private var health : int = 1;
private var dat : int = 0;
function Start()
{
sel = 0.0;
gen();
}
function Mine() // not relevant
{
health--;
if (health <= 0)
{
Destroy(gameObject);
}
}
function gen()
{
sel = Random.value; // generates a random value between 0 and 1
for (var i : int = (ores.Length-1); i >= 0; i--)
{
if (sel <= oreChance[i])
{
GetComponent(SpriteRenderer).sprite = ores[i];
health = oreHeal[i];
dat = oreDat[i];
break;
}
}
}
in my OreChance variable i have arranged chances of my ores ( in order coal, iron, gold, diamond) as 0.1, 0.001, 0.0005, 0.0001
It works fine but the error i encounter is, when i hit play and move it starts generating ores in a repetition, like iron after every 2 blocks, it varies everytime i hit play. Also i have noticed it creates ores even when the sel(selection chance) is more than ore chance.
this is what i get when i just start game, as you can see everything is fine...
this is what happens when i walk ahead(any direction)...as you can see, ores are occuring in a repetition and even though the sel chance is greater than the ore chance. To say more specifically, the new blocks instantiated are exactly as previous blocks. I have tried many solutions already, like using Random.Range ,etc. but every solutions gives this problem.
As already mentioned i am using infinite terrain generation, so if needed i can also attach it's script also.
What do your oreChance[] values look like? Unless these are sorted in increasing order, with the last one set to 1.0, this will not work. And in that case they're misnamed; they're not chances, they're the cumulative chance including all previous ores.
If they really are chance values, then the code is incorrect, because you're just comparing sel to each one. You should add "sel -= oreChance[i]" inside the for loop (but outside the if). And then of course you must make sure that your oreChance[i] values add up to 1.
I have already mentioned, oreChances are in order Coal, Iron, Gold, Diamond with their respective chances as 0.1, 0.001, 0.0005, 0.0001. And why should i set the last chance to 1, because by default my "Rock" is a rock, so if sel does not matches to any ore, it will remain a rock. I runnning the loop in reverse, where it checks the diamond first, and i am also using 'break' statement to prevent less valuable ore from instantiating if the sel matches with a high value ore...
but is this relatied to repetition ? also, can you please clarify sel -= oreChance[i]...
O$$anonymous$$, sorry I missed it where you said it before. But yes, those are true probabilities, which means your code is wrong and you need to change it as described above. And you're right, if you're O$$anonymous$$ with it matching rock if it doesn't match anything else, then you don't need to include that (and the probabilities don't need to add up to 1). I'll post this as an answer so you can accept it. :)
(Also note that it doesn't matter whether you loop over your ores forward or reverse.)
Answer by Bunny83 · Apr 21, 2015 at 04:26 PM
Your problem clearly is in the way you instantiate your chunks. You said you instantiate a prefab, but somehow you seem to replaced that reference with the chunk generated last. That means when you instantiate a new chunk you get a copy of your old chunk. Since you never set the base material explicitly ("rock" i guess), each tile will initially be what has been generated in the last chunk. Furthermore each new chunk will of course create new ores.
Solutions:
Don't overwrite your prefab reference. Keep in mind that a prefab can't hold a reference to itself. Self-references will be replaced with the instantiated reference when the prefab is instantiated. To fix this, either store the prefab reference golbally outside of the prefab, or reassign the reference after instantiating the prefab.
If you can't fix the old-chunk-instantiate problem, you should explicitly assign the "rock" sprite in Start to provide the same starting for the gen method.
thank you so much... i think the self referencing was the problem as i mentioned in the comments of other answers, i was actaully instantiating the same prefab from it self, which was i think causing this, to fix this i have now created 2 prefabs ins$$anonymous$$d of one, and referenced them to one anotherr ins$$anonymous$$d of self referencing, this fixed the problem as far as i've noticed, i am still testing by changing the script to expicitly change it to "Rock" when no other chances match... thank you so much...
@Bunny83 can you please confirm me one thing, that creating 2 prefabs ins$$anonymous$$d of one to avoid self referencing is the right way of doing so or not...
@Sarthak123: Well, there is no "right" or "wrong". Using two prefabs (which reference each other) is one way to prevent this problem. Another way is to use a manager object / singleton which just has a list of prefabs you might need somewhere else.
Another way, like i mentioned in my answer is to reassign the reference when you instantiate the object. Example:
// script that instantiates the first instance.
public class SomeOtherScript : $$anonymous$$onoBehaviour
{
public PScript prefab;
void CreateInstance()
{
var inst = Instantiate(prefab);
// here we "fix" the reference in the new instance.
inst.prefabReference = perfab;
}
}
// script on the prefab
public class PScript : $$anonymous$$onoBehaviour
{
public PScript prefabReference;
void CreateNewInstance()
{
var inst = Instantiate(prefabReference) as PScript;
// same if the instance creates a new instance
inst.prefabReference = prefabReference;
}
}
Answer by JoeStrout · Apr 21, 2015 at 01:27 PM
OK, the problem is that in your loop, you are comparing your randomly-drawn number (from 0 to 1) to each of the probabilities. This doesn't work. Suppose, just for the sake of argument, that you had four ore types, all with an equal probability of 0.1. The first time through the loop, you'd compare sel to 0.1, and either it's < 0.1 or it's not. If it is, you pick the first ore. If not, you go on and compare sel to 0.1 again for the second ore... but it's still not < 0.1 (of course!). So you'll either get the first ore, or nothing, with this code.
The fix is easy: you need to add this inside your loop, but after the if-block:
sel -= oreChance[i];
Now consider the example above, with four ores all with a 0.1 chance. Suppose you draw 0.35 as your sel value. Compare to the first ore: 0.35 < 0.1? Nope, so we subtract 0.1 from 0.35 and continue. Second ore: 0.25 < 0.1? Nope. Third ore: 0.15 < 0.1? Still nope. Fourth ore: 0.05 < 0.1? YES! So in fact anything in the range 0.3 - 0.4 would map to the fourth ore, exactly as you'd want in this case.
umm... there are 2 things, i am unclear of, yes, the above method you suggested, increases my ore chances, but you are wrong when you said you will get first or nothing, i am getting all kinds of ore even after not using the above method...and if my script matches with first, it never checks for second, i am using the break statement, it will break the loop...
you are right i can use loop in forward as well as reverse, but to use in reverse, i used the break statement, there's a reason why used it in reverse but nvm this is not important, my real problem is unsolved,
$$anonymous$$y real problem is,
blocks keep repetiting, i am instantiating them from the prefab so that might be the issue, i have uploaded the screen shots to clarify my problems, please help it's unsolved no matter what i do....
You will get first or nothing IF you had four blocks with the same probability, as I said ("Suppose, just for the sake of argument, that you had four ore types, all with an equal probability of 0.1.") This was just an example to illustrate why your code was incorrect. The correction I've posted here will fix that.
However, it appears that there is more than one problem here. The change I've suggested only fixes one problem.
As to the other problem: if it is fine at first, but then goes wrong when you walk around, then the problem is not in the code you have posted at all. It has something to do with what happens when you move around -- probably that "infinite terrain generation" script you mentioned. I suggest posting a new question focused on that.
actually, the seconds problem was the main from the start, this questions is focused on that, i found a working fix myself, what i was doing was, instantiating from a prefab, a single prefab, which in case was the cause of the problem i think... ins$$anonymous$$d of using single prefab i used 2 prefabs, both of them linked to one another(ins$$anonymous$$d of linking to themselves). This fixes the problem as far as i've noticed, but i would still like to know, why, how, what happened that caused this...
and yes, i never noticed you were absolutely right, when all the ores havfe same chance, my script wont work, thanks for pointing out that, but i wuld like to know about the "Second" problem as that is my main concern
@JoeStrout: His code works fine. You just made the assumption that the probability values he uses should be (programmer) percentages (0-1). He just uses probability thresholds and since he's iterating over them in increasing order (he iterates backwards!!) he get's this percentages:
Coal 0.010 - 0.050 --> 0.04 (4%)
Iron 0.005 - 0.010 --> 0.005 (0.5%)
Gold 0.001 - 0.005 --> 0.004 (0.4%)
Diamond 0.000 - 0.001 --> 0.001 (0.1%)
It's certainly not a recommended way of handling probabilities but it does work. The problem, as i explained in my answer, is that he doesn't really handle the remaining 95% probably and he expects it to be "rock" when it actually isn't.
It's pretty much the same as printing on a piece of paper that isn't blank. The result will be a mixture of the newly printed stuff mixed with what was on the paper previously.
Answer by Fappp · Apr 21, 2015 at 03:15 PM
Thank you! I had some fun with this one :)
What's important to consider is that Random.value or Random.Range isn't truly random. Randomness in computers is a topic on it's own and even has it's own specialized hardware.
More on that here: https://www.random.org/
I made a simple script that takes has a few random values, which in turn control two bools. Those all combined set the position of a gizmo ( or cube ) and the distance from this gizmo towards vector 3 zero defines the extended random value generated by this script ( after it's normalized and sin'd).
My point is, I believe repetition is showing because of limitations with the random values. Wonder what this script'll do to your 2d grid ^_^ ( in the script you need to press Return to get a new value )
var randomValue : float;
var randomRange : float;
var randomRangeExtended : float;
var randomPositionGizmo : Vector3;
var rndPosVisual : GameObject;
var pos : boolean;
var xTrue : boolean;
function Start () {
randomValue = Random.value;
randomRange = Random.RandomRange(0.0,1.0);
}
function Update () {
var rndDir : float = Random.value;
var rndX : float = Random.value;
if ( rndDir >= 0.4999 ){
pos = true;
}
else{
pos = false;
}
if ( rndX >= 0.4999 ){
xTrue = true;
}
else{
xTrue = false;
}
if ( xTrue && pos){
randomPositionGizmo.x -= rndDir;
}
if ( !xTrue && pos){
randomPositionGizmo.z += rndDir;
}
if ( xTrue && !pos){
randomPositionGizmo.x += rndDir * 3;
}
if ( !xTrue && !pos){
randomPositionGizmo.z -= rndDir * 3;
}
rndPosVisual.transform.position = randomPositionGizmo;
if ( Input.GetKeyDown(KeyCode.Return) ){
randomValue = Random.value;
randomRange = Random.RandomRange(0.0,1.0);
randomRangeExtended = Mathf.Abs(Mathf.Sin(randomRange * Random.Range(0, Vector3.Distance( Vector3.zero, randomPositionGizmo) )));
}
}
hmm... nice concept... i was also curious about how computers generate randomness, and i also knew that there might be repetitions, but fixed repetitions and very quick repetitions like what i am experiencing are not related with randomness nature, this script proved that, i think it's something related with unity's prefab instantiation thing...
actually i was just playing around with my instantiation commands and i was able to fix my problem,
what i was doing was, instantiating from a prefab, a single prefab, which in case was the cause of the problem i think... ins$$anonymous$$d of using single prefab i used 2 prefabs, both of them linked to one another(ins$$anonymous$$d of linking to themselves). This fixes the problem as far as i've noticed, but i would still like to know, why, how, what happened that caused this...
i am still playing with the above script you gave, i will upload more results...
There shouldn't be any problem instantiating prefabs as many times as you want, but there can be problems when a prefab has references to other prefabs. These references will still point at the other prefab in the instance, and so if you use that to change something, you're actually changing the prefab. Not sure if this is involved here, but your mention of "linking" makes me think it might be.
And note that Unity's random number generator is very good. I don't think the quality of Random.range has anything at all to do with the problems you're seeing.
yes i have got my problem, it's prefab instantiation and self referencing, thank you everyone, i respect every answer...:)
You're very welcome! Thank you for giving me ideas for randomization!
Your answer
Follow this Question
Related Questions
Random objects in coordinates specific 1 Answer
Instantiate Prefab at random times but keep 3 from spawning 2 Answers
Random.Range is not changing? 1 Answer
Random.Range doesnt work anymore 4 Answers