Tuesday 19 March 2013

Branches!

I'm now on my Easter 'break' which means ill be spending a lot of time on my team project! Incidently this means there is going to be more blogs/longer blogs :) 

Anyway back to business! I have a random dungeon path generator that recognises how many rooms are around it and knows where those adjacent rooms are. That's cool but one path? that's pretty... well... linear and often times linear equals boring. So let's add some branches to the path giving the player some options when traversing the dungeon. What those options are can be decided at a later date.

To crate a branch to my dungeon the code is going to be very similar to that of the main path, however the starting location will be a room that already exists along the main path. To do this I generate a random number between 0 and the length of the path which will be represented by roomCount.

Random.Range(0,roomCount);

I will then take the co-ordinates of a room that have this random number as an index within the array. This is our starting room. After the first room is found the code for making a branch is almost identical to that of drawing the main path with some altered variable names.


uhh, maybe there needs to be some other changes... So I introduced some new public and private variables;

public bool branches : branches is used to decide if the path should have branches via an if statement.
private int branchCount : branchCount is used to count the rooms created that are represent branches, if added to roomCount will produce the index of that room in the array.
public int branchNumber : branchNumber adjusts the number of branches attached to the path
public int branchLength : branchLength adjusts the number of rooms in a branch.

The problem in the previous picture is that there was nothing telling the branches when to stop.

Using branchNumber and branchLength I'm defining  a stopping point for branch creation.



So my branches are working somewhat but I can instantly see where the issue is. Branches crossing over each other and branches crossing with the main path cause my adjacent calculations to exceed 4 rooms and currently there is no option if a room has 5 or more adjacent rooms. To fix this I either have to give options for having 5 or more adjacent rooms, remove the unnecessary additional adjacent rooms or just stop looking for rooms if I have 4 already. So naturally I did all of them just to be safe. :D

I changed the 4 room if statement to

if(adjacentCount >= 4) { Instantiate 4 Door Prefab }

That way if there are somehow 5 or more adjacent rooms it will just place 4 doors.

I then introduced some booleans

bool N
bool S
bool E
bool W

If a room has been found at any of the respective positions, the boolean value will change. If one of the boolean values have changed another room at that location cannot increment adjacentCount.

if(Yarr[j] == roomY+1 & Xarr[j] == roomX & E == false) { adjacentCount++; E = true; }

Lastly I want to remove rooms that have the same value as other rooms. To do this I go through the array with one for loop:

for(int i = 0; i <= roomCount+branchCount; i++)

I then go through the array again inside that loop using another for loop:

for(int i = 0; i <= roomCount+branchCount; i++)
{
         for(int j = 0; j <= roomCount+branchCount; j++)
}

using the If statement:

if(Xarr[j] == roomX & Yarr[j] == roomY & j != i)

I set the X and Y value of the room being compared to -10. When it comes to drawing rooms if a room has -10 X and or -10 Y, just don't draw it.

for(int i = 0; i <= roomCount+branchCount; i++)
{
        roomX = Xarr[i];
        roomY = Yarr[i];
        for(int j = 0; j <= roomCount+branchCount; j++)
        {
                 if(Xarr[j] == roomX & Yarr[j] == roomY & j != i)
               {
Xarr[j] = -10;
Yarr[j] = -10;
 }
        }
}

The Final Product :




There were some other issues that were down to typos in the code that didn't come out with compiler errors that are now fixed :)

Tuesday 12 March 2013

Which way should I be facing!?

Aaaaaaaaaaand we're back, I've been spending most of my time doing other assignments so I've only just found some time to write a post. Last post I showed a picture of a dungeon path that recognised how many rooms were adjacent to it. Thats all good but how do we utilise the information about adjacent rooms and use it to line up and remove unecessary doors?

Well firstly I used some mathematical methods I learnt in GCSE! Binary Logic and Pascal's Triangle. I know there are 4 different relative door positions:

North, South, East, West.

4^2 tells us the number of combinations there are which is 16. If I include the option of 0 adjacent doors as a 0 value that makes 5 options, the 5th row of pascals triangle tells me the distribution of possibilities:

1 4 6 4 1

Now let's check if my GCSE maths was actually useful by doing some logic. Each bit in these calculations represent a possible room or door position.

nsew
0000
0001 w
0010 e
0011 ew
0100 s
0101 sw
0110 se
0111 swe
1000 n
1001 nw
1010 ne
1011 new
1100 ns
1101 nsw
1110 nse
1111 nsew

Posibilities :

0 Doors/Rooms : 1
1 Door/Rooms : 4
2 Doors/Rooms : 6
3 Doors/Rooms: 4
4 Doors/Rooms : 1

So now I know the properties of each room, but how do I program my script to utilise this? Well my script can already figure out how many adjacent rooms there are, and thanks to Pascal and Binary Logic we know that each number of adjacent rooms have certain possibilities and we know the combinations of these possibilities, so first we need to allow our script to represent these combinations.

I created 4 new Boolean variable types; Nbool, Sbool, Ebool, and Wbool. These types represent the bits from earlier and are all initialised as false at the start of the path calculation loop. If a room is adjacent, one of these Boolean values is set to true depending on its' relative co-ordinate.

Next I'll perform a check inside the if statements from before. If for example, Nbool and Sbool are true, this would mean adjacentCount is 2.

if(adjacentCount == 2)
{
      if(Nbool == true & Sbool == true)
      {
              //Load a room with a door the North and South side.
      }
}

Using other relevant if statements I can place every room appropriately.

On the Unity side of things this became somewhat of an issue due to run time rotation transformations but eventually it lead to me defining public variables for every possible room (all 15 of them) and setting up pre-transformed prefabs for all of them. Here's what i got.


And there you have it.... wait what?!



Well it looks like Darksteel set me up with some dodgy prefabs! It's fine though I hard-coded them into the correct position. I'll get some replacements eventually but this will have to do for now. :)

Here's what i got after hard-coding the transformations.


Saturday 9 March 2013

Knowing what's around you!

So I have all these beautiful room prefabs but my script doesn't know when to use the appropriate one! Not only that but it doesn't know which way the prefab needs to be facing! Fortunately I have a plan to get this working.

My script needs to figure out how many rooms are adjacent to the room in question. To do this I need to know the properties of an adjacent room. An adjacent room will have the same co-ordinates as the room in question except it's X or Y value will be 1 higher or 1 lower. Now how do I get my script to check this? Firstly I start off by choosing a room to check, this can be done using a for loop.

for(int i = 0; i <= roomCount; ++i)
{
      Xcheck = Xarray[i];
      Ycheck = Yarray[i];
}

This loop will fetch me every room co-ordinate in the arrays for checking, but I want to check every room values against every other room in the arrays! To do this I embed another for loop inside the for loop I just created.

for(int i = 0; i <= roomCount; ++i)
{
        Xcheck = Xarray[i];
        Ycheck = Yarray[i];
        for(int j = 0; j <= roomCount; ++j)
       {
                if(Xcheck == Xarray[j]+1)
               {
               This room is adjacent
               adjacentCount++;
               }
       }
}

I then introduce several if statements to check if the room's co-ordinates determined by j are adjacent, if the if statement passes then an integer variable called adjacentCount is incremented. When it comes to drawing the room I can then use another if statement such as "if(adjacentCount == 4)" then load a 4 doors prefab. adjacentCount is then reset when a new room is being checked.

I introduced these changes to my script as well as colour changes to each prefab so they are more visible and here is my result.

Red Room = 1 Door
Blue Room = 2 Doors
Green Room = 3 Doors
White Room = 4 Doors


Prefabs and Player Controls

Okay so I have some kind of path generating and I've ironed out some obvious bugs I can see from the scene viewer in Unity, but now it's time to get inside the dungeon and test it from the inside as if I were playing the game. For this to happen I'm going to get some outside help. I need rooms to load instead of these primitive Unity cubes as well as a Player Character to run through these rooms with.

Rooms -

I'm lucky enough to have some housemates with skills in different areas, two of them being product design. One of these housemates (lets call him Darksteel) agreed to make me some place holder rooms for my dungeon. I needed some hollowed out cubes with doors on each side in several combinations.

4 Doors
3 Doors
2 Doors (Parallel)
2 Doors (Adjacent)
1 Door

Darksteel promptly made me these rooms and I modified my script to load in his 4 door rooms as prefabs, added some lights and this is what I got :


Unfortunately I haven't edited my script to detect which room to load in but I think that deserves it's own blog post as it's going to be somewhat complex.

Player Character -

I'm also very lucky to be teamed up with some other skilled programmers who are working on other parts of the game unrelated to dungeon generation. This meant we already had a Player Character prefab set up, this meant I could just reuse the assets they had created. I  then set up my script to load in the Player Character prefab into the First Room using the same if statement I made to draw it. Other than that some changes I made were the scale of the Player Character and adding Terrain and Mesh collision to my prefabs.

It is now possible to traverse my dungeon from the inside as if you were playing the game :)

Here is a pic from the Player Character's camera :

Friday 8 March 2013

Starts, Ends and Wall Hugging

Although the pictures from my last post looked acceptable there were some issues that would take several generations of the dungeon to see clearly.
  • Wall Hugging.
  • No clear start or end.
  • Redrawing of rooms.
Wall hugging -

You may be able to notice in the last picture i posted that the dungeon will hit a wall and stick to it, this is because once the dungeon hits a wall it will have an equal chance to move away from the wall as it does to attempt to move into the wall. I fixed this issue with a small if statement that changes the values of Yvar (the variable that effects the probability for the next room to move up or down). If the Y value is equal to gridSize or 0 then Yvar will change from 50 to 25 or 75, reducing the probability to move in the same direction. This fix is far from perfect but it does the job for the time being and produces some interesting looking dungeons.

No clear start or end -

Visibly determining whether a room is the start or end would be difficult, however as far as my script is concerned there isn't one. This may not seem like a problem right now, and that's because it's not, but when it comes to implementing other features such as; a stash at the start of every dungeon, making sure there are no enemies in the first room or have some kind of entrance door, I would like to know which room is the start and which is the end.

To do this I used an integer variable that increments every time a new room co-ordinate is decided (roomCount). From this I know the index of the array that contain the last room's co-ordinates. I then used two if statements "if(arrayPosition == 0)" and "if(arrayPosition == roomCount)". Finally to make these rooms visibly noticeable i changed their colours.

Redrawing of rooms -

This is the biggest of the 3 problems in my opinion, from the pictures it is impossible to see this but from the code you can clearly see how this is possible. Currently if a new room has been placed by incrementing Y there is nothing saying that the next room to be placed will be on a decrement of Y, this gives the dungeon the opportunity to continuously move up and down along the Y axis and never progress along the X. This causes increased loading times and lower FPS during gameplay.

Although this is the biggest issue it isn't that difficult to fix. I introduced some Boolean variables; moveEast, moveNorth, moveSouth. My aim is to check these variables when deciding where a room shall be placed. When a room moves North (incrementing Y) moveNorth will be set to true, if moveNorth is true, a room cannot be placed by decrementing Y (moveSouth), when the room is placed by incrementing X (moveEast) both moveNorth and moveSouth will be set to false allowing the possibility for the path to move North or South again.

Here's the end result of these fixes :)


Let's get started!

Immediately after it was decided I would be focusing on dungeon generation I whipped up some code in what I'd call a mix of php and C++ (prior to writing this code i was doing a lot of php coding for my Application Development module so it was just in the back of my mind). My immediate thought is that I wanted to create a script that would produce a progressive increase in X and Y values that i could then transform some kind of prefabrication by to position 'Rooms' along my dungeon path.

The script I made would initialise a pair of variables and progressively increment or decrement either one of them but never both (this would cause a diagonal movement along a pair of axis, which I didn't want) after deciding which value (X or Y) to alter the script would then add the value to an array. This would continue under the constraint "while(X != gridSize)" this just gave the script an ending point, eventually gridSize could be any number you or I wanted.

The first 'room' to be placed into the array would start at the co-ordinates; (0,0-gridSize)  to do this I initialised the Y variable with a 0-gridSize random number generator attached to it. The next form of randomisation involves deciding whether additional room values are an increment for X or an increment/decrement to Y (There is no decrement on X because I don't want the path to go back on it's self at this moment). I did this by creating 2 addition variables initialised with a random generator between 0-100(Xvar and Yvar). I then create an if statement to decide whether X will be incremented "(If Xvar <= 25)" else increment Y (if Yvar <= 50) else decrement Y. Adjusting the numbers in the if statement will ultimately change the shape of the dungeon.

At this time i had no way of graphically representing anything i'd done so until then i spent time getting familiar with Unity3D, a few tutorials later i managed to represent the values in my array by drawing cubes into the engine then transforming their X and Z locations by the X and Y values in my array respectively.

This is what i got :)




Introduction

Hi there, I'm Joel and I'm a Computer Games Programming student at the University of Derby in the UK. I'm creating this blog not only to share the progress of my work but also to work as a semi portfolio to show future potential employers.

Currently I am working on a team project under the group name Indiecisive Studios (see what we did there?). The project is a game called 'Venture', a Hack and Slash Action Adventure Survival Game with Role Playing Game Elements in a world that is based on random terrain, item and dungeon generation, The aim of the game is to finish with the highest score possible, whether that is decided on time survived, items gained or enemies slain is still a work in progress. Venture will use the Unity3D game engine and we will be coding in C# for the most part.

My main focus so far has been to create a dungeon that is randomly generated. The methods I use to do this were up to me and for now this blog will showcase how i am progressing towards that goal. I will also mention occasionally the progress of my team and how we work together to create 'Venture'.