The only random functionality Lua provides is math.random
. It does three things:
- When called with no arguments, returns a real number between 0 and 1 (includes 0, excludes 1)
math.random()
--> 0.58201314802
- When called with one argument, returns a whole number between 1 and that number
- When called with two arguments, returns a whole number between the two arguments
math.random(17, 20)
--> 18
Remember that at a fundamental level, everything on the computer is just numbers, so this shouldn't be too discouraging. If we have random numbers, we should be able to get random anything.
Random Point along a Line
Let's say you want a random number x
between low
and high
. Then we can write x
as low + m
where m
is between 0
and high - low
.
As a result, we can generate x
as
1 | x = low + (high - low) * math.random() |
This is uniformly between low
and high
(including decimal numbers).
You can use the same math to generate points randomly between two Vector3s.
For the types that have a :Lerp
method like Color3, Vector3, and CFrame, you can also take advantage of math.random()
in a similar way:
1 | x = low:lerp(high, math.random()) |
Random Things from Lists
For instance, let's say we want to pick a random thing from a list:
1 | flavors = { "chocolate" , "vanilla" , "strawberry" } |
We can only pick random numbers, so what exactly is numbered about this list? There's an obvious thing: the keys. flavors[1]
is "chocolate"
while flavors[3]
is "strawberry"
.
That means if we can pick a random number of 1
, 2
, and 3
, we can turn that into a random element of flavors
.
In general, to choose a random element from list
, you can use
1 | randomElement = list [ math.random(#list) ] |
This is the fundamental way that everything is done. Figure it out in terms of numbers!
Random Positions
One way you can get "random positions" is when you're doing something like picking a random spawn location. In that case, you would probably actually use picking a random thing from a list.
Other times, you want to pick a random point in a particular region.
Random Position in Rectangle
A parallelogram (e.g., rectangle) can be defined in terms of a corner and its two sides as vectors.
For instance
1 | start = Vector 3. new( 0 , 20 , 0 ) |
2 | right = Vector 3. new( 10 , 0 , 0 ) |
3 | up = Vector 3. new( 0 , 30 , 0 ) |
we can generate a random point inside pretty easily:
4 | local point = start + right * u + up * v |
Often, these rectangles will be ones parallel to the XZ plane. This makes the above form in a way simpler:
01 | wide = Vector 3. new( 1 , 0 , 0 ) * width |
02 | tall = Vector 3. new( 0 , 0 , 1 ) * height |
04 | local point = start + wide * math.random() + tall * math.random() |
07 | + Vector 3. new(math.random() * width, 0 , 0 ) |
08 | + Vector 3. new( 0 , 0 , math.random() * height) |
11 | + Vector 3. new( math.random() * width, 0 , math.random() * height ) |
Often I see code that looks like this:
1 | pos = Vector 3. new( math.random(- 50 , 200 ), 0 , math.random(- 30 , 100 ) ) |
I would consider this to be wrong, usually, because it will only generate them to the nearest integer. You should use the full range of numbers, like in the above:
1 | pos = Vector 3. new( - 50 + math.random() * 250 , 0 , - 30 + math.random() * 130 ) |
Random Point in Sphere
At first, it would seem that generating a random point inside of a sphere would not be very different.
Here's a simple (bad) solution:
05 | math.random() * 2 - 1 , |
06 | math.random() * 2 - 1 , |
07 | math.random() * 2 - 1 ) |
10 | function randomSphere() |
11 | local cube = randomCube() |
12 | if cube.magnitude > 1 then |
The idea is that we just "clamp" the points outside of the unit sphere to the surface. While this will generate random points and they will all be in a sphere, they won't be uniform. Since there's a lot of space outside of the sphere near the corners of the cube, there will be 6 points on this "sphere" that are much more common than the other points.
That would mean if we used the above (or for that matter, simply randomCube
) for inaccuracy in a game, you would have a different amount of inaccuracy when firing diagonally as opposed to horizontally.
What's the solution? There aren't overly nice ones. (You can search for them online). A simple (but very slow) option is to just repeat until you get something inside the sphere:
4 | if c.magnitude < 1 then |
See also Sphere Point Picking on MathWorld for another way to do it (and also why using polar coordinates isn't a solution)
If there's another specific thing you want to ask about, comment on it and I will write a portion on it.