Here's the story. I have created a mining game (yes I know there are to many of them already but that's not the point) but I am having trouble with making a seemingly infinite mine. I have made a script that checks through a bunch of Vector3 values every time someone tries to mine. It checks if the position next to the block being mined is one of the Vector3Values and if it isn't, then it creates a block there and put's it's position among the Vector3Values. There are six sides to a block so it has to check for six different values in several hundred Vector3Values. At the moment, this system works but it is not very efficient and is taxing on the server (run on a standard server script). I have asked others about this before and they said to spawn in the blocks when a player is near them and de-spawn once the player is far enough away. How might I do this efficiently with a bunch of different players on the server and fast enough that if a player is falling through a mine, it doesn't create lag or latency? I am not asking for a script but an idea for it that can be built upon. I hope to have a final system with similar efficiency as The Quarry or Epic Mining 2.
I'm going to distill your problem down to something simple:
You have a list of things (Vector3s), and you want to quickly know whether a given
v
is in that list.
Your current solution -- compare it to everything in the list -- is called linear search (linear because you check all of them in the line of the list). If you have 100, this will take 100.
Ideally you could ask directly whether or not a container contains what you want. You can use dictionaries to do this -- dictionaries don't get slower when you put more things in them -- so it will be very fast1. Note that Vector3s themselves can't be the key of the dictionary (for technical reasons2) but you can use something like tostring
on them to produce keys:
local rockLocations = {} -- use as such: local v = Vector3.new(10, 10, 20) local p = Instance.new("Part", workspace) p.CFrame = CFrame.new(v) -- rockLocations[ tostring(v) ] = p
Or just true
instead of p
if you don't need to keep track of what is at that location (but you probably do)
Note that this will only work nicely if all of your vector3s have integers for x
y
and z
.
If you have a bunch of scripts that need to be able to use this data, you could use a ModuleScript to share.
A simple way to make this work is just make the module script return the empty table object:
return {}
Every script that require
s this module will get the same table -- any change any one makes, all of them see that change. Note: This won't share between Script and LocalScripts, or LocalScripts for different players.
A better thing to do would be to make get
and put
functions so that your script using the module don't have to worry about just using Vector3 values:
local r = {} -- e.g., assuming you're on a 4x4x4 grid, this will return the object -- at position v even if `v` isn't perfectly on the grid -- (e.g., 0.23, 0.58, 1.87 will mean the first cell (0,0,0) and 6.83, 3.9, 38.0 will mean (1, 0, 0)) local function key(v) local kx, ky, kz = math.floor(v.x / 4), math.floor(v.y / 4), math.floor(v.z / 4) return kx .. "," .. ky .. "," .. kz end function r:get(v) return r[ key(v) ] end function r:put(v, obj) r[key(v)] = obj end return r
on average. ↩
Keys are compared essentially using rawget
; two Vector3s at the same position in the world are different objects so they will be put in different places in the dictionary. Strings or numbers don't have that problem -- when ==
is true they are also the same object (which is actually quite remarkable for strings) ↩