ArrayMesh in Godot – Complete Guide

Welcome to the world of procedural geometry in Godot 4, where you have the power to craft and mold the virtual world with code! In this tutorial, we’ll explore the robust ArrayMesh class – a key tool for creating and manipulating 3D meshes dynamically. Perfect for both novices enthusiastic to learn game development and experienced coders looking to sharpen their skills, we’re diving into a topic that stands at the core of visual richness in games. So whether you dream of sculpting stunning landscapes, designing intricate objects, or simply have an interest in 3D game mechanics, here’s your portal into crafting them from scratch.

What is ArrayMesh?

At its essence, an ArrayMesh is a type of Mesh in Godot used to construct surfaces by specifying their attributes as arrays. Think of it as a flexible mold that you can fill with numerical data to shape your 3D assets. Unlike a StaticMesh, which is fixed and cannot be changed at runtime, an ArrayMesh gives you on-the-fly control over your geometries.

What is ArrayMesh used for?

ArrayMesh comes in handy when you need to create 3D objects procedurally. Whether you’re coding a game that requires terrain generated on-the-fly, objects that morph in real-time, or simply want to create your content through code rather than traditional 3D modeling methods, ArrayMesh gives you that flexibility.

Why is learning ArrayMesh important?

Learning about the ArrayMesh class opens up a new dimension of possibilities for your game development toolbox. It allows for innovative mechanics, such as destructible environments or customizable player avatars. Understanding ArrayMesh also forms a strong foundation for thinking about 3D spaces in games and how they’re constructed, which is a valuable skill in the game development process.

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Setting Up the ArrayMesh in Godot 4

To start using ArrayMesh in your Godot 4 projects, first, you need to initialize it. Below is an example of how to create a new ArrayMesh and a MeshInstance to display it. The MeshInstance node is what actually makes the mesh appear in the scene.

var array_mesh = ArrayMesh.new()
var mesh_instance = MeshInstance.new()
mesh_instance.mesh = array_mesh
add_child(mesh_instance)

After setting up the basic nodes, it’s time to begin defining the geometry. Your first step will be to construct vertex arrays. Vertices are the corner points of your 3D shapes.

var vertices = PoolVector3Array()
vertices.push_back(Vector3(0, 0, 0)) # Add the first vertex
vertices.push_back(Vector3(0, 1, 0)) # Add the second vertex
vertices.push_back(Vector3(1, 1, 0)) # Add the third vertex

Defining the Surface of the Mesh

To turn the vertices into a surface, you need to define the triangles that make up the mesh. Here’s how you can specify the indices that form a single triangle.

var indices = PoolIntArray()
indices.push_back(0) # Index for the first vertex
indices.push_back(1) # Index for the second vertex
indices.push_back(2) # Index for the third vertex

With the vertices and indices defined, you can build an array that will be used to create the ArrayMesh surface with the PRIMITIVE_TRIANGLES primitive type, indicating each set of three vertices form a triangle.

var array = []
array.resize(Mesh.ARRAY_MAX)
array[Mesh.ARRAY_VERTEX] = vertices
array[Mesh.ARRAY_INDEX] = indices

array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)

Note that ARRAY_MAX is the total number of different vertex attribute arrays you can define, such as colors, normals, and texture coordinates, in addition to vertices and indices.

Adding Color and Texture

To add color to your vertices, you can use a PoolColorArray. Below is an example of assigning a color to each vertex.

var colors = PoolColorArray()
colors.push_back(Color(1, 0, 0)) # Red color for the first vertex
colors.push_back(Color(0, 1, 0)) # Green color for the second vertex
colors.push_back(Color(0, 0, 1)) # Blue color for the third vertex

array[Mesh.ARRAY_COLOR] = colors

If you want to map a texture onto your mesh, you will also need to include texture coordinates for each vertex in a PoolVector2Array.

var uvs = PoolVector2Array()
uvs.push_back(Vector2(0, 0)) # UV for the first vertex
uvs.push_back(Vector2(0, 1)) # UV for the second vertex
uvs.push_back(Vector2(1, 1)) # UV for the third vertex

array[Mesh.ARRAY_TEX_UV] = uvs

Update the surface with the new color and texture coordinate information:

array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)

This foundation will have you setting colors and textures dynamically for your procedural geometries in no time.

Normals and Shading

Normal vectors are crucial for correct lighting and shading of 3D objects. In Godot, you can specify normals using a PoolVector3Array. Let’s calculate normals for a simple triangle as an example:

var normals = PoolVector3Array()
var normal = (vertices[1] - vertices[0]).cross(vertices[2] - vertices[0]).normalized()

normals.push_back(normal) # Same normal for each vertex
normals.push_back(normal)
normals.push_back(normal)

array[Mesh.ARRAY_NORMAL] = normals

After adding in the normals, we have a basic surface that will interact with Godot’s lighting system.

array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)

With these foundational pieces laid out, you’ve now set up a mesh programmatically using ArrayMesh in Godot! The skills you’re acquiring here will be directly applicable to a wide range of 3D applications within your game development journey.

As we continue to refine our ArrayMesh, let’s explore how to create more complex shapes and manipulate them further.

Traditionally, more complex shapes consist of numerous triangles. To extend our single triangle into a square, we simply need to define two triangles that share a side. We’ll adjust our vertex and index arrays accordingly:

vertices.push_back(Vector3(1, 0, 0)) # Add the fourth vertex for square

# Defining two triangles that make up a square
indices = PoolIntArray([0, 1, 2, 0, 2, 3])

With a slight adjustment to the indices array, we now have two triangles forming a square.

Moving forward, Godot allows us to not only define static shapes but to animate and transform them as well through code. Let’s look at how we can scale our ArrayMesh:

mesh_instance.scale = Vector3(2, 2, 2)  # Scale the mesh instance

This will uniformly scale our square mesh by a factor of two on all axes.

But what if we want our shape to move? We can apply a transformation to the MeshInstance directly:

mesh_instance.translation += Vector3(1, 0, 0)  # Move the mesh to the right

Remember that these transformations affect the MeshInstance node, not the ArrayMesh itself. The ArrayMesh’s geometry remains the same, but the instance of it that we see in the world changes.

To add even more interactivity, let’s play with the ability to morph the mesh at runtime. Suppose you want to make one of the vertices of your square move up and down, creating a dynamic waving motion. Here’s an example of how you might achieve that:

func _process(delta):
    var new_vertices = array[Mesh.ARRAY_VERTEX] as PoolVector3Array
    new_vertices[1].y += sin(OS.get_ticks_msec() / 1000.0) * delta
    
    array[Mesh.ARRAY_VERTEX] = new_vertices
    array_mesh.surface_remove(0)
    array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)

By adjusting the y-coordinate of the second vertex over time with a sine wave, we create the effect of the square ‘waving’. Note that we must remove the existing surface and add a new one for the changes to take effect each frame.

Lastly, let’s add a touch of realism by calculating the normals dynamically so that our waving vertex also has correct lighting:

func recalculate_normals(vertices, indices):
    var normals = PoolVector3Array()

    for i in range(0, indices.size(), 3):
        var vertex1 = vertices[indices[i]]
        var vertex2 = vertices[indices[i+1]]
        var vertex3 = vertices[indices[i+2]]

        var normal = (vertex2 - vertex1).cross(vertex3 - vertex1).normalized()
        
        normals.append(normal)
        normals.append(normal)
        normals.append(normal)

    return normals

# Update normals each frame
func _process(delta):
    var new_vertices = array[Mesh.ARRAY_VERTEX] as PoolVector3Array
    # modifications to new_vertices here...
    
    array[Mesh.ARRAY_VERTEX] = new_vertices
    array[Mesh.ARRAY_NORMAL] = recalculate_normals(new_vertices, indices)
    
    array_mesh.surface_remove(0)
    array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)

By recalculating the normals every time we modify the vertices, we ensure the lighting reacts naturally to our mesh’s transformation.

With these examples, you’re now well on your way to mastering dynamic geometry in Godot 4. The power of ArrayMesh lies not just in creating static models, but in bringing them to life through code. Remember, the possibilities are limited only by your imagination and understanding of 3D geometry. Keep experimenting with vertices, indices, and transformations, and you’ll open up a whole new realm of creative potential in your game development projects.Continuing with our exploration of ArrayMesh in Godot 4, let’s delve into creating a customizable cube mesh. Cubes are a fundamental building block in many 3D games, from environment design to puzzle mechanics. By understanding how to programmatically create a cube, you can start to build more complex objects and algorithms for procedural generation.

Creating a cube involves defining eight vertices, one for each corner:

var vertices = PoolVector3Array([
    Vector3(0, 0, 0), Vector3(0, 1, 0), Vector3(1, 1, 0), Vector3(1, 0, 0),  # Front face vertices
    Vector3(0, 0, 1), Vector3(0, 1, 1), Vector3(1, 1, 1), Vector3(1, 0, 1)   # Back face vertices
])

With the vertices defined, we must create the indices for each of the six faces of the cube:

var indices = PoolIntArray([
    # Front face
    0, 1, 2, 0, 2, 3,
    # Top face
    1, 5, 6, 1, 6, 2,
    # Back face
    5, 4, 7, 5, 7, 6,
    # Bottom face
    4, 0, 3, 4, 3, 7,
    # Left face
    4, 5, 1, 4, 1, 0,
    # Right face
    3, 2, 6, 3, 6, 7
])

Creating textured cubes requires us to provide UV coordinates for each vertex. Given that a cube has 6 faces and each face will have a UV rectangle, we’ll handle UVs like so:

var uvs = PoolVector2Array([
    # UVs front face
    Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0),
    # Repeat for every face with corresponding UVs
    # ...
])

The above UVs assume that each face’s texture will be the same; however, if you want different textures on different cube sides, you’ll need to expand your UV mapping accordingly and potentially utilize an atlas.

If a cube is in motion, or if we want to animate it in some way, we can apply transformations directly to the vertex data. For example, we can create a rotating cube by updating the vertices within the `_process` function:

func _process(delta):
    var rotation_speed = 1.0  # Rotation speed in radians per second
    var rotation_axis = Vector3(0, 1, 0).normalized()  # Y-axis

    var transform = Transform().rotated(rotation_axis, rotation_speed * delta)

    for i in range(vertices.size()):
        vertices[i] = transform.xform(vertices[i])

    array[Mesh.ARRAY_VERTEX] = vertices
    array_mesh.surface_remove(0)
    array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)

Here we’re creating a Transform object, rotating it around the y-axis, and transforming each vertex with this rotation Transform every frame.

What about adapting the cube’s size dynamically, say for a power-up animation where the cube grows larger? You could easily incorporate a scaling vector that changes over time:

var scale_factor = Vector3(1, 1, 1)  # Start with original size

func _process(delta):
    scale_factor += Vector3(delta, delta, delta)  # Increase size over time
    array_mesh.surface_remove(0)
    array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array, null, null, false, scale_factor)

Combining these various concepts, you can already see the potential of how ArrayMesh can breathe life into your Godot projects. Whether you’re animating a simple power-up or constructing a complex, ever-changing world, you now have the foundational knowledge to start building it.

As you become more comfortable with ArrayMesh, challenge yourself to explore even further. Consider adding user interaction, integrating physics for collision detection, and combining multiple ArrayMeshes to create more elaborate scenes. Remember to test your creations in the editor often, and don’t be afraid to refactor as you discover more efficient or effective methods of achieving your design goals.

With ArrayMesh, your Godot 4 creations are limited only by your imagination.ampilkan Happy coding!

Advancing Your Skills in Godot 4

Your journey with procedural geometry and the ArrayMesh class in Godot 4 is just beginning. The next step is to expand your game development skills and knowledge through our comprehensive Godot Game Development Mini-Degree. This set of tailored courses covers a spread of topics from 2D and 3D game creation to the nuances of the GDScript programming language, ensuring you can continue to grow and challenge your creative talent. It’s the perfect way to take your newfound skills to the next level, at your own pace and around your own schedule.

Whether you’re a complete beginner or an aspiring developer seeking to polish your portfolio, our Mini-Degree provides a structured path toward becoming proficient in Godot 4. You will learn all about building cross-platform games, controlling gameplay flow, creating UI systems, and implementing various mechanics for different game genres. Each course, designed by experienced and certified instructors, offers interactive lessons, hands-on coding practice, and completion certificates to mark your achievements.

And if you’re hungry for even more knowledge, we welcome you to explore our broader array of Godot courses tailored to serve all proficiency levels. With our extensive library of over 250 courses, Zenva is committed to helping you make your mark in the world of game development. Take that step forward, and let’s build something amazing together!

Conclusion

Embarking on the journey of mastering Godot 4’s ArrayMesh has opened the door to a new world of creative possibilities. You’re now equipped to bring your innovative ideas to life with procedural geometry, fine-tuning your games with the vibrant detail and dynamic interactions that players love. But this is just the starting point. Imagine the worlds you can build and the stories you can tell as you delve deeper into the capabilities of Godot 4. We, at Zenva, are here to guide you every step of the way. So why wait? Level up your game development mastery with our Godot Game Development Mini-Degree, and transform your passion into reality.

Let your journey in game development be a continuous adventure of learning, experimentation, and creativity. Utilize the power of Godot 4 and our extensive course library to challenge yourself, refine your skills, and become the game developer you aspire to be. Together, we will shape the future of gaming, one line of code at a time. Start today, and join a global community of developers who chose Zenva for their success story.

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.