# Infinite Mining Ore Generation

Hey. I’ve been working on a concept for an infinite mining game. I have done a fair share of research almost everywhere, and have run into a roadblock on ore generation. I’m using rays for when a player mines a block they will check if there are blocks around the player, and if there aren’t blocks around the player it will generate them. I’m sure most of you guys have seen this system somewhere, but if you haven’t here is a link to berezaa’s azure mines open source model. https://www.roblox.com/library/4920625917/Infinite-Mining-Game-Kit-v4-by-berezaa

Ok enough background. The problem I have run into is that when a player mines a block next to another player’s tunnel, it will generate the blocks in there tunnel. I haven’t figured a way around this that works. Should I switch to a different method of generating the blocks or is there a fix that I’m missing? I appreciate any help or ideas.

Here’s a gif of the problem: https://gyazo.com/63e49ca7400f5512611ffc7808cb561d

Here’s my code:

``````local Mine = game.Workspace.Mine
local Ores = game.ReplicatedStorage.Ores

Mine.ChildRemoved:Connect(function(ore)
local Pos = ore.Position
local NewBlockTop
local NewBlockBottom
local NewBlockRight
local NewBlockLeft
local NewBlockForward
local NewBlockBackward

--//Raycasting checking... Basically this checks what blocks are around the mined block. There are 6 total possibilities for blocks that can spawn, and this function will eliminate the blocks that are already there and then spawns in the blocks that aren't there

--Ray 1 is checking for the block below
local ray1 = Ray.new(ore.Position, Vector3.new(0, -5, 0))
local part1, hitPos1 = workspace:FindPartOnRay(ray1)
if part1 == nil then
local math1 = math.random(1, 100)
if math1 <= 70 then
NewBlockBottom = Ores.Stone:Clone()
elseif math1 >= 71 and math1 <=80 then
NewBlockBottom = Ores.Coal:Clone()
elseif math1 >= 81 and math1 <=85 then
NewBlockBottom = Ores.Iron:Clone()
elseif math1 >= 86 and math1 <=90 then
NewBlockBottom = Ores.Gold:Clone()
elseif math1 >= 91 and math1 <=95 then
NewBlockBottom = Ores.Amythest:Clone()
elseif math1 >= 96 and math1 <=98 then
NewBlockBottom = Ores.Platinum:Clone()
elseif math1 >= 99 then
NewBlockBottom = Ores.Obisidian:Clone()
end
end

--Ray 2 is checking for the block above,

local ray2 = Ray.new(ore.Position, Vector3.new(0, 5, 0))
local part2, hitPos2 = workspace:FindPartOnRay(ray2)
if part2 == nil and ore.Position.Y < 1534 then
local math1 = math.random(1, 100)
if math1 <= 70 then
NewBlockTop = Ores.Stone:Clone()
elseif math1 >= 71 and math1 <=80 then
NewBlockTop = Ores.Coal:Clone()
elseif math1 >= 81 and math1 <=85 then
NewBlockTop = Ores.Iron:Clone()
elseif math1 >= 86 and math1 <=90 then
NewBlockTop = Ores.Gold:Clone()
elseif math1 >= 91 and math1 <=95 then
NewBlockTop = Ores.Amythest:Clone()
elseif math1 >= 96 and math1 <=98 then
NewBlockTop = Ores.Platinum:Clone()
elseif math1 >= 99 then
NewBlockTop = Ores.Obisidian:Clone()
end
end

--Ray 3 is checking on positive X(or right as im calling it)

local ray3 = Ray.new(ore.Position, Vector3.new(5, 0, 0))
local part3, hitPos3 = workspace:FindPartOnRay(ray3)
if part3 == nil and ore.Position.Y < 1534 then
local math1 = math.random(1, 100)
if math1 <= 70 then
NewBlockRight = Ores.Stone:Clone()
elseif math1 >= 71 and math1 <=80 then
NewBlockRight = Ores.Coal:Clone()
elseif math1 >= 81 and math1 <=85 then
NewBlockRight = Ores.Iron:Clone()
elseif math1 >= 86 and math1 <=90 then
NewBlockRight = Ores.Gold:Clone()
elseif math1 >= 91 and math1 <=95 then
NewBlockRight = Ores.Amythest:Clone()
elseif math1 >= 96 and math1 <=98 then
NewBlockRight = Ores.Platinum:Clone()
elseif math1 >= 99 then
NewBlockRight = Ores.Obisidian:Clone()
end
end

--Ray 4 is checking on the negative X (or left as im calling it)

local ray4 = Ray.new(ore.Position, Vector3.new(-5, 0, 0))
local part4, hitPos4 = workspace:FindPartOnRay(ray4)
if part4 == nil and ore.Position.Y < 1534 then
local math1 = math.random(1, 100)
if math1 <= 70 then
NewBlockLeft = Ores.Stone:Clone()
elseif math1 >= 71 and math1 <=80 then
NewBlockLeft = Ores.Coal:Clone()
elseif math1 >= 81 and math1 <=85 then
NewBlockLeft = Ores.Iron:Clone()
elseif math1 >= 86 and math1 <=90 then
NewBlockLeft = Ores.Gold:Clone()
elseif math1 >= 91 and math1 <=95 then
NewBlockLeft = Ores.Amythest:Clone()
elseif math1 >= 96 and math1 <=98 then
NewBlockLeft = Ores.Platinum:Clone()
elseif math1 >= 99 then
NewBlockLeft = Ores.Obisidian:Clone()
end
end

--Ray 5 is checking on the positive Y (or forward as im calling it)

local ray5 = Ray.new(ore.Position, Vector3.new(0, 0, 5))
local part5, hitPos5 = workspace:FindPartOnRay(ray5)
if part5 == nil and ore.Position.Y < 1534 then
local math1 = math.random(1, 100)
if math1 <= 70 then
NewBlockForward = Ores.Stone:Clone()
elseif math1 >= 71 and math1 <=80 then
NewBlockForward = Ores.Coal:Clone()
elseif math1 >= 81 and math1 <=85 then
NewBlockForward = Ores.Iron:Clone()
elseif math1 >= 86 and math1 <=90 then
NewBlockForward = Ores.Gold:Clone()
elseif math1 >= 91 and math1 <=95 then
NewBlockForward = Ores.Amythest:Clone()
elseif math1 >= 96 and math1 <=98 then
NewBlockForward = Ores.Platinum:Clone()
elseif math1 >= 99 then
NewBlockForward = Ores.Obisidian:Clone()
end
end

--Ray 6 is checking on the negative Y (or backward as im calling it)

local ray6 = Ray.new(ore.Position, Vector3.new(0, 0, -5))
local part6, hitPos6 = workspace:FindPartOnRay(ray6)
if part6 == nil and ore.Position.Y < 1534 then
local math1 = math.random(1, 100)
if math1 <= 70 then
NewBlockBackward = Ores.Stone:Clone()
elseif math1 >= 71 and math1 <=80 then
NewBlockBackward = Ores.Coal:Clone()
elseif math1 >= 81 and math1 <=85 then
NewBlockBackward = Ores.Iron:Clone()
elseif math1 >= 86 and math1 <=90 then
NewBlockBackward = Ores.Gold:Clone()
elseif math1 >= 91 and math1 <=95 then
NewBlockBackward = Ores.Amythest:Clone()
elseif math1 >= 96 and math1 <=98 then
NewBlockBackward = Ores.Platinum:Clone()
elseif math1 >= 99 then
NewBlockBackward = Ores.Obisidian:Clone()
end
end

if NewBlockBottom then
NewBlockBottom.Parent = Mine
NewBlockBottom.Position = Vector3.new(Pos.X, Pos.Y - 5, Pos.Z)
end

--[[if NewBlockTop then
NewBlockTop.Parent = Mine
NewBlockTop.Position = Vector3.new(Pos.X, Pos.Y + 5, Pos.Z)
end]]--

if NewBlockRight then
NewBlockRight.Parent = Mine
NewBlockRight.Position = Vector3.new(Pos.X + 5, Pos.Y, Pos.Z)
end

if NewBlockLeft then
NewBlockLeft.Parent = Mine
NewBlockLeft.Position = Vector3.new(Pos.X - 5, Pos.Y, Pos.Z)
end

if NewBlockForward then
NewBlockForward.Parent = Mine
NewBlockForward.Position = Vector3.new(Pos.X, Pos.Y, Pos.Z + 5)
end

if NewBlockBackward then
NewBlockBackward.Parent = Mine
NewBlockBackward.Position = Vector3.new(Pos.X, Pos.Y, Pos.Z - 5)
end
end)
``````

Notes on code:

• I have commented out the code that creates a block on top as it creates the most problem
• 1534 is the height the start of the mine is at
• Don’t mind the pickaxe, I made it in 5 seconds and I don’t need a good looking one. It’s there as a placeholder

I would do this with a 3D array, you wouldn’t have to cast rays and it would be much easier to detect if there’s a block on a side. Also you would be able to easily mark if there was a block in one place and it was dug, so the generator wouldn’t spawn the unwanted blocks which happens in your case.

1 Like

Alright, thanks for responding quickly. I’ll research 3d arrays and try to put something together.

Try making a non-collidable, transparent placeholder block, that is created when you mine a block. This will trick the ray into thinking that there is a block there, so will not make s new block.

I found this multi-dimensional array code (autotables) on StackOverflow. It allows you to create tables with varying amounts of dimensions, but for your purpose you only need 3.

Once you’ve implemented 3D tables, you can use them to easily check if there is another part in the area you want to create parts in. If there is, you can just skip that part and continue creating parts in another area.

By using 3D tables, you won’t need raycasting at all.

Auto-tables generate sub-tables transparently using metatables and essentially after creating it you should be able to forget about them.

``````function newAutotable(dim)
local MT = {};
for i=1, dim do
MT[i] = {__index = function(t, k)
if i < dim then
t[k] = setmetatable({}, MT[i+1])
return t[k];
end
end}
end

return setmetatable({}, MT);
end

-- Usage
local at = newAutotable(3);
print(at) -- returns table
print(at) -- returns table
print(at) -- returns nil
at = 2;
print(at) -- returns value
print(at) -- error, because only 3 dimensions set
``````

I already tried this actually. Before I executed the idea I thought it was genius. There’s a but. The player can’t mine the blocks under the secret transparent block because the mouse is still targeting it.

I think this’ll be the best solution considering Toko also said this’ll work. When I next start working I’m gunna try it out.

Then just make the mouse ignore it.
Mouse.TargetFilter

I just tried this and it worked. The thing is the player still can’t click through the “dug”(or tunnel in my code) parts. Here’s a gif of what’s happening and the code.

https://gyazo.com/ff92fda395a3d3779736f91b71bef006

client side pickaxe defining the filter:

``````local mouse = player:GetMouse()
local filter = mouse.TargetFilter
local TunnelStorage = game.Workspace.TunnelStorage
filter = TunnelStorage
filter = TunnelStorage
end)
TunnelStorage.ChildRemoved:Connect(function()
filter = TunnelStorage
end)
``````

Try this.

``````local mouse  = game.Players.LocalPlayer:GetMouse()
mouse.TargetFilter = game.Workspace.TunnelStorage
``````

Don’t forgot to put the blocks into the storage, and target filter automatically makes its children ignored.

First off, I am literally using the code you wrote. (player is already defined as local player and I use the exact same line to define targetfilter.) Second, The blocks are in the tunnel storage and I read up about targetfilter a little while ago. The problem is that the player can’t select the blocks below the tunnel blocks, not that they aren’t in the storage.

I thought it had something to do with the variables getting mixed up, anyways I realize that the mouse doesn’t really x-ray ignored parts, it appears. I can’t think of any more solutions out of this, sorry.

You should try using UserInputService and raycasting instead.

``````local Player = game:GetService("Players").LocalPlayer
local UserInputService = game:GetService("UserInputService")
local Camera = workspace.CurrentCamera

local RANGE = 1e4
local GUI_INSET = Vector2.new(0, 36)

local RAYCAST_PARAMS = RaycastParams.new()
RAYCAST_PARAMS.IgnoreWater = false
-- update this above table with the part you want to ignore.
RAYCAST_PARAMS.FilterType = Enum.RaycastFilterType.Blacklist

local function get3DPosition()
local screenPosition = UserInputService:GetMouseLocation() - GUI_INSET
local oray = Camera:ScreenPointToRay(screenPosition.X, screenPosition.Y, 0)
return workspace:Raycast(oray.Origin, oray.Direction * RANGE, RAYCAST_PARAMS)
end

local selectedInput = Enum.UserInputType.MouseButton1
UserInputService.InputBegan:Connect(function(input, ignore)
if not ignore and input.UserInputType == selectedInput then
local raycastResult = get3DPosition()
if not raycastResult then return end -- it didn't hit anything

local hit, pos, normal = raycastResult.Instance, raycastResult.Position, raycastResult.Normal
-- use the hitPart, hitPosition, and the surface normal however u want
end
end)