Wednesday, 25 January 2017

Procedural Generation Tutorial Brick Texture - Part 3 of 4

Procedural Generation Tutorial Brick Texture - Part 3 of 4



The end result of the full tutorial.



At the end of Part 3 we will have produced this (See image 1).
Image 1


Before you start you should know these things:

You should have gone through and completed ‘Part 2’ of this tutorial before going any further. If you find yourself not understanding some of the terminology or code of the tutorial I would also recommend going through the previous tutorial to get up to speed. I will leave a link to it below to go through at your leisure.

http://joseph-easter.blogspot.com/2016/11/procedural-generation-tutorial-brick_28.html



If you follow this tutorial and find I am moving too fast or if you don’t know the things in the list above, I would recommend getting up to speed and then come back to this tutorial when you are ready.

With this tutorial if you want to convert this into JavaScript by all means do so but it may be easier for you to follow this in C# and stick with the language of the tutorial to make it easier.

PROJECT RESOURCES: At the bottom of the post there is a download link to a zip file. This files includes all files needed to follow Part 3 and the completed project file of Part 3.





In this tutorial series, we will be creating a simple brick texture using code, a slight variation from the simple checker board pattern. We will learn some more programming principals.



We will:


  • Recode the brick function creating the right-side brick
  • Create a switch statement
  • Tidy up code, putting left and right brick in separate functions




Step 1:- The theory


This part will be quick simple compared to Part 2. Most of the code is done for creating the brick we just need to copy some of it and change a few variables. The complicated bit will come when choosing which brick texture to use and how to make it easy to switch between the two textures.

For the right side of the brick we still want to keep a space between the top and bottom of the texture showing the mortar. However, we need to leave the right edge blank and cover the left side instead. This is easy to do. See figure 1.0 for reference.

Figure 1.0

The difficult will be how we chose which brick texture to colour in the function and how to make it easy for other programmers to use. We will basically parse an enum to the function. Then that function will have a switch statement which will use the enum to decided which part of the function to run. When this is, all done, we will tidy up the code and put the left and right brick into different function and reference them.

Step 2:- Choosing Left or Right, Recoding the function

The first thing we will do is modify the ‘CreateBrickColour’ method. Now, it only creates the left side of the brick, we need it to create both textures on demand as we need them. We can do this by parsing a value to the function and a switch statement can decide what to do based on the parsed value. We will create a switch statement because if we want to add more settings we can add it to the code easily and it’s easier to read. We will keep the ‘leftBrickTexture’ as the first option.

When writing the switch statement, we need a variable with multiple settings, we could create an int or string to use, but because the function is specific this could confuse developers. For this reason, we will use an enum with specific descriptive settings. Create an enum called ‘brickHalves’ and add ‘left’ and ‘right’ to it. After this add an enum parse variable to the function and put the ‘leftBrickTexture’ code in the first switch statement brackets.

Code:

private enum brickHalves {left, right};
private brickHalves brickHalf;
...
void Start()
{
   SetMainTextureSize();
   ConvertBricksToSquares(brickX, bricksY);
   CreateMortarColour{leftBrickTexture);
   CalculateBrickAndMortarSpacing();
   CreateBrickColour(bricksHalves.left);
   ConvertColourToArray(blockWidth * 
                        blockHeight);
   //CreatePattern();
}
...
void CreateBrickColour(brickHalves bH)
{
   switch(bH)
   {
      case brickHalves.left:
           leftBrickTexture.SetPixels32(
                  mortarThicknessWidth,
                  mortarThicknessHeight,
                  brickColourWidth,
                  brickColourHeight,
                  colour1Arr);
           leftBrickTexture.Apply();
           break;
   }
}


Now this is good but we need to add the brick colour code to the ‘rightBrickTexture’ Texture2D variable in step three. This will be quite straight forward.

Step 3:- Creating the Right-Side Brick Texture

Before we do anything else we need to comments out the line where we assign the ‘leftBrickTexture’ to ‘mainTexture’ to avoid confusion.

Code:

void CreateMortarColour(Texture2D tex)
{
   ConvertColourToArray(tex.width *
                        tex.height);
   tex.SetPixels32(0, 0, tex.width,
                   tex.height,
                   colour0Arr);
   tex.Apply();
   //GetComponent<Renderer>().material.
                           mainTexture = tex;
}


Back to the ‘CreateBrickColour’ function. In the ‘CreateBrickColour’ function create a new case in the switch statement for ‘brickHalves.right’, this is the option for the right half of the brick. In the start function just under the first ‘CreateBrickColour’ reference add another one but parse ‘brickHalves.right’ instead of ‘brickHalves.left’. Also, we need to colour the mortar background of the texture as well. Add another reference to ‘CreateMortarColour’ and parse it ‘rightBrickTexture’.


Code:

void Start()
{
   SetMainTextureSize();
   ConvertBricksToSquares(brickX, bricksY);
   CreateMortarColour{leftBrickTexture);
   CreateMortarColour{rightBrickTexture);
   CalculateBrickAndMortarSpacing();
   CreateBrickColour(brickHalves.left);
   CreateBrickColour(brickHalves.right);
   ConvertColourToArray(blockWidth * 
                        blockHeight);
   //CreatePattern();
}
...
void CreateBrickColour(brickHalves bH)
{
   switch(bH)
   {
      case brickHalves.left:
           leftBrickTexture.SetPixels32(
                  mortarThicknessWidth,
                  mortarThicknessHeight,
                  brickColourWidth,
                  brickColourHeight,
                  colour1Arr);
           leftBrickTexture.Apply();
           break;
      case brickHalves.right:
           break;
   }

}


Now in this new case we will use ‘SetPixels32’ on ‘rightBrickTexture’. A lot of the code will look very similar since a lot of it is doing the same stuff just right aligning the brick colour block. What we need to do is copy and paste the code from the above case but change the texture name to ‘rightbrickTexture’. This is because we already have the size and colour of the pixel area we need to cover and the position on the ‘Y’ axis to start at. What we need to change is where the ‘X’ starting position is. Because ‘SetPixels32’ works from the bottom left to top right, and we want the brick to have a space on the right edge of the texture we tell it to start from the bottom left pixel of the texture which is ‘0’. Then like last time we apply the changes to the texture using ‘rightBrickTexture.Apply’.Now in this new case we will use ‘SetPixels32’ on ‘rightBrickTexture’. A lot of the code will look very similar since a lot of it is doing the same stuff just right aligning the brick colour block. What we need to do is copy and paste the code from the above case but change the texture name to ‘rightbrickTexture’. This is because we already have the size and colour of the pixel area we need to cover and the position on the ‘Y’ axis to start at. What we need to change is where the ‘X’ starting position is. Because ‘SetPixels32’ works from the bottom left to top right, and we want the brick to have a space on the right edge of the texture we tell it to start from the bottom left pixel of the texture which is ‘0’. Then like last time we apply the changes to the texture using ‘rightBrickTexture.Apply’.

Code:

void CreateBrickColour(brickHalves bH)
{
   switch(bH)
   {
      case brickHalves.left:
           leftBrickTexture.SetPixels32(
                  mortarThicknessWidth,
                  mortarThicknessHeight,
                  brickColourWidth,
                  brickColourHeight,
                  colour1Arr);
           leftBrickTexture.Apply();
           break;
      case brickHalves.right:
           rightBrickTexture.SetPixels32(
                  0, mortarThicknessHeight,
                  brickColourWidth,
                  brickColourHeight,
                  colour1Arr);
           rightBrickTexture.Apply();
           break;
   }

}


Now this is ok but we want to see our texture on the plain itself to check for imperfections. To do this we will assign ‘rightBrickTexture’ to the ‘mainTexture’ of our game objects material. We will add this temporally in the case of the switch statement when we colour it and after applying changes. (You can remove or comment this line out after wards because we are just checking for mistakes on a big level). (See image 1.1 and 1.2).

Figure 1.1

Code:

void CreateBrickColour(brickHalves bH)
{
   switch(bH)
   {
      case brickHalves.left:
           leftBrickTexture.SetPixels32(
                  mortarThicknessWidth,
                  mortarThicknessHeight,
                  brickColourWidth,
                  brickColourHeight,
                  colour1Arr);
           leftBrickTexture.Apply();
           break;
      case brickHalves.right:
           rightBrickTexture.SetPixels32(
                  0, mortarThicknessHeight,
                  brickColourWidth,
                  brickColourHeight,
                  colour1Arr);
           rightBrickTexture.Apply();
           GetComponent<Renderer>().material.
              mainTexture = rightBrickTexture;
           break;
   }

}


Figure 1.2

Step 3:- Creating the Right-Side Brick Texture

What we have is good for now. However, we could make this a little neater, also if we add more to this function the code will be harder to read. The good thing about this function is that it’s no more than 20 lines. Although it does two separate things; it chooses what to do based on a value using a switch statement, it uses ‘SetPixels32’ on a texture and applies those changes. We can do a little better than that and make the code easier to read. The best thing to do is create a function for the Left brick and the Right brick and reference those functions in the switch statement case. That is what we will do here.

We will create a function below ‘CreateBrickColour’ called ‘LeftBrickColour’. Then cut and paste the code from the ‘brickHalves.left’ case to the ‘LeftBrickColour’ function.

Code:

void LeftBrickColour()
{
   leftBrickTexture.SetPixels32(
   mortarThicknessWidth,
   mortarThicknessHieght,
   brickColourWidth,
   brickColourHeight,
   colour1Arr);
   leftBrickTexture.Apply();

}


Now create another function below that and call it ‘RightBrickColour’. Then do the same thing but with the code from ‘brickHalves.right’ in the ‘CreateBrickColour’ function to the new ‘RightBrickColour’ function.

Code:

void RightBrickColour()
{
   rightBrickTexture.SetPixels32(
   mortarThicknessWidth,
   mortarThicknessHieght,
   brickColourWidth,
   brickColourHeight,
   colour1Arr);
   rightBrickTexture.Apply();

}


Reference these new functions where the code was previously in the case statements.

Code:

void CreateBrickColour(brickHalves bH)
{
   switch(bH)
   {
      case brickHalves.left:
           LeftBrickColour();
           break;
      case brickHalves.right:
           RightBrickColour();
           break;
   }

}

Great, part three is finished. The most of the basic building blocks are done. We need to put them all together now into a pattern.

We have learnt how to:
  • Write and use a switch statement
  • Perform a task based on a given switch statement case
  • How switch statements can be easier than using multiple if statements
  • How to use a custom enumeration (enum) and parse it to a function
  • How an enumeration can make multiple settings easier to implement 
  • How to tidy up our code and give a function one specific task

In part 4 we will learn how to get and set pixels form one texture to another using GetPixels and SetPixels together. We will then use this to create our large brick pattern based on the checker board code and the two textures we have created ‘leftBrickTexture’ and ‘rightBrickTexture’ and finish this segment.

Click here for Part 4.

Download resources and project files.

2 comments:

  1. This is a good read. The left and right sides of the brick are split into two separate textures and both textures are split into separate posts. I like this because it has kept the individual posts relatively short, and the differences in the code are explained more clearly yet quickly. It is explained how to make the code easier for developers to use by using a switch statement in a function and parsing it an enum for the appropriate side of the brick. It was nice to learn how to clean up the code as part of the tutorial, this is a valuable skill when a project becomes more complex and make function have specific purposes. Specifically creating the left and right sides of the brick are put into different methods as they are so distinct.

    ReplyDelete
  2. I am glad I read this. I learnt how to use an enum and parse it to a method. I also learnt how to implement and use a switch statement. Like the comment above I am glad it left in how to clean up the code as maintaining it could get more cluttered later on when things get more complex. It makes it easier to read as well and isolate problems when they arise. Giving a function one specific task is a good programming principal. I like the length of the post as well, giving both textures their individual posts. Keep things brief and too the point.

    ReplyDelete