local ss = game.ServerStorage
local pieces = ss.Pieces:GetChildren()
function generate(cframe : CFrame)
local randomPiece : Model = pieces[math.random(1, #pieces)]:Clone()
randomPiece.Parent = workspace.Pieces
randomPiece:PivotTo(cframe)
return randomPiece
end
return generate
Piece Creation Function (Server Script):
-- Create Piece function --
local function createPiece()
piecesMade += 1
local piece : Model = generate(nextCFrame)
piece.Name = piecesMade
local endWall : Part = piece.EndWall
nextCFrame = endWall.CFrame
local connection : RBXScriptConnection
connection = endWall.Touched:Connect(function(hit)
if hit.Parent:FindFirstChildOfClass("Humanoid") then
print(`{hit.Parent} made it through piece #{piece.Name}`)
createPiece()
clearOldPieces()
connection:Disconnect()
end
end)
return piece
end
If anyone knows how to fix I would greatly appreciate it. I’ve been waiting for 2 hours.
This does not work for a variety of reasons including but not limited to:
Why does your road have humanoid in them?
Touched only works on unanchored parts
This can be triggered if a player walks on the path
instead do
local partsOverlappingWithRoad = Workspace:GetPartsInPart(part)
for _, part in ipairs(partsOverlappingWithRoad) do
if part:HasTag("tag your road templates something and paste it here") then
print(`{hit.Parent} made it through piece #{piece.Name}`)
createPiece()
clearOldPieces()
connection:Disconnect()
end
end
I would say that your code is the one of the most readable I have seen so far, good job, keep it up!
From experience with my procedural generation logic, you can simply keep track of double turns and remove that direction from the room options if the last turn was the 2nd turn. Ex. two snippets from my room generation module:
if self.LastTurn == "Left" and self.DoubleLeft then
--print("Left", self.LastTurn)
allowedRooms = recursiveRemove(allowedRooms, "Left")
elseif self.LastTurn == "Right" and self.DoubleRight then
--print("Right", self.LastTurn)
allowedRooms = recursiveRemove(allowedRooms, "Right")
end
if randomRoom.Name:find("Left") then
if self.LastTurn == "Left" then
self.DoubleLeft = true
self.DoubleRight = false
end
self.LastTurn = "Left"
elseif randomRoom.Name:find("Right") then
if self.LastTurn == "Right" then
self.DoubleRight = true
self.DoubleLeft = false
end
self.LastTurn = "Right"
end
@ig1oo1 I tried making something like yours but I’m still getting those annoying loops:
repeat
task.wait()
piece = generate(nextCFrame)
if piece.Name:find("Right") and lastTurn == "Right" then
piece:Destroy()
else
lastTurn = "Right"
end
if piece.Name:find("Left") and lastTurn == "Left" then
piece:Destroy()
else
lastTurn = "Left"
end
until (piece ~= nil)
repeat
task.wait()
piece = generate(nextCFrame)
local floors = piece.Floors
for _, floor in floors:GetChildren() do
for _, hit in workspace:GetPartsInPart(floor, overlapParams) do
if not hit then continue end
if hit.Parent.Parent:HasTag("Piece") and hit.Parent.Parent ~= piece and hit.Parent.Parent.Name ~= tostring(piecesMade) then
print(piece:GetFullName())
print(hit:GetFullName())
piece:Destroy()
return
end
end
end
until (piece ~= nil)
piecesMade += 1
Sorry about that, I think my code was slightly unclear. Also, forgive me if I get something incorrect, I made the generation module a while ago.
I believe this is the problem. If you look at my code, I was tracking the last turn that was made - not just the current turn. So what I did was check if the last turn was right and DoubleRight is true, then I would remove all right turns from the list of room options. Same thing for left turns. Now, for the double turn detection, all I did was check if the current room is a right turn and the last turn was also a right turn. If both conditions are true, I set DoubleRight to true, and DoubleLeft to false. Again, same thing with left turns.
I personally believe this is a very crude and a bad way of solving this problem. While it should technically work in theory (aside from the dead-ends), it still doesn’t guarantee that there will be no problems. In something like this dealing with procedural generation, you should almost always rely on solid logic to ensure that everything works fine.
If your connectors could be placed in uniform bounding boxes, you could possibly discretize your “possible placement grid” into cells of uniform size. Then, you could just check if the corresponding cells of the 3 possible connector placements are occupied and avoid them from your available connectors list.
Yes, like ABizzare_Dummy pointed out, using collision is inefficient, however, in this case if grid works then this should work too.
I would also like to point out that just like in the snake game, it is possible to get yourself into a position where it would be impossible to not hit one’s own tail (e.g. by drawing a square with only 1 opening, and entering through that opening)
Is it a hard requirement that the paths must not de-spawn? How are the paths stored? linked list? perhaps if a collision is inevitable, cut off the tail?
I don’t understand. What my code is doing is it is detecting if the new piece is a right turn and if the last turning piece was a right turn. If both of these are true, it resets, generating a different piece until the criteria are met. To me, it seems like both of our scripts do the same thing, detect double turns.
Sorry if I didn’t understand that well, I’m having to deal with some overly authorative parents right now.
I have a folder in ServerStorage with all the pieces inside
Well, how the game works is you run through a series of generated tunnels from a wall that will kill you. With a bit of tweaking, I could probably make it so that the tunnels are able to despawn quickly without any issues.
It’s the way your if statements are organized. You are resetting the last turn to left (or right) even if the piece.Name doesn’t contain “Left” (or “Right”). Try this code;
repeat
task.wait()
piece = generate(nextCFrame)
local isRight = piece.Name:find("Right")
local isLeft = piece.Name:find("Left")
if isRight then
if lastTurn == "Right" then
piece:Destroy()
continue
end
lastTurn = "Right"
elseif isLeft then
if lastTurn == "Left" then
piece:Destroy()
continue
end
lastTurn = "Left"
end
until piece ~= nil
Let me know if you need more help understanding it.
Edit: Also, double turns are fine, it’s just the triple turns that we want to avoid
EDIT: I was able to fix the bug by setting the piece variable to nil after the piece:Destroy()
I set it to generate 500 times and I did not find a single crossover. Additionally, I like how it makes the path a lot more straight
Previous Post
@ig1oo1 I found a bug and I was wondering if you could help me fix
So basically a few lines below the repeat segment I have a variable that is assigned to the piece’s endPart. However, it seems like the contents of the piece are being destroyed before it reaches that part.
I made a small segment that makes a clone of the piece before that variable assignment for testing,
Edit: I added a print statement before the piece:Destroy() and it prints very shortly before the error occurs.
Code:
repeat
task.wait()
piece = generate(nextCFrame)
local isRight = piece.Name:find("Right")
local isLeft = piece.Name:find("Left")
if isRight then
if lastTurn == "Right" then
piece:Destroy()
continue
end
lastTurn = "Right"
elseif isLeft then
if lastTurn == "Left" then
piece:Destroy()
continue
end
lastTurn = "Left"
end
until (piece)
piecesMade += 1
piece.Name = piecesMade
piece:Clone().Parent = workspace
print(piece)
local endWall : Part = piece.EndWall -- Errors here that EndWall doesn't exist