- Home /
How do I split a rectangle into random smaller rectangles?
It's on a grid, these smaller rectangles should have a minimum size and they should have random size. I will probably need to cap the amount of rooms too. I'm having a problem on the "repeat until it's too small to split" part mainly. I've got it to divide it one time, but I can't get my head around this loop.
I'm using Javascript(Unityscript).
This is what I would like to make. A house similar to this.
The grey tiles are empty, white tiles are the floor that I'm trying to build and rooms should have 7 tiles in size to be divided, for rooms to have a minimum of 3x3 in size and maximum of 6x6.
I'm using an int[,] array as grid and setting values to instantiate objects later on another function.
After building the floor (white) I'll build the walls around, and the doors, which I have no idea how to put them where they'll work.
I'll attach the code in case someone would like to take a look.
#pragma strict
var size:int;
var minroomsize:int;
var splitpointx:int;
var splitpointy:int;
var grid:int[,];
var wall:GameObject;
var floor:GameObject;
var binary:int;
var newgrid;
var walls:GameObject;
var floors:GameObject;
var grid1:int[,];
var grid2:int[,];
var g1lengthx:int;
var g2lengthx:int;
var g1lengthy:int;
var g2lengthy:int;
var group:GameObject;
var group1:GameObject;
var group2:GameObject;
function Start(){
size=20;
minroomsize=4;
splitpointx=0;
splitpointy=0;
grid= new int[size,size];
Run(size);
}
function Run(size:int){
House(size);
binary=Random.Range(0.0,2.0);
if(binary==1){
SplitV(grid);
}else{
SplitH(grid);
}
Debug.Log("g1lengthx = "+g1lengthx+" g1lengthy = "+g1lengthy+" g2lengthx = "+g2lengthx+" g2lengthy = "+g2lengthy);
Loop(g1lengthx,g1lengthy,grid1);
Loop(g2lengthx,g2lengthy,grid2);
Test(size,grid1,grid2);
}
function Update(){
if(Input.GetButtonDown("F5")){
Reset();
Run(size);
}
}
function House(size:int){
for(var x:int;x<size;x++){
for(var y:int;y<size;y++){
if(x>0&&x<size-1&&y>0&&y<size-1){
grid[x,y]=2;
}
}
}
}
function Loop(x:int,y:int,grid:int[,]){
binary=Random.Range(0.0,2.0);
var number:int;
number=0;
if(binary==1){
while(x>minroomsize){
SplitV(grid);
if(x<=minroomsize){
break;
}
number++;
if(number>20){
break;
}
if(number==20){
Debug.Log("wrong");
}
}
}else{
while(y>minroomsize){
SplitH(grid);
number++;
if(y<=minroomsize){
break;
}
if(number>20){
break;
}
if(number==20){
Debug.Log("wrong");
}
}
}
}
function SplitV(grid:int[,]){
splitpointx=Random.Range(minroomsize,size-1-minroomsize);
//Debug.Log("vertical split");
grid1=new int[size,size];
grid2=new int[size,size];
for(var m:int=1;m<grid.GetLength(0);m++){
for(var n:int=1;n<grid.GetLength(1);n++){
if(m<splitpointx){
//grid2[m,n]=0;
grid1[m,n]=grid[m,n];
if(grid1[1,n]==2){
g1lengthy++;
}
if(grid1[m,1]==2){
g1lengthx++;
}
}else if(m>splitpointx){
//grid1[m,n]=0;
grid2[m,n]=grid[m,n];
if(grid2[grid.GetLength(0)-1,n]==2){
g2lengthy++;
}
if(grid2[m,grid.GetLength(1)-1]==2){
g2lengthx++;
}
}
}
}
}
function SplitH(grid:int[,]){
splitpointy=Random.Range(minroomsize,size-1-minroomsize);
//Debug.Log("horizontal split");
grid1=new int[size,size];
grid2=new int[size,size];
for(var m:int=1;m<grid.GetLength(0);m++){
for(var n:int=1;n<grid.GetLength(1);n++){
if(n<splitpointy){
grid1[m,n]=grid[m,n];
if(grid1[1,n]==2){
g1lengthy++;
}
if(grid1[m,1]==2){
g1lengthx++;
}
}else if(n>splitpointy){
grid2[m,n]=grid[m,n];
if(grid2[grid.GetLength(0)-1,n]==2){
g2lengthy++;
}
if(grid2[m,grid.GetLength(1)-1]==2){
g2lengthx++;
}
}
}
}
}
function Build(size:int){
for(var x:int;x<size;x++){
for(var y:int;y<size;y++){
if(grid[x,y]==1){
var walls=Instantiate(wall,Vector3(x,wall.transform.localScale.y/2,y),Quaternion.identity);
walls.transform.parent=this.transform;
}
if(grid[x,y]==2){
var floors=Instantiate(floor,Vector3(x,0,y),Quaternion.Euler(90,0,0));
floors.transform.parent=this.transform;
}
}
}
}
function Test(size:int,grid1:int[,],grid2:int[,]){
//Debug.Log("Test");
for(var x:int=1;x<size;x++){
for(var y:int=1;y<size;y++){
if(grid1[x,y]==1){
//Debug.Log("grid1 wall");
var walls=Instantiate(wall,Vector3(x,wall.transform.localScale.y/2,y),Quaternion.identity);
walls.transform.parent=group1.transform;
}
if(grid1[x,y]==2){
//Debug.Log("grid1 floor");
var floors=Instantiate(floor,Vector3(x,0,y),Quaternion.Euler(90,0,0));
floors.transform.parent=group1.transform;
}
if(grid2[x,y]==1){
//Debug.Log("grid2 wall");
walls=Instantiate(wall,Vector3(x,wall.transform.localScale.y/2,y),Quaternion.identity);
walls.transform.parent=group2.transform;
}
if(grid2[x,y]==2){
//Debug.Log("grid2 floor");
floors=Instantiate(floor,Vector3(x,0,y),Quaternion.Euler(90,0,0));
floors.transform.parent=group2.transform;
}
}
}
}
function Reset(){
if(this.transform.childCount!=0){
var child:int;
child=this.transform.childCount;
for(var x:int;x<child;x++){
Destroy(this.transform.GetChild(x).gameObject);
}
}
}
I really appreciate if someone helped me out on this. =)
You need to re-insert your image. Note there are size limits on images.
Is the image working now? I can see it from here. I've hosted it now.
So now I see the picture and understand the problem. You have a big body of code here. What is this code doing or not doing? In relation to the code, what are you asking of list? Your problem seems like the perfect excuse for using recursion.
There is also the issue here that it isn't a Unity question.
well I'm using Unity. And since I use Unityscript I thought this was the best place. I'll consider this next time. The code is adding values to a bi-dimensional array, then it gets half of this array values and write each to two new arrays, leaving an empty row or column (with random position) between them, then it instantiates tiles based on the values on the two grids. All I need is to make the loop happen until the rooms have from 6 to 3 tiles of space, and possibly get some tips on how to do it better. I'm new to program$$anonymous$$g, been like 6 months since I started so I don't know how to use recursion here.
Answer by Bunny83 · Dec 19, 2013 at 04:58 PM
I've just written two quick estension methods for Rect:
Just place this code in a C# file inside the "plugins" folder in your assets folder. You can use it from C# and UnityScript that way.
// C#
public static class RectSplitter
{
public static List<Rect> SplitOnce(this Rect aSource, Vector2 aMinSize)
{
float aspect = aMinSize.x/aMinSize.y;
if (aSource.width > aSource.height*aspect)
{
if (aSource.width > aMinSize.x*2)
{
float range = (aSource.width-aMinSize.x*2);
float split = Random.Range(0,range) + aMinSize.x;
Rect R1 = aSource;
Rect R2 = aSource;
R1.xMax -= split;
R2.xMin = R1.xMax;
var result = new List<Rect>();
result.Add(R1);
result.Add(R2);
return result;
}
else
return null;
}
else
{
if (aSource.height > aMinSize.y*2)
{
float range = (aSource.height-aMinSize.y*2);
float split = Random.Range(0,range) + aMinSize.y;
Rect R1 = aSource;
Rect R2 = aSource;
R1.yMax -= split;
R2.yMin = R1.yMax;
var result = new List<Rect>();
result.Add(R1);
result.Add(R2);
return result;
}
else
return null;
}
}
public static List<Rect> Split(this Rect aSource, Vector2 aMinSize)
{
var result = new List<Rect>();
var tmp = SplitOnce(aSource,aMinSize);
if (tmp != null)
{
foreach(Rect R in tmp)
{
result.AddRange(Split(R,aMinSize));
}
}
else
result.Add(aSource);
return result;
}
}
This method splits a given Rect into smaller rects based on the given min-size. It goes recursively over the rects until they reached a size that can't be splitted anymore.
Note: The method works on rects and float values, so if you need them applied to a grid, you have to round the min and max values.
var rects = baseRect.Split(new Vector2(5,4));
for (var i = 0; i < rects.Count; i++)
{
rects[i].xMin = Mathf.Round(rects[i].xMin);
rects[i].yMin = Mathf.Round(rects[i].yMin);
rects[i].xMax = Mathf.Round(rects[i].xMax);
rects[i].yMax = Mathf.Round(rects[i].yMax);
}
Also keep in mind that since you want a 1 grid wall, all rects should be 1 grid bigger. I used a min value of 5 and 4 to get a min room of 4x3
You just have to add the walls on two sides of each room (bottom and right for example). The baserect should be one grid smaller than the actual space since you need the border wall top and left.
Your whold room creation procedure should be based more on objects than on seperate tiles on a grid. Regarding your "door problem": Each final room-rect has 4 possible doors (on each wall), however you first have to figure out which rooms are actually "touching" and how much they overlap. It's up to you to define limits (if your door is 1, 2, ... grids wide). Once you know the overlapping "zone" you can just place a door in the middle. If you store the information which room has a connection to what other room you can do some "path" checks by vitually walking through the house by starting in a room and go from room to room without using the same door twice. If you walked the whole "tree" (so you used every door in each room) you should have visited each room. If not you're missing doors ;) Maybe your min size is too small in that case.
ps, you can of course use a seperate $$anonymous$$ size for x and y if you want. I can adjust the code... just a moment
edit
changed the $$anonymous$$ size into a Vector2. Now you can specify the $$anonymous$$ size seperate for width and height.
Oh forgot to make the class static ^^. Edited once more.
I have to say that i didn't tested it yet, but i will when i find the time ;)
Another update ^^ Had the splitting wrong, now it works as expected. However a little problem is that this will split the rects down as much as possible as long as each rect is larger than the $$anonymous$$ size. This results in an astounding equally sized and distributed rects. $$anonymous$$aybe, if you want some "bigger" rooms you should implement a random "skipping", maybe based on the rect area size. The smaller the size the more likely you don't split it again even when it's possible.
Thank you very much Bunny83! It's a different approach but I think I can work with this. I'll give you feedback as soon as I have some time to test it. Thanks for being so thorough.
where should I round the $$anonymous$$ and max values? It doesn't find baseRect. I'll keep trying meanwhile.
Your answer
Follow this Question
Related Questions
Random Room Generator 1 Answer
Code to randomly generate a mesh? 0 Answers
how can i make randomly generated worlds with collectable resources 0 Answers
Infinite loop when I try to generate randomly a 2D dungeon.. 3 Answers
How can I make a Terraria Style game from sprites without using an enormous amount of gameobjects? 2 Answers