1

I have recently used Visual Basic .Net to write a particle system which emits particles with random velocities in the x and y direction and is affected by gravity. I switched to C# .Net and used the XNA Game Studio which makes the graphics handling much more convenient than GDI+.

The problem I have with C# is that the random numbers are not "random enough". My particle system has 2500 particles but you can clearly see that the particles are distributed in a grid-like fashion about 100 pixels apart and I did not have that problem with Visual Basic's Rnd() function.

What does Visual Basic do which C# does not, and how can I get the same results in C#?

I have tried to re-initialise my random numbers at different stages of the game loop but I end up either with my particles staying at one position or emitting just in a constant stream in one direction.

This is my C# code: LoadContent is called first thing after the program has started. I'm using the millisecond as a seed just so that I start each time with a different configuration.

The next time I re-seed is after all the calculations are done on the system just before rendering. The other alternative I tried is to re-seed after every 100 particles have been calculated but with no difference.

        protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);
        mousePos = Content.Load<Texture2D>("Point");
        spriteTex = Content.Load<Texture2D >("Point");
        rnd = new Random(a.Millisecond);
        for (int i = 0; i < spritePos .Length; i++)
        {
            newPos[i] = new Vector2(800, 450);
            spritePos[i] = newPos[i] ;
            Scale[i] = rnd.Next(100,500);
            renderCol[i] = new Color(rnd.Next(255), rnd.Next(255), rnd.Next(255), 1);
            spriteVelocity[i] = new Vector2((rnd.Next(2000)-1000)/100, -rnd.Next(500,1500)/100);
            Life[i] = rnd.Next(60);
            Rotate[i] = (rnd.Next(1000)-500) * 0.001f;
            RotateSpeed[i] = (rnd.Next(1000)-500) * 0.0001f;

        }

    }

This is my VB code, the only place where I use the rnd function:

            For i = x To x + 1000
                ptc(i) = New particle(New Vector((Rnd() * 200) - 200 * Rnd(), Rnd() * -100 - 200 * Rnd() - 200), New Vector(e.X, e.Y), 500, Color.FromArgb(Rnd() * 255, 255, 0))
                x += 1
            Next

In my VB code there is no place where I call the randomize function, I have noticed that my particles have the same pattern-like behaviour if I do. Excuse all the strange arithmetic, it's all just experimentation.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
ChP
  • 466
  • 1
  • 4
  • 17
  • Can you show us your code (both VB.net and C#)? – dtb Aug 17 '11 at 20:11
  • Can you post a snippet of code? Namely, the code that initializes a new particle and sets its velocity. – Steve Wortham Aug 17 '11 at 20:12
  • Also ensure that you're not creating a new `Random` instance for each random number you want to create [(link)](http://stackoverflow.com/questions/767999/random-number-generator-not-working-the-way-i-had-planned-c). – dtb Aug 17 '11 at 20:13
  • ((rnd.Next(2000)-1000)/100 use Random.Next(int maxValue) which generates random integers between 0 and maxValue. I think this might be the cause of the grid like look. Try Random.NextDouble() – Just another metaprogrammer Aug 17 '11 at 21:05

3 Answers3

3

Ok, what you do to fix it Charl, is to create a single Random object (which you are) with no seed, and use it over and over again. So something like:

const double max = 1000.0;
Random rand = new Random();     // Like mentioned, don't provide a seed, .NET already picks a great seed for you
for(int iParticle = 0; iParticle < 2500; iParticle++)
{
    double x = rand.NextDouble() * max;     // Will generate a random number between 0 and max
    double y = rand.NextDouble() * max;
}

To get a random floating point value (float or double) between a lower and upper bound, you can use something like:

double x = (rand.NextDouble() * (max - min)) + min;

EDIT: And make sure to use double or float. Ints are whole numbers only, doubles and floats can store real numbers and is probably what VB was using.

John McDonald
  • 1,790
  • 13
  • 20
  • Note: If you actually need an Integer value, like for Color, using rand.Next(255) is perfect, that's what you should be using. – John McDonald Aug 17 '11 at 20:51
  • Thank you very much, this is the perfect solution! So the integers were the problem. – ChP Aug 17 '11 at 21:07
  • I don't mind it for the colours, it's a bit harder to see any pattern there, but now my particles are actually "random enough". – ChP Aug 17 '11 at 21:09
1

If you had posted some code we would probably have been able to point out where you are creating a new Random() object for each call .

Like in, for example, Random number in a loop


After seeing the code,

are you aware that (rnd.Next(2000)-1000)/100 is an integer only expression? The result will be converted to float but always end in ##.0.

In VB I / J yields a double.

Community
  • 1
  • 1
H H
  • 263,252
  • 30
  • 330
  • 514
  • Thanks, but declaring my random number in the loop just makes my particles seem to "orbit around the origin, it doesn't move at all. That's why I'm so stumped, any attempt to remedy my dilemma only worsens it, but I'm sure there's a solution. – ChP Aug 17 '11 at 20:47
  • Yes, I tried John McD's answer and it was indeed the integers giving me problems, and C# complains about type casting much more than VB does. – ChP Aug 17 '11 at 21:17
-1

Random class ensures pseudo-randomness, if that is what you are using. Have a look at RNGCryptoServiceProvider.

Vladimir
  • 3,599
  • 18
  • 18
  • I've read much about random numbers and CSRNG's most of it on this forum and in statistics we also learned about pseudo-random numbers and that they are in fact not random. What bugs me is that the simple VB function Rnd() gives me adequate results but C# doesn't. They are both .Net so I figured that they would use the same algorithm, but C# doesn't have an Rnd() function. – ChP Aug 17 '11 at 20:56