Terug naar Blog

Defold gaat volledig 3D: Een technische breakdown en architectuur-tutorial

Gepubliceerd op 12 mei 2026
Defold gaat volledig 3D: Een technische breakdown en architectuur-tutorial

Kort samengevat

Defold is getransformeerd tot een volwaardige 3D game engine zonder in te leveren op zijn kenmerkende snelheid en kleine build size. Deze gids verkent de architecturale verschuiving naar een perspective rendering pipeline en het implementeren van custom GLSL shaders en camera-controllers. Voor developers die multiplayer-functionaliteit willen schalen, biedt de integratie met horizOn een krachtige backend-oplossing die de complexiteit van server-orchestratie wegneemt.

Elke indie developer kent het moment waarop hun game engine hen in de steek laat. Je begint met een lichtgewicht 2D-concept, scope creep introduceert 3D-elementen, en plotseling explodeert de build size van je engine, kruipen je laadtijden voorbij en crashen je web-builds door een gebrek aan geheugen. Massieve engines zoals Unity en Unreal zijn fenomenaal voor AAA-fidelity, maar voor solo developers die mikken op frictieloze, cross-platform distributie — vooral WebGL en mobile — voelt het vaak als met een tank naar de supermarkt rijden.

Maak kennis met Defold. Historisch gezien geprezen als een ultrasnelle, zero-bloat 2D engine die door studio's wordt gebruikt om elk platform van HTML5 tot Nintendo Switch te bereiken met een enkele codebase, is Defold officieel volwassen geworden. Het is nu een volledig capabele 3D game engine. Hoewel het technisch gezien onder de motorkap altijd al in een 3D-context rendeerde (door platte vlakken te projecteren via een orthografische camera), hebben de recente updates dedicated 3D-tooling, volledige glTF mesh support en een gestroomlijnde workflow voor echte 3D-ontwikkeling geïntroduceerd.

Dit is een enorme verschuiving voor het ecosysteem. Als je op zoek bent naar een engine die in seconden compileert, binaries van slechts enkele megabytes produceert en je nog steeds de kracht geeft om dynamische 3D-werelden te renderen, dan is Defold nu een top-tier kanshebber. In deze Defold 3D game engine tutorial en architecturale breakdown duiken we diep in hoe de rendering pipeline van Defold werkt, hoe je vanaf nul een custom 3D-camera script, en wat de backend implicaties zijn van het netwerken van een lichtgewicht 3D-game.

Onder de motorkap: De realiteit van de 3D-pipeline van Defold

Om te begrijpen hoe je Defold in 3D beheerst, moet je de rendering-filosofie begrijpen. Defold geeft je niet direct een vooraf geconfigureerde PBR (Physically Based Rendering) pipeline zoals Unreal Engine dat doet. In plaats daarvan biedt het een zeer geoptimaliseerd, data-driven render script geschreven in Lua.

Alles wat in Defold op het scherm wordt getekend, wordt afgehandeld door een render_script. Standaard is dit script geconfigureerd voor 2D. Het stelt een orthografische projectiematrix in, sorteert sprites op hun Z-waarde (depth) en tekent ze van achter naar voren. Om de 3D-mogelijkheden van Defold te ontgrendelen, moeten we dit script herschrijven om een perspective projection matrix te gebruiken, hardware depth testing in te schakelen en custom render predicates te definiëren voor onze 3D models.

Deze low-level toegang is een tweesnijdend zwaard. Aan de ene kant moet je wat matrix-wiskunde schrijven. Aan de andere kant heb je absolute controle over je draw calls, waardoor je de rendering kunt optimaliseren voor ultra-low-end hardware op manieren die monolithische engines ongelooflijk moeilijk maken.

Je custom 3D Render Script architectureren

Om echte 3D models te renderen zonder dat ze elkaar onjuist overlappen op basis van de tekenvolgorde, moeten we de depth buffer (Z-buffer) inschakelen. Hier is een fundamenteel 3D render script dat de standaard pipeline van Defold vervangt.

-- main/3d_pipeline.render_script
function init(self)
    -- Define the background clear color (RGBA)
    self.clear_color = vmath.vector4(0.1, 0.1, 0.12, 1.0)
    self.clear_buffers = {
        [render.BUFFER_COLOR_BIT] = self.clear_color,
        [render.BUFFER_DEPTH_BIT] = 1.0,
        [render.BUFFER_STENCIL_BIT] = 0
    }

    -- Create render predicates. 'model' is the default tag for 3D meshes
    self.predicates = {
        model = render.predicate({"model"}),
        gui = render.predicate({"gui"}),
        text = render.predicate({"text"})
    }
end

function update(self)
    -- 1. Setup the rendering state for the frame
    render.set_depth_mask(true)
    render.set_stencil_mask(0xff)
    render.clear(self.clear_buffers)

    -- 2. Configure the 3D Camera Projection
    local window_width = render.get_window_width()
    local window_height = render.get_window_height()
    if window_width == 0 or window_height == 0 then return end
    
    local aspect_ratio = window_width / window_height
    local fov = math.rad(60) -- 60 degree Field of View
    local near_z = 0.1
    local far_z = 1000.0
    
    local proj_matrix = vmath.matrix4_perspective(fov, aspect_ratio, near_z, far_z)
    render.set_projection(proj_matrix)

    -- 3. Draw 3D Models with Depth Testing enabled
    render.set_depth_test(render.COMPARE_LEQUAL)
    render.set_cull_face(render.FACE_BACK)
    render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
    
    -- The view matrix is passed via messages from our camera script
    if self.view_matrix then
        render.set_view(self.view_matrix)
        render.draw(self.predicates.model)
    end

    -- 4. Draw GUI over the 3D scene (Orthographic)
    render.set_depth_mask(false)
    render.set_depth_test(render.COMPARE_ALWAYS)
    local gui_proj = vmath.matrix4_orthographic(0, window_width, 0, window_height, -1, 1)
    render.set_projection(gui_proj)
    render.set_view(vmath.matrix4())
    
    render.draw(self.predicates.gui)
    render.draw(self.predicates.text)
end

function on_message(self, message_id, message)
    if message_id == hash("set_view_matrix") then
        self.view_matrix = message.matrix
    end
end

Merk op hoe expliciet we hier de state beheren. We wissen de depth buffer, berekenen de perspective matrix op basis van de huidige vensterdimensies, dwingen back-face culling af om rasterization cycles te besparen, en schakelen tot slot de projection matrix terug naar orthografisch voordat we de GUI renderen. Dit geeft je een robuuste, split-pipeline rendering architectuur.

Een First-Person 3D Camera Controller bouwen

Een render script alleen zal niet veel laten zien zonder een camera die door de ruimte beweegt. Defold werkt zwaar op een Message Passing architecture. In tegenstelling tot objectgeoriënteerde engines waar de camera direct transform.Translate() zou kunnen aanroepen, zal in Defold ons camera-script de view matrix berekenen en deze verzenden naar het render script dat we zojuist hebben geschreven.

Laten we een standaard First-Person camera bouwen die mouse-look (pitch and yaw) en keyboard movement (WASD) afhandelt.

-- scripts/camera_controller.script
go.property("mouse_sensitivity", 0.2)
go.property("move_speed", 10.0)

function init(self)
    msg.post(".", "acquire_input_focus")
    self.pitch = 0
    self.yaw = 0
    
    -- Hide and capture the mouse cursor
    window.set_mouse_lock(true)
    
    self.forward = vmath.vector3(0, 0, -1)
    self.right = vmath.vector3(1, 0, 0)
    self.up = vmath.vector3(0, 1, 0)
    self.velocity = vmath.vector3(0)
end

function update(self, dt)
    local pos = go.get_position()
    
    -- Apply movement velocity
    if vmath.length_sqr(self.velocity) > 0 then
        local move_dir = vmath.normalize(self.velocity)
        pos = pos + move_dir * self.move_speed * dt
        go.set_position(pos)
    end

    -- Calculate the view matrix
    local rotation = go.get_rotation()
    self.forward = vmath.rotate(rotation, vmath.vector3(0, 0, -1))
    local target = pos + self.forward
    
    local view_matrix = vmath.matrix4_look_at(pos, target, self.up)
    
    -- Send the calculated view matrix to the render pipeline
    msg.post("@render:", "set_view_matrix", { matrix = view_matrix })
    
    -- Reset velocity for the next frame
    self.velocity = vmath.vector3(0)
end

function on_input(self, action_id, action)
    if action_id == hash("mouse_moved") then
        self.yaw = self.yaw - action.dx * self.mouse_sensitivity
        self.pitch = self.pitch + action.dy * self.mouse_sensitivity
        
        -- Clamp pitch to prevent the camera from flipping over
        self.pitch = math.max(-89, math.min(89, self.pitch))
        
        -- Convert Euler angles to a Quaternion
        local rot_y = vmath.quat_rotation_y(math.rad(self.yaw))
        local rot_x = vmath.quat_rotation_x(math.rad(self.pitch))
        go.set_rotation(rot_y * rot_x)
        
    elseif action_id == hash("move_forward") then
        self.velocity = self.velocity + self.forward
    elseif action_id == hash("move_backward") then
        self.velocity = self.velocity - self.forward
    elseif action_id == hash("move_left") then
        self.right = vmath.cross(self.forward, self.up)
        self.velocity = self.velocity - self.right
    elseif action_id == hash("move_right") then
        self.right = vmath.cross(self.forward, self.up)
        self.velocity = self.velocity + self.right
    end
end

Dit script vangt mouse delta movements op om pitch en yaw aan te passen, en zet die Euler angles om in een robuuste Quaternion rotatie. Vervolgens worden de forward en right vectoren direct uit die rotatie afgeleid om ervoor te zorgen dat het indrukken van "W" je altijd in de richting beweegt waarin je momenteel kijkt.

3D Asset Integratie en Custom Shaders

Met de recente updates van Defold is het binnenhalen van 3D assets in je project triviaal geworden. De engine ondersteunt standaard de .gltf en .glb formaten, die de industriestandaard zijn geworden voor web en lichtgewicht game development.

Het renderen van een mesh vereist echter een material, en materials vereisen shaders. Standaard bevat Defold basis-materials, maar het schrijven van je eigen GLSL shaders geeft je het visuele onderscheidend vermogen dat nodig is om op te vallen. Laten we een snelle, unlit textured shader schrijven die perfect is geoptimaliseerd voor mobile of HTML5 targets.

De Vertex Shader (model.vp)

// model.vp
uniform highp mat4 view_proj;
uniform highp mat4 world;

attribute highp vec4 position;
attribute mediump vec2 texcoord0;

varying mediump vec2 var_texcoord0;

void main()
{
    // Calculate the final screen-space position of the vertex
    vec4 p = view_proj * world * vec4(position.xyz, 1.0);
    var_texcoord0 = texcoord0;
    gl_Position = p;
}

De Fragment Shader (model.fp)

// model.fp
varying mediump vec2 var_texcoord0;
uniform lowp sampler2D texture_sampler;
uniform lowp vec4 tint;

void main()
{
    // Sample the texture and multiply by a color tint
    lowp vec4 tex_color = texture2D(texture_sampler, var_texcoord0.xy);
    gl_FragColor = tex_color * tint;
}

In je Defold material-bestand koppel je de view_proj uniform aan de ingebouwde view-projection matrix van de engine, en texture_sampler aan de diffuse texture van je mesh. Omdat deze shaders geen dynamic lighting of shadow maps berekenen, draaien ze ongelooflijk snel, waardoor je gemakkelijk 60 FPS kunt behouden op low-end hardware.

3D Multiplayer en State Synchronization afhandelen

Wanneer je overstapt van een 2D grid-based game naar een volledige 3D-omgeving, neemt de complexiteit van je networking architecture exponentieel toe. De overgang van 2D naar 3D betekent dat je state synchronization nu rekening moet houden met Z-axis depth, floating-point inaccuracies tussen verschillende physics engines en volledige quaternion rotaties. Als je dit slecht aanpakt, eindig je met ernstige visual stuttering — een veelvoorkomend probleem dat we hebben verkend bij het analyseren van How To Fix Player Location Desync In Uefn And Unreal Engine Multiplayer.

Omdat Defold een lichtgewicht engine is, wordt het niet geleverd met een zwaar, ingebouwd replication system zoals de RPCs van Unreal Engine. Je bent zelf verantwoordelijk voor het efficiënt inpakken van je state-data.

Vertrouwen op REST APIs om 3D-posities te synchroniseren zal je game loop onmiddellijk vertragen. In plaats daarvan heb je persistente, bidirectionele verbindingen nodig. Hoewel onze vorige gids behandelde hoe je kunt Ditch Http Polling An Unreal Engine Websockets Tutorial For Real Time Backends, gelden precies dezelfde architecturale principes direct voor de op Lua gebaseerde WebSocket-extensies van Defold.

Hier is een voorbeeld van hoe je 3D transform data zou moeten serialiseren om de payload size via WebSockets te minimaliseren:

-- scripts/network_sync.lua
local json = require "builtins.scripts.json"

function serialize_transform(go_id)
    local pos = go.get_position(go_id)
    local rot = go.get_rotation(go_id)
    
    -- Compress the payload to the absolute minimum required data
    -- We round position to 2 decimal places to save bandwidth
    local payload = {
        id = hash_to_hex(go_id),
        x = math.floor(pos.x * 100) / 100,
        y = math.floor(pos.y * 100) / 100,
        z = math.floor(pos.z * 100) / 100,
        -- Quaternions require all 4 components for accurate reconstruction
        qx = rot.x,
        qy = rot.y,
        qz = rot.z,
        qw = rot.w
    }
    
    return json.encode(payload)
end

Door de floats af te ronden en alleen essentiële data in te pakken, voorkom je dat je network buffers overstromen tijdens snelle bewegingsupdates. Het beheren van deze flow is de sleutel tot responsieve cross-platform play.

De infrastructuurlast van Cross-Platform 3D

Het schrijven van een zeer geoptimaliseerde 3D game client in Defold is enorm bevredigend. De engine zit je niet in de weg, compileert in milliseconden en laat je strikt focussen op de logica.

Echter, op het moment dat je besluit om die 3D-game multiplayer te maken, cloud saves toe te voegen of cross-platform leaderboards te implementeren, verschuift je focus onmiddellijk van game-ontwikkeling naar server orchestration. Je bent plotseling Dockerfiles aan het schrijven, Kubernetes clusters aan het configureren, aan het worstelen met Redis instances voor session state, en je WebSocket gateways aan het beveiligen tegen DDoS attacks.

Dit zelf bouwen vereist het opzetten van load balancers, database sharding en SSL cert management — gemakkelijk 4-6 weken werk alleen al om een betrouwbaar prototype draaiende te krijgen.

Met horizOn komen deze backend-services vooraf geconfigureerd, waardoor je je game kunt verschepen in plaats van je infrastructuur. horizOn biedt native integraties voor user authentication, real-time database syncing en server-authoritative logic, wat perfect de kloof overbrugt voor lichtgewicht engines zoals Defold die niet worden geleverd met eigen backend-ecosystemen. Je behoudt de snelheid van een piepkleine client-engine terwijl je het zware werk van de server-architectuur uitbesteedt.

Best Practices voor Defold 3D Development

Als je van plan bent een commercieel levensvatbaar 3D-project in Defold te bouwen, houd je dan strikt aan deze architecturale richtlijnen:

  1. Houd Geometrie zwaar geoptimaliseerd: Defold is ontworpen voor snelheid. Om het lichtgewicht voordeel te behouden, moet je je level-geometrie onder de 100.000 polygons totaal per scene houden, vooral als je mikt op HTML5/WebGL. Gebruik baked normal maps in plaats van high-density meshes om detail te simuleren.
  2. Benut Render Predicates voor Frustum Culling: Defold culled objecten die zich buiten het zicht van de camera bevinden in 3D-ruimte niet automatisch out of the box. Je moet custom frustum culling logica schrijven in Lua, waarbij je de model components van objecten die buiten de grenzen vallen dynamisch uitschakelt om rasterization tijd te besparen.
  3. Consolideer Draw Calls via Atlasing: Elk uniek material en texture vereist een aparte draw call die naar de GPU wordt gestuurd. Combineer je textures in grote texture atlases. Als 10 verschillende 3D models exact hetzelfde material en atlas delen, kan Defold ze onder de motorkap veel efficiënter batchen.
  4. Complexe wiskunde pre-calculeren: Matrix-vermenigvuldigingen en quaternion-conversies zijn zeer dure operaties in Lua. Cache je forward en right vectoren en bereken ze alleen opnieuw wanneer de rotatie van de speler daadwerkelijk verandert, in plaats van de zware wiskunde onvoorwaardelijk in elke frame uit te voeren.
  5. Ontkoppel logica van de render-frequentie: Je game-logica (update) draait misschien op 60 FPS, maar je custom physics- of networking-stappen kunnen op 30 FPS draaien. Interpolate je 3D visuele posities op basis van velocity in plaats van ze direct naar de nieuwste state te snappen om boterzachte rendering op verschillende monitor-refresh rates te garanderen.
  6. Beheer Lua Garbage Collection: In een dynamische 3D-omgeving maak en vernietig je regelmatig vector-objecten en matrices. De garbage collector van Lua kan merkbare frame-spikes veroorzaken als deze niet wordt beheerd. Hergebruik vmath.vector3 en vmath.matrix4 instances waar mogelijk door hun interne waarden direct bij te werken, in plaats van nieuwe lokale variabelen te instantiëren binnen je update loop. Pre-allocate memory pools voor bullets en entities.
  7. Bake je verlichting extern: Omdat dynamic lighting in custom GLSL shaders snel je performance budget op mobiele apparaten zal opeten, kun je global illumination en ambient occlusion het beste direct in je textures baken met Blender of Maya voordat je je glTF models exporteert. Een simpele unlit shader met prachtig baked lighting zal altijd beter presteren dan een complexe dynamische shader op mobiele webbrowsers.

Conclusie

De evolutie van Defold naar een robuuste 3D game engine is een enorme overwinning voor onafhankelijke developers. Het behoudt met succes zijn bliksemsnelle compileertijden en ongelooflijk kleine binary footprints, terwijl het de ruwe wiskundige fundamenten en tooling biedt die nodig zijn om uitgestrekte, boeiende 3D-werelden te bouwen. Door custom render scripts te beheersen, matrix-operaties te begrijpen en je netwerkdata efficiënt te serialiseren, kun je cross-platform titels bouwen die technisch concurreren met veel grotere, bloat-heavy engines.

Wanneer je klaar bent om je zeer geoptimaliseerde 3D-client online te brengen en je multiplayer backend te schalen zonder de hoofdpijn van het beheren van ruwe infrastructuur, probeer dan horizOn gratis of bekijk de API docs om te zien hoe snel je real-time services in je volgende Defold-project kunt integreren.


Bron: Defold is Now a Full 3D Game Engine