Hello, So at the moment I have been working on a block style map generation script that works.
But what I want it to do is for each block check if there is air in front of it and check if there is a block under where the air is and if so make a wedge there to give the edges a well edge.
I have tried using raycasting but if it caused a lot of lag on joining and I couldn’t get it to work.
Here is my script so far.
--Objects//
local GenerationFolder = game:GetService("ReplicatedStorage"):FindFirstChild("GenerationParts")
local GeneratedMapFolder = workspace:FindFirstChild("GeneratedMap")
--Values//
local Seed = math.random(-100, 100)
local MapSize = 3 * 100
local MapHeight = 100
--Tables//
local PointsTable = {
}
--Functions//
function CreatePart(X, Y, Alpha)
local Part = GenerationFolder:FindFirstChild("Block"):Clone()
local newY = math.floor(Alpha / (3/MapHeight) + 0.5) * (3/MapHeight) * MapHeight
Part.Position = Vector3.new(X, newY, Y)
Part.Parent = GeneratedMapFolder
--Color Edits
Part.Color = Color3.fromRGB(91, 154, 76)
end
--Create Generation
for Y = 1, MapSize, 3 do
for X = 1, MapSize, 3 do
local Noise = math.noise((X + Seed) / MapSize, (Y + Seed) / MapSize, 0)
CreatePart(X, Y, Noise)
end
end
Raycasting is the way to go. Raycast out all 4 directions and if one of the Raycasts doesn’t hit anything, raycast down from that position. If the second raycast hits a block, then place a wedge there.
The Raycasts don’t have to be big, just big enough to hit the block next to it.
Let’s say your blocks are 10x10x10 studs big, then you could make the ray 5.5 or 6 studs long starting from the middle of the block, or start it from the side of the block and make it only 1 stud long. That would definitely be enough and would cause slightly less lag.
As for the lag, you can’t really do much about it. Complex world generations like this will always cause lag because they need a lot of math and calculations to be playable. You should create a Loading Screen similar to Minecraft, and start the map generation after that UI is shown to the player to make sure they don’t notice anything from the lag or the generation itself.
You can also slow the generation down by only placing blocks that are visible or by adding a cooldown to the loop. But these would make the generation take longer which also isn’t a great factor. So do things how you find the best.
The marching cubes algorithm may be of interest to you. Bit of a complex subject, but it will give you that edge style you’re looking for. Also, this video may be of help.
Unfortunately ROBLOX isn’t well-suited to these type of projects simply because ROBLOX has very little support for real-time mesh generation.
I can’t test the code right now so I can’t test if it works or not but I would do something like this:
local block = -- the block
local sides = {
block.CFrame.LookVector,
block.CFrame.RightVector,
block.CFrame.LookVector * -1,
block.CFrame.RightVector * -1
} -- I used a table for the sides because it will make later uses of the sides less complicated
for i = 1, 4, 1 do -- looping through the 4 sides of the block
local ray1 = workspace:Raycast(block.Position + sides[i]*4, sides[i]*2)
-- Some explaining:
-- Right now I will assume that a block is exactly 10*10*10 studs big, but correct some numbers based on the real sizes
-- First, I move the beginning of the ray 4 studs to the direction in which the ray should go. This will allow me to make the ray shorter and thus increase performance (slightly)
-- The value you move with can be anything that is close to but smaller than half the size of the blocks. The point is to get the beginning of the ray as close to the side of the block as possible
-- Second, I cast a ray that just goes out of the block enough to detect if there's a block next to it (i used 2 studs)
if not(ray1) then -- only continue if the ray didn't hit anything
local ray2 = workspace:Raycast(block.Position + sides[i]*6, Vector3.new(0, -6, 0))
-- Cast a ray from the end of the first ray (4+2 = 6) and go straight down (I used 6 studs for the same reason as before)
if ray2 then -- check if there is a block under where the wedge would be
-- Place the wedge next to the block
end
end
end
Please try understanding it and if you have any questions or if something isn’t clear, feel free to ask.
Hope it helps!
That worked thanks and for some reason it didn’t make so much lag like mine did lol.
But now I’m struggling with how to set the rotation on the wedge as at the moment it only is facing one direction.
Here is the new function I added with your help.
--Create Edges
function CreateEdge(Position, Rotation)
local EdgePart = GenerationFolder:FindFirstChild("EdgeBlock"):Clone()
EdgePart.CFrame = CFrame.new(Position) * CFrame.Angles(Rotation)
EdgePart.Parent = GeneratedMapFolder
EdgePart.Color = Color3.fromRGB(91, 154, 76)
end
for _, Part in pairs(GeneratedMapFolder:GetChildren()) do
local PointsTable = {
Part.CFrame.LookVector,
Part.CFrame.RightVector,
Part.CFrame.LookVector * -1,
Part.CFrame.RightVector * -1
}
for i = 1, 4, 1 do
local ray1 = workspace:Raycast(Part.Position + PointsTable[i]*2, PointsTable[i]*1)
if not(ray1) then
local ray2 = workspace:Raycast(Part.Position + PointsTable[i]*3, Vector3.new(0, -3, 0))
if ray2 then
CreateEdge(Part.Position + PointsTable[i]*3)
end
end
end
end
In the create wedge function it requires a rotation but I can’t seem to figure out how to give it the right rotation.
Instead of passing the rotation, pass the position of the part that “created” the wedge, and then you can use the method @oddcraft18 posted.
function CreateEdge(Position, MainPartPosition)
local EdgePart = GenerationFolder:FindFirstChild("EdgeBlock"):Clone()
EdgePart.CFrame = CFrame.new(Position, MainPartPosition)
end
This will make the wedge’s front face the part that created it. But, since the front of a wedge part is the side where it slopes, you have to turn it around, meaning to rotate it 180 degrees (= 1 pi).
So your final function would look like this:
function CreateEdge(Position, MainPartPosition)
local EdgePart = GenerationFolder:FindFirstChild("EdgeBlock"):Clone()
EdgePart.CFrame = CFrame.new(Position, MainPartPosition) * CFrame.Angles(0, 1, 0)
-- add the rest
end
And make sure you pass the part position instead of a Rotation value as a second argument in the loop.