[Unity] Voxel terrain generation - 8 - Grass and FlowerssteemCreated with Sketch.

in indiedev •  6 years ago 

 We've done a lot, so why don't we make the world look better?

We will implement Grass and Flowers.


 Let me explain how this works :

Before, we generated cubes, only cubes. But now, we will generate a new mesh shape called "Billlboard". It will be a cross, with two planes rendered from each side. It is the best solution we have to optimize the game.

 

But there are still lots of things to deal with. And a main issue :

If we remove collision to walk through grass, how can we still have a collision with the raycast. And how do we have a cube collision for a cross mesh?

Also, there is one thing to remember :

Billboard will also be called "Blocks". A block can be of any shape. It's collision makes it a cube.


- Let's start by adding our new blocks to the database, but first, go to your "Block" script :

We will need a new boolean called "isBillboard". When a block will be generated, it will know what type of block it is. If is is a billboard or not.


- Now, in the first constructor (Full texture block) we will need to set the type of block :

 We will do it in the "full texture" constructor only because we don't need more than one texture. 


- Now go to your "DB_Block" script :

For each of our Full Texture blocks, we will have to set the new value, if it is a Billboard or not :

 And now we can add our new blocks, Grass and Flower : 

 

I used the name "Grass_bill" because "Grass" already exists. And because we search in the database with names, we can't have the same.

I used "Flower1" to add more of them later.

"_bill" will be used to know which of them are Billboards.

Also, notice how i created them :

Name : "Grass_Bill"

isSolid : FALSE

isTransparent : TRUE (We will generate blocks around it)

texture : 2,1

isBillboard : TRUE


- Now, we have to know where we can generate these blocks. To do that, go to the "Noise" script :

Copy and paste the "GenerateHeight" method and call it "GenerateGrassHeight".

 Here, we will get the same height we have for the grass layer, but we add 1 to place them above grass. 


- Go to the Chunk script and let's test this method :

 

We create the new layer after we added grass. We calculate the height (grass height + 1) .

We don't don't want to fill the layer, so we will add a probability to have it appear. (Random.Range, between 0 and 100, we do it if we are greater than 85).

Here, we will create a diamond block to test our code. It will be replaced later.


 - Now let's test it : 

 Diamond blocks are spawning above the grass layer. You can change the probability to have more or less blocks. 


- Now we will replace these blocks with billboards. But before we do that, there are things to understand :

- When the chunk is generated, the collider will be made from the block inside of it. 

- If we add billboards, we want to be able to walk through them, meaning that we won't count them when building the collider.

- BUT, if the block doesn't have a collider, we won't have a collision with the raycast and won't be able to find the block.

- Finally, the collision of a block is generated from it's values. Making a billboard will give a cross collider and not a cube. The detection with a raycast won't work like we expect. So we need a cube collider around the cross billboard


So..how do we make it work?

The best solution is to generate another chunk object linked to the chunk instance. So a chunk will have to version of himself. One with collision, and another one without collision.

The non collider object will actually have a collider but we will make this object unable to collide with the player. It will only collide with the raycast.


 - Go to your "Chunk" script : 

 We add the second chunk object, the one without collisions. 


 - We will generate an object from the chunkNoCollideObj and set it's position, just like the chunk object we had : 


 - This object will have it's own values and it's own mesh : 


 - In the "ClearChunk" method, we will also clear the new chunk : 


 - Now in the "GeneratePhysicalChunk" method, we will also generate the new chunk : 


We will generate it exactly like we did for the other object but with the new values.

There is only one more line : "chunkNoCollideObj.layer = 8;". 

The chunk without collision object will be placed on the 8th layer. The player will be on another layer. So we can tell Unity to avoid collisions between these two layers but sill have a collision.


- Let's make it work in Unity :

Here we will create a new layer in Unity, it will contain the billboard chunkObjects and will know how to manage collisions. We could initialize a new layer directly in the code but i want to keep the guide easy. So, remember that if you import your scripts in another project, you must do the same manipulation :

In the toolbar, go to Edit > Project Settings > Tags and Layers and open the layers menu. 


 - Then, add the "noCollide" layer to the first User Layer, the 8th layer : 

 

- Now go to Edit > Project Settings > Physics : 


 - The Layer Collision Matrix is where you will decide what are the collisions between the layers. Here, the player is in the layer "Ignore Raycast" and the chunk is in "noCollide", so we will disable the collision between them : 

That's what we could access and initialize inside the code but to keep an easy project, i won't explain it. It's up to you to look up for it and learn how it works if you want it to be done automatically.


With this, the player won't be able to collide with the no collision chunk because we put the new chunk on the noCollide layer when it is generated.


- But it can't work yet, because our blocks are still being generated on the first chunk. We have to put them on the other one :

Go to your "Block_Cube" script, let's change the constuctor.

When we call this constructor to generate a new block, we will also need to know if it is a billboard.

If it is, we start the billboard block type creation.

If it isn't, we start the cube creation.

This is the GenerateBillboard method.

It won't check if there is a block nearby, it will only create the block.

There are 4 new faces.


Let me explain :

We will have 4 faces for the cross mesh, without top and bottom, and a cube around it for the collision.

If we make a cross billboard, we will only have a cross collider and a really bad collision detection for the raycast.

What we need is a cross mesh and a box collider around the block.

BUT, if we make a cube and a cross inside, we still have to set UV's to every face and we need an invisible cube around the cross.

The only way to do that is to have a transparent texture in the texture atlas, a blank texture. It will be useful for more meshes so we can have one.


 Here, 1,1 will be my transparent texture. 


- Now, to generate the block, we will call "CreateBillboardSide" with the side we need. It will be almost the same as we did for the cube but with 4 more faces :

Like we did, we generate triangles and all the vector possibilities.

But this time, we store them in our new lists used for the no collision chunk.

 Same for the faces generation, we use the same we had for the cube. 

 But this time, we add the 4 new faces. They will use the cube vertices and draw two diagonals. 


Now, we apply UVs like we used to. But this time, the block texture will be on the cross faces only.

Also, because i use now a 6x6 atlas, i will change the texture offset. I did the same for the cube generation.


 Finally, we do the same for the cube around the cross but it will have the transparent texture that we set manually. 


 - Before we test the game, there is one last thing to do. Go to the Chunk script and on the layers generation : 

We replace the line we used to generate diamonds. The one we added to test the layer.

Here, at the grass billboard layer, still for the same probability, we have a list of plants that we can have. We add the blocks to the list and pick a random one to place in the world.


 - Let's test our game to see how it works : 

Our meshes are perfectly being generated and we have a cube collider. It's working !

But we can't remove blocks because when we regenerate the chunk, we also have to regenerate the no collision chunk.


 - Go to the World_Interaction script, in the "ClearChunk" method : 

 We will clear the other one. It should be working. 


- Let's test our game again :

We can now destroy our blocks. 

Also, remember that this solution is useful because the two chunk objects (Collision and no Collision) are from the same Chunk script instance. So they share the same chunk map.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

remove collision

Why should i? This is the most important part of the code

terrain generation

Thanks willfe for this Great Tutorial Serie "Voxel terrain generation" you helped me alot creating my Unity Game. even that the Tutorial Series is 6 years old it still works fine and is great explained.