AnthonyPython Posted February 23, 2015 Share Posted February 23, 2015 as the Titles says it all, is this now easier to do? I know Flexman has a unfinished guide in 2012. but it's still some great info, of course though, that was LE 2.x, and Leadwerks has gotten better. can anyone create such a thing, like specify, the prefabs, what height/width/length. (VIA LUA) and is it possible to have them, saved as a .map without having them being saved in Ricks save/load since that wouldn't be really a good thing. if it is too much to do I'll understand it. but having something like this will increase my work flow of getting puzzles done quicker. Quote OS: Windows 10 Pro CPU: i3-10100 CPU @ 3.60GHz GPU: NVIDIA 2060 Super - 8 GB RAM: 32 GB Link to comment Share on other sites More sharing options...
gamecreator Posted February 23, 2015 Share Posted February 23, 2015 http://www.leadwerks.com/werkspace/blog/52/entry-882-lets-make-a-game-procedual-content-creation-part-01/ Obligatory link Quote Link to comment Share on other sites More sharing options...
Rick Posted February 23, 2015 Share Posted February 23, 2015 Nothing stops anyone from doing this. It could get trickier to save it as an LE .map file though, Josh did release the Map class that shows the .map format, but it changes fairly regularly so you'd have to account for that. I personally would make my own map file, but then you wouldn't be able to edit it in the LE editor which I'm not sure if that's a requirement you would have. However, I wouldn't use the save/load I made since that was more specific to just game data instead of the entire level itself. I would personally use sqlite3 as the map file. It's very fast, has a lua implementation for it, and you can easily open it up with a db editor for visually see it. Quote Link to comment Share on other sites More sharing options...
AnthonyPython Posted February 23, 2015 Author Share Posted February 23, 2015 Nothing stops anyone from doing this. It could get trickier to save it as an LE .map file though, Josh did release the Map class that shows the .map format, but it changes fairly regularly so you'd have to account for that. I personally would make my own map file, but then you wouldn't be able to edit it in the LE editor which I'm not sure if that's a requirement you would have. However, I wouldn't use the save/load I made since that was more specific to just game data instead of the entire level itself. I would personally use sqlite3 as the map file. It's very fast, has a lua implementation for it, and you can easily open it up with a db editor for visually see it. Thanks for the advise rick. I'll definitely look into sqlite3. Quote OS: Windows 10 Pro CPU: i3-10100 CPU @ 3.60GHz GPU: NVIDIA 2060 Super - 8 GB RAM: 32 GB Link to comment Share on other sites More sharing options...
AnthonyPython Posted February 23, 2015 Author Share Posted February 23, 2015 BTW, I just got the c++ dlc while on sale. Quote OS: Windows 10 Pro CPU: i3-10100 CPU @ 3.60GHz GPU: NVIDIA 2060 Super - 8 GB RAM: 32 GB Link to comment Share on other sites More sharing options...
shadmar Posted February 23, 2015 Share Posted February 23, 2015 I thought on this one a while back: http://gamedevelopment.tutsplus.com/tutorials/bake-your-own-3d-dungeons-with-procedural-recipes--gamedev-14360 Great article. Quote HP Omen - 16GB - i7 - Nvidia GTX 1060 6GB Link to comment Share on other sites More sharing options...
Genebris Posted February 23, 2015 Share Posted February 23, 2015 Not sure if this can help you, but I have made basic dungeon generation shown on this video: I have five models (painted in different colors in video) and specified each enter and exit spot with pivot point. Here is code: Script.startPivot="" --entity Script.dungeon={} Script.names={"d1", "d2l", "d2r", "d3l", "d3r"} --Names of all possible dungeon segments to check AABB intersection Script.d1="Prefabs/Dungeon/d1.pfb" Script.d2l="Prefabs/Dungeon/d2l.pfb" Script.d2r="Prefabs/Dungeon/d2r.pfb" Script.d3l="Prefabs/Dungeon/d3l.pfb" Script.d3r="Prefabs/Dungeon/d3r.pfb" Script.currentR=0 Script.nextR=0 Script.j=0 Script.i=0 Script.s=0 Script.allDungeonSegments={} --Debug function Script:Start() --math.randomseed(10) end function Script:UpdateWorld() if App.window:KeyHit(Key.G) then self:Use() end end function Script:Use() self:GenerateDungeon() end function Script:GenerateDungeon() --main generation function self.currentR=0 self.nextR=0 self.i=0 self.j=0 player:ConsoleLog("Generation started. Countentities = "..App.world:CountEntities()) self:DeleteAllDungeonSegments() player:ConsoleLog("All segments deleted. Countentities = "..App.world:CountEntities()) self.dungeon={} self.dungeon[0]=self:CreateSegment(self:GetRandomPart()) --Create first dungeon element self.dungeon[0]:SetRotation(0,self.currentR,0,true) local pos = self.dungeon[0]:FindChild("Enter"):GetPosition(true) local previousPos = self.startPivot:GetPosition(true) local dPos = pos-previousPos self.dungeon[0]:SetPosition(pos-dPos, true) local result = true --Create main path self.dungeon, result=self:GeneratePath(100, self.dungeon) --If this function failed to generate dungeon we still need to save it, but we sholdn't proceed further if not result then self:GenerateDungeon() --Failed to generate dungeon, starting again return end --Generate forks self:GenerateForks() end function Script:GeneratePath(amount, startingTable, emergencyLimit) if not emergencyLimit then emergencyLimit=200 end local dungeon={} dungeon=startingTable local emergencyCounter=0 local i=#dungeon+1 self.intersectionFound=false while i<=amount do emergencyCounter=emergencyCounter+1 --System:Print(emergencyCounter.." i="..i) if emergencyCounter>emergencyLimit then --reached limit of retries, generate again return dungeon, false --still need to return current dungeon to be able to clear it end dungeon=self:CreateSegment(self:GetRandomPart()) local yRotation=dungeon[i-1]:GetRotation(true).y+self:GetExtraRotation(dungeon[i-1]) dungeon:SetRotation(0,yRotation,0,true) pos = dungeon:FindChild("Enter"):GetPosition(true) previousPos = dungeon[i-1]:FindChild("Exit"):GetPosition(true) dPos = pos-previousPos dungeon:SetPosition(dungeon:GetPosition(true)-dPos, true) --Check AABB local aabb=dungeon:GetAABB(Entity.GlobalAABB) local j=1 local maximum=#self.allDungeonSegments while j<=maximum do --System:Print(j) if self.allDungeonSegments[j]~=dungeon and self.allDungeonSegments[j]~=dungeon[i-1] and self.allDungeonSegments[j]~=dungeon[i-2] then if aabb:IntersectsAABB(self.allDungeonSegments[j]:GetAABB(Entity.GlobalAABB)) then System:Print(dungeon:GetKeyValue("name").." intersects with "..self.allDungeonSegments[j]:GetKeyValue("name")) if i<4 then return dungeon, false else System:Print("Three steps back") self:DeleteSegment(dungeon) System:Print("Deleted last segment") dungeon=nil self:DeleteSegment(dungeon[i-1]) System:Print("Deleted i-1") dungeon=nil self:DeleteSegment(dungeon[i-2]) System:Print("Deleted i-2") dungeon=nil i=i-3 j=1000--maximum+1 end end end j=j+1 end i=i+1 --System:Print("End of I loop. i="..i) end return dungeon, true --returning true if managed to generate dungeon properly end function Script:GenerateForks() local i=1 while i <= #self.dungeon do if self.dungeon:GetKeyValue("name")=="d1" and math.random(0, 20)==0 then local matrix=self.dungeon:GetMatrix(true) self:DeleteSegment(self.dungeon) self.dungeon={} self.dungeon[0]=self:CreateSegment(self:GetRandomPart(4,5)) self.dungeon[0]:SetMatrix(matrix, true) self.dungeon[1]=self:CreateSegment(self:GetRandomPart()) local yRotation=self.dungeon[0]:GetRotation(true).y+self:GetExtraRotation(self.dungeon[0]) --second element self.dungeon[1]:SetRotation(0,yRotation,0,true) pos = self.dungeon[1]:FindChild("Enter"):GetPosition(true) previousPos = self.dungeon[0]:FindChild("Exit2"):GetPosition(true) dPos = pos-previousPos self.dungeon[1]:SetPosition(self.dungeon[1]:GetPosition(true)-dPos, true) --local dungeon --local result local dungeon, result = self:GeneratePath(15, self.dungeon, 30) if not result then self:DeleteSegment(dungeon[#dungeon]) self.dungeon=dungeon else self.dungeon=dungeon end end i=i+1 end end --other function function Script:GetRandomPart(minimum, maximum) --Choose random element if not minimum then minimum=0; maximum=3 end local r=math.random(minimum, maximum) if r==1 or r==0 then return self.d1 elseif r==2 then return self.d2r elseif r==3 then return self.d2l elseif r==4 then return self.d3r elseif r==5 then return self.d3l end end function Script:GetExtraRotation(name) name=name:GetKeyValue("name") if name=="d2r" or name=="d3r" then return (90) elseif name=="d2l" or name=="d3l" then return (-90) end return 0 --all other segments return 0 end function Script:CreateSegment(name) local entity=Prefab:Load(name) table.insert (self.allDungeonSegments, entity) return entity end function Script:DeleteSegment(entity) local i=1 local maximum=#self.allDungeonSegments while i<=maximum do if self.allDungeonSegments==entity then local temp = self.allDungeonSegments table.remove (self.allDungeonSegments, i) --self.allDungeonSegments=0 temp:Release() i=maximum end i=i+1 end end function Script:DeleteAllDungeonSegments() for i=1, #self.allDungeonSegments do self.allDungeonSegments:Release() end self.allDungeonSegments={} end function Script:IsDungeonSegment(name) --not needed name=name:GetKeyValue("name") for i=1, #self.names do if self.names==name then return true end end return false end 6 Quote Link to comment Share on other sites More sharing options...
AnthonyPython Posted February 24, 2015 Author Share Posted February 24, 2015 thanks for the info! Quote OS: Windows 10 Pro CPU: i3-10100 CPU @ 3.60GHz GPU: NVIDIA 2060 Super - 8 GB RAM: 32 GB Link to comment Share on other sites More sharing options...
AnthonyPython Posted February 24, 2015 Author Share Posted February 24, 2015 Not sure if this can help you, but I have made basic dungeon generation shown on this video: I have five models (painted in different colors in video) and specified each enter and exit spot with pivot point. Here is code: Script.startPivot="" --entity Script.dungeon={} Script.names={"d1", "d2l", "d2r", "d3l", "d3r"} --Names of all possible dungeon segments to check AABB intersection Script.d1="Prefabs/Dungeon/d1.pfb" Script.d2l="Prefabs/Dungeon/d2l.pfb" Script.d2r="Prefabs/Dungeon/d2r.pfb" Script.d3l="Prefabs/Dungeon/d3l.pfb" Script.d3r="Prefabs/Dungeon/d3r.pfb" Script.currentR=0 Script.nextR=0 Script.j=0 Script.i=0 Script.s=0 Script.allDungeonSegments={} --Debug function Script:Start() --math.randomseed(10) end function Script:UpdateWorld() if App.window:KeyHit(Key.G) then self:Use() end end function Script:Use() self:GenerateDungeon() end function Script:GenerateDungeon() --main generation function self.currentR=0 self.nextR=0 self.i=0 self.j=0 player:ConsoleLog("Generation started. Countentities = "..App.world:CountEntities()) self:DeleteAllDungeonSegments() player:ConsoleLog("All segments deleted. Countentities = "..App.world:CountEntities()) self.dungeon={} self.dungeon[0]=self:CreateSegment(self:GetRandomPart()) --Create first dungeon element self.dungeon[0]:SetRotation(0,self.currentR,0,true) local pos = self.dungeon[0]:FindChild("Enter"):GetPosition(true) local previousPos = self.startPivot:GetPosition(true) local dPos = pos-previousPos self.dungeon[0]:SetPosition(pos-dPos, true) local result = true --Create main path self.dungeon, result=self:GeneratePath(100, self.dungeon) --If this function failed to generate dungeon we still need to save it, but we sholdn't proceed further if not result then self:GenerateDungeon() --Failed to generate dungeon, starting again return end --Generate forks self:GenerateForks() end function Script:GeneratePath(amount, startingTable, emergencyLimit) if not emergencyLimit then emergencyLimit=200 end local dungeon={} dungeon=startingTable local emergencyCounter=0 local i=#dungeon+1 self.intersectionFound=false while i<=amount do emergencyCounter=emergencyCounter+1 --System:Print(emergencyCounter.." i="..i) if emergencyCounter>emergencyLimit then --reached limit of retries, generate again return dungeon, false --still need to return current dungeon to be able to clear it end dungeon=self:CreateSegment(self:GetRandomPart()) local yRotation=dungeon[i-1]:GetRotation(true).y+self:GetExtraRotation(dungeon[i-1]) dungeon:SetRotation(0,yRotation,0,true) pos = dungeon:FindChild("Enter"):GetPosition(true) previousPos = dungeon[i-1]:FindChild("Exit"):GetPosition(true) dPos = pos-previousPos dungeon:SetPosition(dungeon:GetPosition(true)-dPos, true) --Check AABB local aabb=dungeon:GetAABB(Entity.GlobalAABB) local j=1 local maximum=#self.allDungeonSegments while j<=maximum do --System:Print(j) if self.allDungeonSegments[j]~=dungeon and self.allDungeonSegments[j]~=dungeon[i-1] and self.allDungeonSegments[j]~=dungeon[i-2] then if aabb:IntersectsAABB(self.allDungeonSegments[j]:GetAABB(Entity.GlobalAABB)) then System:Print(dungeon:GetKeyValue("name").." intersects with "..self.allDungeonSegments[j]:GetKeyValue("name")) if i<4 then return dungeon, false else System:Print("Three steps back") self:DeleteSegment(dungeon) System:Print("Deleted last segment") dungeon=nil self:DeleteSegment(dungeon[i-1]) System:Print("Deleted i-1") dungeon=nil self:DeleteSegment(dungeon[i-2]) System:Print("Deleted i-2") dungeon=nil i=i-3 j=1000--maximum+1 end end end j=j+1 end i=i+1 --System:Print("End of I loop. i="..i) end return dungeon, true --returning true if managed to generate dungeon properly end function Script:GenerateForks() local i=1 while i <= #self.dungeon do if self.dungeon:GetKeyValue("name")=="d1" and math.random(0, 20)==0 then local matrix=self.dungeon:GetMatrix(true) self:DeleteSegment(self.dungeon) self.dungeon={} self.dungeon[0]=self:CreateSegment(self:GetRandomPart(4,5)) self.dungeon[0]:SetMatrix(matrix, true) self.dungeon[1]=self:CreateSegment(self:GetRandomPart()) local yRotation=self.dungeon[0]:GetRotation(true).y+self:GetExtraRotation(self.dungeon[0]) --second element self.dungeon[1]:SetRotation(0,yRotation,0,true) pos = self.dungeon[1]:FindChild("Enter"):GetPosition(true) previousPos = self.dungeon[0]:FindChild("Exit2"):GetPosition(true) dPos = pos-previousPos self.dungeon[1]:SetPosition(self.dungeon[1]:GetPosition(true)-dPos, true) --local dungeon --local result local dungeon, result = self:GeneratePath(15, self.dungeon, 30) if not result then self:DeleteSegment(dungeon[#dungeon]) self.dungeon=dungeon else self.dungeon=dungeon end end i=i+1 end end --other function function Script:GetRandomPart(minimum, maximum) --Choose random element if not minimum then minimum=0; maximum=3 end local r=math.random(minimum, maximum) if r==1 or r==0 then return self.d1 elseif r==2 then return self.d2r elseif r==3 then return self.d2l elseif r==4 then return self.d3r elseif r==5 then return self.d3l end end function Script:GetExtraRotation(name) name=name:GetKeyValue("name") if name=="d2r" or name=="d3r" then return (90) elseif name=="d2l" or name=="d3l" then return (-90) end return 0 --all other segments return 0 end function Script:CreateSegment(name) local entity=Prefab:Load(name) table.insert (self.allDungeonSegments, entity) return entity end function Script:DeleteSegment(entity) local i=1 local maximum=#self.allDungeonSegments while i<=maximum do if self.allDungeonSegments==entity then local temp = self.allDungeonSegments table.remove (self.allDungeonSegments, i) --self.allDungeonSegments=0 temp:Release() i=maximum end i=i+1 end end function Script:DeleteAllDungeonSegments() for i=1, #self.allDungeonSegments do self.allDungeonSegments:Release() end self.allDungeonSegments={} end function Script:IsDungeonSegment(name) --not needed name=name:GetKeyValue("name") for i=1, #self.names do if self.names==name then return true end end return false end this will come in handy Quote OS: Windows 10 Pro CPU: i3-10100 CPU @ 3.60GHz GPU: NVIDIA 2060 Super - 8 GB RAM: 32 GB Link to comment Share on other sites More sharing options...
awgsknite Posted February 24, 2015 Share Posted February 24, 2015 Nice! Thanks ! I'm trying to make one of these 3D dungeon generators as well. I have one in C# but idunno how to convert it to lua. Any ideas on how to convert it ? Maybe someone can convert it and post it in this thread so that everyone can use it ? Here it is: using System; using System.Collections; using System.Collections.Generic; public class MazeGenerator : MonoBehaviour { public int width, height; public Material brick; private int[,] Maze; private List<Vector3> pathMazes = new List<Vector3>(); private Stack<Vector2> _tiletoTry = new Stack<Vector2>(); private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) }; private System.Random rnd = new System.Random(); private int _width, _height; private Vector2 _currentTile; public String MazeString; public Vector2 CurrentTile { get { return _currentTile; } private set { if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1){ throw new ArgumentException("Width and Height must be greater than 2 to make a maze"); } _currentTile = value; } } private static MazeGenerator instance; public static MazeGenerator Instance { get {return instance;} } void Awake() { instance = this;} void Start() { MakeBlocks(); } // end of main program // ============= subroutines ============ void MakeBlocks() { Maze = new int[width, height]; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { Maze[x, y] = 1; } } CurrentTile = Vector2.one; _tiletoTry.Push(CurrentTile); Maze = CreateMaze(); // generate the maze in Maze Array. GameObject ptype = null; for (int i = 0; i <= Maze.GetUpperBound(0); i++) { for (int j = 0; j <= Maze.GetUpperBound(1); j++) { if (Maze[i, j] == 1) { MazeString=MazeString+"X"; // added to create String ptype = GameObject.CreatePrimitive(PrimitiveType.Cube); ptype.transform.position = new Vector3(i * ptype.transform.localScale.x, 0, j * ptype.transform.localScale.z); if (brick != null) { ptype.renderer.material = brick; } ptype.transform.parent = transform; } else if (Maze[i, j] == 0) { MazeString=MazeString+"0"; // added to create String pathMazes.Add(new Vector3(i, 0, j)); } } MazeString=MazeString+"\n"; // added to create String } print (MazeString); // added to create String } // ======================================= public int[,] CreateMaze() { //local variable to store neighbors to the current square as we work our way through the maze List<Vector2> neighbors; //as long as there are still tiles to try while (_tiletoTry.Count > 0) { //excavate the square we are on Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0; //get all valid neighbors for the new tile neighbors = GetValidNeighbors(CurrentTile); //if there are any interesting looking neighbors if (neighbors.Count > 0) { //remember this tile, by putting it on the stack _tiletoTry.Push(CurrentTile); //move on to a random of the neighboring tiles CurrentTile = neighbors[rnd.Next(neighbors.Count)]; } else { //if there were no neighbors to try, we are at a dead-end toss this tile out //(thereby returning to a previous tile in the list to check). CurrentTile = _tiletoTry.Pop(); } } print("Maze Generated ..."); return Maze; } // ================================================ // Get all the prospective neighboring tiles "centerTile" The tile to test // All and any valid neighbors</returns> private List<Vector2> GetValidNeighbors(Vector2 centerTile) { List<Vector2> validNeighbors = new List<Vector2>(); //Check all four directions around the tile foreach (var offset in offsets) { //find the neighbor's position Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y); //make sure the tile is not on both an even X-axis and an even Y-axis //to ensure we can get walls around all tunnels if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1) { //if the potential neighbor is unexcavated (==1) //and still has three walls intact (new territory) if (Maze[(int)toCheck.x, (int)toCheck.y] == 1 && HasThreeWallsIntact(toCheck)) { //add the neighbor validNeighbors.Add(toCheck); } } } return validNeighbors; } // ================================================ // Counts the number of intact walls around a tile //"Vector2ToCheck">The coordinates of the tile to check //Whether there are three intact walls (the tile has not been dug into earlier. private bool HasThreeWallsIntact(Vector2 Vector2ToCheck) { int intactWallCounter = 0; //Check all four directions around the tile foreach (var offset in offsets) { //find the neighbor's position Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y); //make sure it is inside the maze, and it hasn't been dug out yet if (IsInside(neighborToCheck) && Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1) { intactWallCounter++; } } //tell whether three walls are intact return intactWallCounter == 3; } // ================================================ private bool IsInside(Vector2 p) { //return p.x >= 0 p.y >= 0 p.x < width p.y < height; return p.x >= 0 && p.y >= 0 && p.x < width && p.y < height; } } Quote http://steamcommunity.com/profiles/76561197977392956/7977392956/ Link to comment Share on other sites More sharing options...
Averice Posted February 24, 2015 Share Posted February 24, 2015 If you look in what are you working on thread I have been using perlin noise for terrain generation using blocks and pure Lua, would work the same using a prefab and instancing it. Quote Link to comment Share on other sites More sharing options...
AnthonyPython Posted February 25, 2015 Author Share Posted February 25, 2015 I thought on this one a while back: http://gamedevelopment.tutsplus.com/tutorials/bake-your-own-3d-dungeons-with-procedural-recipes--gamedev-14360 Great article. it is indeed, I love it! Quote OS: Windows 10 Pro CPU: i3-10100 CPU @ 3.60GHz GPU: NVIDIA 2060 Super - 8 GB RAM: 32 GB Link to comment Share on other sites More sharing options...
AnthonyPython Posted February 27, 2015 Author Share Posted February 27, 2015 ok, anyways I found this open source Unity C# Procedural generation both for dungeon if you want that or single rooms for like i need. I used a C# to C++ convert and it worked very well, of course though since it was unity it has unity specific coding in it I cleaned some of it up, and now is to actually complete it, any help to get this to work with prefabs would be great, looking through the leadwerks command references right now trying to fix those error's. and adapt, any help would be great Generator.zip 3 Quote OS: Windows 10 Pro CPU: i3-10100 CPU @ 3.60GHz GPU: NVIDIA 2060 Super - 8 GB RAM: 32 GB Link to comment Share on other sites More sharing options...
YouGroove Posted February 27, 2015 Share Posted February 27, 2015 I think i will attempt some some Lua generator for another LE3 game prototype. Quote Stop toying and make games Link to comment Share on other sites More sharing options...
AnthonyPython Posted February 28, 2015 Author Share Posted February 28, 2015 I think i will attempt some some Lua generator for another LE3 game prototype. ok groove Quote OS: Windows 10 Pro CPU: i3-10100 CPU @ 3.60GHz GPU: NVIDIA 2060 Super - 8 GB RAM: 32 GB Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.