For a modern game, is there any particular reason why 'seed = SHA256(seed + "reseed"); return SHA256(seed + "value")' isn't a good enough, performant enough RNG?
I don't know enough about SHA256 to say whether or not it's good enough, I'll assume it is.
Performant enough strongly depends on the game. However looking at the implementation of the algorithm on wikipedia[0], that looks extremely slow by RNG standards. Most RNGs finish in a handful of instructions, but building the 'message schedule array' takes 64 steps, and the 'main compression loop' also takes 64 steps. Consider that to reduce these to a range you'll have to throw out iterations (you need to do this with any RNG to avoid bias), you're looking at a lot of wasted time.
Maybe you can afford to waste that time, but given that there are much simpler and more efficient RNG algorithms, I'm not sure why you wouldn't use one of those.
As long as your initial seed is sufficiently random that would be perfectly fine for a game, the bits returned should be unpredictable enough that a pattern shouldn't be spotted (assuming you use a hidden value rather than "reseed" being a fixed string - otherwise someone with a rainbow table for your chosen hash&salt will be able to predict the generator's output. Also you know the output for a given start seed which is good for testing purposes.
Of course you need to make sure the initial seed is sufficiently random otherwise you'll risk generating the same sequence each time, which could make this a chicken->egg->chicken situation!
> > For games, the most important features (other than the features of an RNG that all applications desire, such as good performance, high period, etc.) are being able to seed the state, and save/restore the state (required for save game support if you need seeding and want that to work after they save and restore the game).
Given that restriction, what's wrong with the generator I suggest?
No, it's a pseudorandom generator. It's a key attribute of a cryptographically secure function like SHA256 that its output is indistinguishable from random bits.
> You are just returning a different representation of the seed you already have.
I'm returning a function of the seed, yes. That's the whole point of a seeded PRNG: that seed(x); rng(); rng(); rng() will always return the same sequence of results. This is used for example in procedural generation, to ensure that a dungeon level or map chunk is always generated the same way (a cool hack, then, is to just store the seed instead of the level itself, and rerun the level-generation algorithm with the seed when one wishes to regenerate the level). In such an application one wants the appearance of randomness, but with repeatability.
Which was what the original post asked for.
What I suggested is not secure for generating keys or for other cryptographic purposes.
Yes, if you fed the output of a SHA256 calculation back into the algorithm repeatedly you would get a pseudo-random stream. I think that is what you intended. Your original post was not clear on that and I thought you were just taking a value and returning a mutation of it using some unknown "reseed" and "value".
No, it is a property of all hash functions - if you reduce input of arbitrary length to few bytes there will always be short circles. It is why you should always think twice before feeding output from one hash function call to another as the set of possible output values might be smaller and smaller with every repetition.