An FPS game in the style of Minecraft. This is my first attempt at writing a voxel engine and multiplayer system in Unity.
For example, a cube map of size 128x128x10 has 163,840 cubes, which are 1,966,080 triangles and 5,898,240 faces. If each cube is a separate game object, Unity draws each one in a separate batch, i.e. a GPU operation. This is the fastest way to create a map, but it has a huge computational cost in the rendering phase, even for such a small map.
We could try enabling static batching on each cube to reduce the number of batches, but that doesn't change the fact that many unnecessary, hidden faces are drawn.
A Voxel Engine draws only the visible part of the map. The hidden faces, which are many, are not rendered. Also, the map is divided into chunks so that 1 batch operation can draw multiple blocks at once.
When digging or placing a block, we need to detect which voxel the player is looking at. A common approach to solving this problem is to draw a line of increasing length in front of the camera until it reaches a solid voxel or the reach length.
for (var lenght = 0.1f; lenght < Reach; lenght += 0.1f)
{
var pos = Vector3Int.FloorToInt(_transform.position + _transform.forward * (length));
var blockType = _wm.GetVoxel(pos);
...
}
Now, as the player may be looking at a block with a steep angle, we need to find a balance between how precise we want the Raycast to be, i.e. a small value for the step increment, and how fast we want it to be, i.e. how many steps are taken. In the example above, a value of 0.1 has been chosen.
However, as this whole operation is performed on each player's camera movement, it needs to be done as quickly as possible.
I, on the other hand, decided to use the higher-level `Physics.Raycast' Unity feature.
if (Physics.Raycast(ray, out hit, Reach, 1 << LayerMask.NameToLayer("Ground")))
if (hit.collider != null)
{
var pos = Vector3Int.FloorToInt(hit.point + cameraTransform.forward * 0.05f);
var blockType = _wm.GetVoxel(pos);
...
}
This solution is not only faster, but also more general, as it can be applied to enemy damage detection. In fact, by changing the layer on which the ray is cast to "Enemy", we can detect whether the shot has reached the enemy and, if so, which part of his body has been hit, thanks to the multiple colliders.
The relay service was used in conjunction with the lobby service to provide remote multiplayer. To avoid interfering with the host's router, firewall, and port forwarding, the relay system inserts a server between the host and the clients.
In this project, to switch between localhost and remote modes you should:
- Deactivate the
LobbyManager
, - Activate the
DebugManager
, - Set the
NetworkManager
's transport protocol type to "Relay Unity Transport".
Task | Done |
---|---|
Player movement | β |
Basic Voxel Engine | β |
UV mapping and texture atlas | β |
Texturepack and BlockTypes array | β |
Chunk subdivision | β |
Map generator | β |
Creating and deleting blocks | β |
Submeshes for transparent blocks | β |
Inventory management | β |
Multiplayer implementation | β |
Map.developing.demo.mp4
Author | Github profile |
---|---|
Valerio Morelli | https://github.com/MrPio |