This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English
Jedes Objekt, das die Engine auf dem Bildschirm anzeigt – Sprites, Modelle, Kacheln, Partikel oder GUI-Knoten (GUI nodes) –, wird von einem Renderer gezeichnet. Das Herzstück des Renderers ist ein Render-Skript (render script), das die Rendering-Pipeline steuert. Standardmäßig wird jedes 2D-Objekt mit der richtigen Bitmap, der angegebenen Farbmischung und in der richtigen Z-Tiefe gezeichnet—du musst dich also möglicherweise über die Reihenfolge und einfache Farbmischung hinaus nie mit dem Rendering befassen. Für die meisten 2D-Spiele funktioniert die Standard-Pipeline gut, aber dein Spiel kann besondere Anforderungen haben. In diesem Fall kannst du mit Defold eine maßgeschneiderte Rendering-Pipeline schreiben.
Die Rendering-Pipeline steuert, was gerendert wird, wann es gerendert wird und auch wo es gerendert wird. Was gerendert wird, bestimmen Render-Prädikate (render predicates). Wann ein Prädikat gerendert wird, wird im Render-Skript festgelegt, und wo ein Prädikat gerendert wird, bestimmen Ansicht und Projektion. Die Rendering-Pipeline kann außerdem Grafik, die von einem Render-Prädikat gezeichnet wird und außerhalb eines festgelegten Begrenzungsquaders oder Sichtvolumens liegt, aussondern. Dieser Vorgang heißt Aussondern von Geometrie außerhalb des Sichtvolumens (Frustum Culling).
Die Renderdatei enthält eine Referenz auf das aktuelle Render-Skript sowie benutzerdefinierte Materialien, die im Render-Skript verfügbar sein sollen (zur Verwendung mit render.enable_material())
Das Herzstück der Rendering-Pipeline ist das Render-Skript. Es ist ein Lua-Skript mit den Funktionen init(), update() und on_message() und dient hauptsächlich dazu, mit der zugrunde liegenden Grafik-API zu interagieren. Das Render-Skript nimmt im Lebenszyklus deines Spiels einen besonderen Platz ein. Einzelheiten findest du in der Dokumentation zum Anwendungslebenszyklus.
Im Ordner „Builtins“ deiner Projekte findest du die standardmäßige Render-Ressource (“default.render”) und das standardmäßige Render-Skript (“default.render_script”).

So richtest du einen benutzerdefinierten Renderer ein:
Kopiere die Dateien “default.render” und “default.render_script” an eine Stelle in deiner Projekthierarchie. Du kannst natürlich ein Render-Skript von Grund auf neu erstellen. Es empfiehlt sich aber, mit einer Kopie des Standardskripts zu beginnen, besonders wenn du mit Defold und/oder Grafikprogrammierung noch nicht vertraut bist.
Bearbeite deine Kopie der Datei “default.render” und ändere die Eigenschaft Script so, dass sie auf deine Kopie des Render-Skripts verweist.
Ändere die Eigenschaft Render (unter bootstrap) in der Einstellungsdatei game.project so, dass sie auf deine Kopie der Datei “default.render” verweist.
Um die Zeichenreihenfolge von Objekten steuern zu können, erstellst du Render-_Prädikate_. Ein Prädikat legt anhand einer Auswahl von Material-_Tags_ fest, was gezeichnet werden soll.
Jedem Objekt, das auf den Bildschirm gezeichnet wird, ist ein Material zugewiesen. Es steuert, wie das Objekt auf den Bildschirm gezeichnet werden soll. Im Material gibst du ein oder mehrere Tags an, die mit dem Material verknüpft werden sollen.
In deinem Render-Skript kannst du dann ein Render-Prädikat erstellen und angeben, welche Tags zu diesem Prädikat gehören sollen. Wenn du die Engine anweist, das Prädikat zu zeichnen, wird jedes Objekt gezeichnet, dessen Material alle für das Prädikat angegebenen Tags enthält.
Sprite 1 Sprite 2 Sprite 3 Sprite 4
Material A Material A Material B Material C
outlined outlined greyscale outlined
tree tree tree house
-- a predicate matching all sprites with tag "tree"
local trees = render.predicate({"tree"})
-- will draw Sprite 1, 2 and 3
render.draw(trees)
-- a predicate matching all sprites with tag "outlined"
local outlined = render.predicate({"outlined"})
-- will draw Sprite 1, 2 and 4
render.draw(outlined)
-- a predicate matching all sprites with tags "outlined" AND "tree"
local outlined_trees = render.predicate({"outlined", "tree"})
-- will draw Sprite 1 and 2
render.draw(outlined_trees)
Eine ausführliche Beschreibung der Funktionsweise von Materialien findest du in der Materialdokumentation.
Das standardmäßige Render-Skript ist so konfiguriert, dass es eine für 2D-Spiele geeignete orthografische Projektion verwendet. Es bietet drei verschiedene orthografische Projektionen: Stretch (Standard), Fixed Fit und Fixed. Als Alternative zu den orthografischen Projektionen im standardmäßigen Render-Skript kannst du auch die Projektionsmatrix verwenden, die eine Kamerakomponente (camera component) bereitstellt.
Die gestreckte Projektion zeichnet immer einen Bereich deines Spiels, der den in game.project festgelegten Abmessungen entspricht, auch wenn die Fenstergröße geändert wird. Ändert sich das Seitenverhältnis, werden die Spielinhalte entweder vertikal oder horizontal gestreckt:

Gestreckte Projektion mit ursprünglicher Fenstergröße

Gestreckte Projektion mit horizontal gestrecktem Fenster
Die gestreckte Projektion ist die Standardprojektion. Wenn du zu einer anderen Projektion gewechselt hast und wieder zurückwechseln möchtest, sendest du dazu eine Nachricht an das Render-Skript:
msg.post("@render:", "use_stretch_projection", { near = -1, far = 1 })
Wie die gestreckte Projektion zeigt die fest eingepasste Projektion immer einen Spielbereich, der den in game.project festgelegten Abmessungen entspricht. Wenn jedoch die Fenstergröße und damit das Seitenverhältnis geändert werden, behalten die Spielinhalte ihr ursprüngliches Seitenverhältnis bei, und vertikal oder horizontal werden zusätzliche Spielinhalte angezeigt:

Fest eingepasste Projektion mit ursprünglicher Fenstergröße

Fest eingepasste Projektion mit horizontal gestrecktem Fenster

Fest eingepasste Projektion mit einem auf 50 % der ursprünglichen Größe verkleinerten Fenster
Du aktivierst die fest eingepasste Projektion, indem du eine Nachricht an das Render-Skript sendest:
msg.post("@render:", "use_fixed_fit_projection", { near = -1, far = 1 })
Die feste Projektion behält das ursprüngliche Seitenverhältnis bei und rendert deine Spielinhalte mit einer festen Zoomstufe. Wenn die Zoomstufe auf einen anderen Wert als 100 % eingestellt ist, zeigt sie also mehr oder weniger als den Spielbereich, der durch die Abmessungen in game.project definiert ist:

Feste Projektion mit Zoomstufe 2

Feste Projektion mit Zoomstufe 0,5

Feste Projektion mit Zoomstufe 2 und einem auf 50 % der ursprünglichen Größe verkleinerten Fenster
Du aktivierst die feste Projektion, indem du eine Nachricht an das Render-Skript sendest:
msg.post("@render:", "use_fixed_projection", { near = -1, far = 1, zoom = 2 })
Wenn du das standardmäßige Render-Skript verwendest und im Projekt aktivierte Kamerakomponenten vorhanden sind, haben diese Vorrang vor allen anderen im Render-Skript eingestellten Ansichten und Projektionen. Mehr zur Arbeit mit Kamerakomponenten in Render-Skripten findest du in der Kameradokumentation.
Orthografische Kameras unterstützen einen Orthographic Mode, der steuert, wie sich die Kamera an das Fenster anpasst:
Fixed verwendet den Wert Orthographic Zoom der Kamera.Auto Fit (einpassen) hält den gesamten Entwurfsbereich sichtbar.Auto Cover (ausfüllen) füllt das Fenster aus und kann Inhalte beschneiden.Du kannst die Modi im Editor oder zur Laufzeit über die Kamera-API wechseln:
-- Use auto-fit behavior with an orthographic camera
camera.set_orthographic_mode("main:/go#camera", camera.ORTHO_MODE_AUTO_FIT)
-- Query current mode
local mode = camera.get_orthographic_mode("main:/go#camera")
Mit der Render-API in Defold kannst du Geometrie außerhalb des Sichtvolumens aussondern. Wenn dieses Aussondern aktiviert ist, wird jede Grafik ignoriert, die außerhalb eines festgelegten Begrenzungsquaders oder Sichtvolumens liegt. In einer großen Spielwelt, von der jeweils nur ein Teil sichtbar ist, kann das Aussondern von Geometrie außerhalb des Sichtvolumens die Datenmenge, die zum Rendern an die GPU gesendet werden muss, erheblich verringern. Dadurch steigt die Leistung und auf Mobilgeräten wird der Akku geschont. Üblicherweise werden die Ansicht und die Projektion der Kamera verwendet, um den Begrenzungsquader zu erstellen. Das standardmäßige Render-Skript verwendet die Ansicht und die Projektion (von der Kamera), um ein Sichtvolumen zu berechnen.
Aktiviere das Aussondern von Geometrie außerhalb des Sichtvolumens für einen Zeichenaufruf (draw call), indem du eine kombinierte Ansichts- und Projektionsmatrix in der Option frustum an render.draw() übergibst:
local frustum = self.proj * self.view
render.draw(predicates.particle, { frustum = frustum })
Beim Rendern mit einer Kamerakomponente kann render.set_camera() die kombinierte Ansichts- und Projektionsmatrix der Kamera automatisch für nachfolgende Zeichenaufrufe verwenden:
render.set_camera("main:/go#camera", { use_frustum = true })
render.draw(predicates.particle)
render.set_camera()
Bei beiden Methoden werden Partikeleffekt-Emitter anhand ihrer Begrenzungen ausgesondert.
Das Aussondern von Geometrie außerhalb des Sichtvolumens ist in der Engine für jeden Komponententyp einzeln implementiert. Aktueller Stand:
| Komponente | Unterstützt |
|---|---|
| Sprite | JA |
| Modell | JA |
| Mesh | JA (1) |
| Beschriftung | JA |
| Spine | JA |
| Partikeleffekt | JA |
| Kachelkarte (tile map) | JA |
| Rive | NEIN |
1 = Der Begrenzungsquader des Meshes muss von dir festgelegt werden. Mehr erfahren.
Ab Defold 1.13.0 verwenden die Primitive von Komponenten (components) eine Vertex-Reihenfolge gegen den Uhrzeigersinn, wobei die Normale des Primitivs zur Kamera zeigt. Sprites, GUI-Knoten, Kachelkarten (Kachelraster) und Partikeleffekte verwenden dieselbe Reihenfolge wie andere Komponententypen. Deshalb können für alle Komponenten dieselben Einstellungen zum Aussondern von Flächen verwendet werden.
Dies kann Projekte betreffen, die das Aussondern von Flächen für andere Komponenten als Modelle einstellen. Wenn eine Komponente unerwartet ausgesondert wird, stelle sicher, dass mit render.set_cull_face(graphics.FACE_TYPE_BACK) die Rückseiten ausgewählt sind, oder entferne den Aufruf von render.set_cull_face(), um den Standardmodus graphics.FACE_TYPE_BACK zu verwenden.
Beim Rendern von Komponenten ist üblicherweise auch davon die Rede, in welchem Koordinatensystem sie gerendert werden. In den meisten Spielen werden einige Komponenten im Weltkoordinatensystem und andere im Bildschirmkoordinatensystem gezeichnet.
GUI-Komponenten und ihre Knoten werden normalerweise im Bildschirmkoordinatensystem gezeichnet. Die untere linke Ecke des Bildschirms hat dabei die Koordinate (0,0), die obere rechte Ecke die Koordinate (Bildschirmbreite, Bildschirmhöhe). Das Bildschirmkoordinatensystem wird niemals durch eine Kamera versetzt oder auf andere Weise verschoben. Dadurch werden die GUI-Knoten unabhängig davon, wie die Welt gerendert wird, immer auf dem Bildschirm gezeichnet.
Sprites, Kachelkarten und andere Komponenten von Spielobjekten (game objects) in deiner Spielwelt werden normalerweise im Weltkoordinatensystem gezeichnet. Wenn du dein Render-Skript nicht änderst und keine Kamerakomponente verwendest, um die Ansicht und Projektion zu ändern, entspricht dieses Koordinatensystem dem Bildschirmkoordinatensystem. Sobald du jedoch eine Kamera hinzufügst und sie bewegst oder die Ansicht und Projektion änderst, weichen die beiden Koordinatensysteme voneinander ab. Wenn sich die Kamera bewegt, wird die untere linke Ecke des Bildschirms gegenüber (0, 0) versetzt, sodass andere Teile der Welt gerendert werden. Wenn sich die Projektion ändert, werden die Koordinaten sowohl verschoben (also gegenüber 0, 0 versetzt) als auch durch einen Skalierungsfaktor verändert.
Im Folgenden siehst du den Code für ein benutzerdefiniertes Render-Skript, das eine leicht abgewandelte Version des integrierten Skripts ist.
init() richtet die Prädikate, die Ansicht und die Löschfarbe ein. Diese Variablen werden beim eigentlichen Rendern verwendet.function init(self)
-- Define the render predicates. Each predicate is drawn by itself and
-- that allows us to change the state of OpenGL between the draws.
self.predicates = create_predicates("tile", "gui", "text", "particle", "model")
-- Create and fill data tables will be used in update()
local state = create_state()
self.state = state
local camera_world = create_camera(state, "camera_world", true)
init_camera(camera_world, get_stretch_projection)
local camera_gui = create_camera(state, "camera_gui")
init_camera(camera_gui, get_gui_projection)
update_state(state)
end
update() wird einmal pro Frame aufgerufen. Ihre Aufgabe ist es, das eigentliche Zeichnen durch Aufrufe der zugrunde liegenden OpenGL-ES-APIs (OpenGL Embedded Systems API) auszuführen. Um genau zu verstehen, was in der Funktion update() passiert, musst du die Funktionsweise von OpenGL verstehen. Es gibt viele hilfreiche Informationsquellen zu OpenGL ES. Die offizielle Website ist ein guter Ausgangspunkt. Du findest sie unter https://www.khronos.org/opengles/
Dieses Beispiel enthält die zum Zeichnen von 3D-Modellen notwendige Einrichtung. Die Funktion init() hat ein Prädikat self.predicates.model definiert. An anderer Stelle wurde ein Material mit dem Tag “model” erstellt. Außerdem gibt es einige Modellkomponenten, die dieses Material verwenden:
function update(self)
local state = self.state
if not state.valid then
if not update_state(state) then
return
end
end
local predicates = self.predicates
-- clear screen buffers
--
render.set_depth_mask(true)
render.set_stencil_mask(0xff)
render.clear(state.clear_buffers)
local camera_world = state.cameras.camera_world
render.set_viewport(0, 0, state.window_width, state.window_height)
render.set_view(camera_world.view)
render.set_projection(camera_world.proj)
-- render models
--
render.set_blend_func(graphics.BLEND_FACTOR_SRC_ALPHA, graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA)
render.enable_state(graphics.STATE_CULL_FACE)
render.enable_state(graphics.STATE_DEPTH_TEST)
render.set_depth_mask(true)
render.draw(predicates.model_pred)
render.set_depth_mask(false)
render.disable_state(graphics.STATE_DEPTH_TEST)
render.disable_state(graphics.STATE_CULL_FACE)
-- render world (sprites, tilemaps, particles etc)
--
render.set_blend_func(graphics.BLEND_FACTOR_SRC_ALPHA, graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA)
render.enable_state(graphics.STATE_DEPTH_TEST)
render.enable_state(graphics.STATE_STENCIL_TEST)
render.enable_state(graphics.STATE_BLEND)
render.draw(predicates.tile)
render.draw(predicates.particle)
render.disable_state(graphics.STATE_STENCIL_TEST)
render.disable_state(graphics.STATE_DEPTH_TEST)
-- debug
render.draw_debug3d()
-- render GUI
--
local camera_gui = state.cameras.camera_gui
render.set_view(camera_gui.view)
render.set_projection(camera_gui.proj)
render.enable_state(graphics.STATE_STENCIL_TEST)
render.draw(predicates.gui, camera_gui.frustum)
render.draw(predicates.text, camera_gui.frustum)
render.disable_state(graphics.STATE_STENCIL_TEST)
end
Bisher ist dies ein einfaches und geradliniges Render-Skript. Es zeichnet in jedem Frame auf dieselbe Weise. Manchmal ist es jedoch wünschenswert, einen Zustand im Render-Skript zu speichern und abhängig davon unterschiedliche Operationen auszuführen. Es kann auch wünschenswert sein, aus anderen Teilen des Spielcodes mit dem Render-Skript zu kommunizieren.
on_message() definieren und Nachrichten aus anderen Teilen deines Spiels oder deiner Anwendung empfangen. Ein häufiger Fall, in dem eine externe Komponente Informationen an das Render-Skript sendet, ist die Kamera. Eine Kamerakomponente, die den Kamerafokus erhalten hat, sendet in jedem Frame automatisch ihre Ansicht und Projektion an das Render-Skript. Diese Nachricht heißt "set_view_projection":local MSG_CLEAR_COLOR = hash("clear_color")
local MSG_WINDOW_RESIZED = hash("window_resized")
local MSG_SET_VIEW_PROJ = hash("set_view_projection")
function on_message(self, message_id, message)
if message_id == MSG_CLEAR_COLOR then
-- Someone sent us a new clear color to be used.
update_clear_color(state, message.color)
elseif message_id == MSG_SET_VIEW_PROJ then
-- The camera component that has camera focus will sent set_view_projection
-- messages to the @render socket. We can use the camera information to
-- set view (and possibly projection) of the rendering.
camera.view = message.view
self.camera_projection = message.projection or vmath.matrix4()
update_camera(camera, state)
end
end
Jedes Skript oder GUI-Skript kann jedoch über den speziellen Socket @render Nachrichten an das Render-Skript senden:
-- Change the clear color.
msg.post("@render:", "clear_color", { color = vmath.vector4(0.3, 0.4, 0.5, 0) })
Um bestimmte Engine-Ressourcen an das Render-Skript zu übergeben, kannst du sie der Tabelle Render Resources in der dem Projekt zugewiesenen .render-Datei hinzufügen:

So verwendest du diese Ressourcen in einem Render-Skript:
-- "my_material" will now be used for all draw calls associated with the predicate
render.enable_material("my_material")
-- anything drawn by the predicate will end up in "my_render_target"
render.set_render_target("my_render_target")
render.draw(self.my_full_screen_predicate)
render.set_render_target(render.RENDER_TARGET_DEFAULT)
render.disable_material()
-- bind the render target result texture to whatever is getting rendered via the predicate
render.enable_texture(0, "my_render_target", graphics.BUFFER_TYPE_COLOR0_BIT)
render.draw(self.my_tile_predicate)
Defold unterstützt derzeit nur Materials und Render Targets als referenzierte Render-Ressourcen. Im Laufe der Zeit wird dieses System jedoch weitere Ressourcentypen unterstützen.
Renderziele (render targets) unterstützen Multisample-Kantenglättung (MSAA). Sie glättet Geometriekanten in einem Renderdurchlauf außerhalb der Bildschirmausgabe. Die Anzahl der Samples des Renderziels ist unabhängig von Display ▸ Samples, das die Kantenglättung für das Fenster steuert.
Stelle für eine .render_target-Ressource im Editor Sample Count auf 1, 2, 4, 8 oder 16 ein. Ein Wert von 1 deaktiviert Multisampling. Füge die Ressource in deiner .render-Datei zur Tabelle Render Resources hinzu und verwende ihren zugewiesenen Namen mit render.set_render_target(), wie im obigen Beispiel.
Alternativ kannst du in der Funktion init() deines Render-Skripts ein Renderziel erstellen. Füge sample_count neben den angehängten Puffern in die äußere Parametertabelle ein:
self.offscreen = render.render_target({
sample_count = 4,
[graphics.BUFFER_TYPE_COLOR0_BIT] = {
format = graphics.TEXTURE_FORMAT_RGBA,
width = 1024,
height = 1024,
min_filter = graphics.TEXTURE_FILTER_LINEAR,
mag_filter = graphics.TEXTURE_FILTER_LINEAR,
u_wrap = graphics.TEXTURE_WRAP_CLAMP_TO_EDGE,
v_wrap = graphics.TEXTURE_WRAP_CLAMP_TO_EDGE,
},
})
self.scene_predicate = render.predicate({"scene"})
self.present_predicate = render.predicate({"present"})
Dieses Beispiel verwendet ein Renderziel, das nur Farbpuffer enthält. Alle angehängten Farb-, Tiefen- und Stencil-Puffer eines Renderziels teilen sich dessen Sample-Anzahl. Füge einen Tiefenpuffer hinzu und aktiviere den üblichen Tiefentest-Zustand, wenn der Durchlauf einen Tiefentest erfordert.
Gib für den folgenden Ausschnitt aus update() den Materialien der Szene das Tag scene und dem Material eines bildschirmfüllenden Vierecks das Tag present. Das Material des Vierecks muss Textureinheit 0 abtasten. Lege für jeden Durchlauf die passende Ansicht und Projektion fest:
render.set_render_target(self.offscreen)
render.set_viewport(0, 0, 1024, 1024)
render.clear({[graphics.BUFFER_TYPE_COLOR0_BIT] = vmath.vector4(0, 0, 0, 1)})
-- Set the scene view and projection here.
render.draw(self.scene_predicate)
render.set_render_target(render.RENDER_TARGET_DEFAULT)
render.set_viewport(0, 0, render.get_window_width(), render.get_window_height())
-- Set the full-screen quad view and projection here.
render.enable_texture(0, self.offscreen, graphics.BUFFER_TYPE_COLOR0_BIT)
render.draw(self.present_predicate)
render.disable_texture(0)
Der Wechsel zu einem anderen Renderziel beendet den Durchlauf und führt die Multisamples seiner angehängten Farbpuffer automatisch zusammen. render.enable_texture() bindet die daraus entstandene Farbtextur, sodass das Viereck einen gewöhnlichen Textur-Sampler verwendet. Ein separater Befehl zum Zusammenführen ist nicht erforderlich.
Die angeforderte Sample-Anzahl ist standardmäßig 1 und muss eine positive ganze Zahl sein. Grafik-Backends reduzieren nicht unterstützte Anforderungen auf eine unterstützte Anzahl, die eine Zweierpotenz ist. Falls nötig, fallen sie auf 1 zurück und protokollieren eine Warnung, wenn sich die Anzahl ändert. Höhere Sample-Anzahlen erhöhen den Speicherbedarf der angehängten Puffer.
Wenn du eine Renderziel-Ressource verwendest, prüfe ihre tatsächliche Sample-Anzahl aus einem Spielobjekt-Skript .script mit resource.get_render_target_info(). Zum Beispiel, nachdem du /render/offscreen.render_target zu Render Resources hinzugefügt hast:
function init(self)
local info = resource.get_render_target_info("/render/offscreen.render_targetc")
print("Render target sample count:", info.sample_count)
end
Verwende diese tatsächliche Anzahl, wenn du die Unterstützung auf einem Gerät prüfst, anstatt anzunehmen, dass die angeforderte Anzahl verfügbar war. Die vollständigen Parameter- und Ergebnistabellen findest du unter render.render_target() und resource.get_render_target_info().
Texturen werden in Defold intern durch ein Handle dargestellt. Dies entspricht im Wesentlichen einer Zahl, die ein Texturobjekt überall in der Engine eindeutig identifizieren soll. Du kannst also die Spielobjektwelt mit der Rendering-Welt verbinden, indem du diese Handles zwischen dem Render-System und einem Spielobjekt-Skript übergibst. Beispielsweise kann ein Skript, das einem Spielobjekt zugewiesen ist, eine dynamische Textur erstellen und sie an den Renderer senden, damit sie als globale Textur in einem Zeichenbefehl verwendet wird.
In einer .script-Datei:
local my_texture_resource = resource.create_texture("/my_texture.texture", tparams)
-- note: my_texture_resource is a hash to the resource path, which can't be used as a handle!
local my_texture_handle = resource.get_texture_info(my_texture_resource)
-- my_texture_handle contains information about the texture, such as width, height and so on
-- it does also contain the handle, which is what we are after
msg.post("@render:", "set_texture", { handle = my_texture_handle.handle })
In einer .render_script-Datei:
function on_message(self, message_id, message)
if message_id == hash("set_texture") then
self.my_texture = message.handle
end
end
function update(self)
-- bind the custom texture to the draw state
render.enable_texture(0, self.my_texture)
-- do drawing..
end
Derzeit lässt sich nicht ändern, auf welche Textur eine Ressource verweist. Solche direkten Handles kannst du nur im Render-Skript verwenden.
Die Render-Skript-API von Defold übersetzt Renderoperationen in die folgenden Grafik-APIs:
| System | Grafik-API | Hinweis |
|---|---|---|
| macOS | OpenGL 3.3 oder Metal | Vulkan über MoltenVK |
| Windows | OpenGL 3.3 oder Vulkan 1.1 | |
| Linux x86-64 | OpenGL 3.3 oder Vulkan 1.1 | |
| Linux ARM64 | OpenGL ES oder Vulkan 1.1 | EGL/GLES ist der Standard |
| Android | OpenGLES 3.0 oder Vulkan 1.1 | Rückgriff auf OpenGLES 2.0 |
| iOS | OpenGLES 3.0 oder Metal | Vulkan über MoltenVK |
| HTML5 | WebGL 2.0 oder WebGPU | Rückgriff auf WebGL 1.0 |
"set_view_projection""window_resized"local MSG_WINDOW_RESIZED = hash("window_resized")
function on_message(self, message_id, message)
if message_id == MSG_WINDOW_RESIZED then
-- The window was resized. message.width and message.height contain the new dimensions.
...
end
end
"draw_line"ray_casts, Vektoren und mehr zu visualisieren. Die Linien werden mit dem Aufruf render.draw_debug3d() gezeichnet.-- draw a white line
local p1 = vmath.vector3(0, 0, 0)
local p2 = vmath.vector3(1000, 1000, 0)
local col = vmath.vector4(1, 1, 1, 1)
msg.post("@render:", "draw_line", { start_point = p1, end_point = p2, color = col } )
"draw_text"always_on_top.font gezeichnet. Die Systemschrift hat ein Material mit dem Tag debug_text und wird im standardmäßigen Render-Skript zusammen mit anderem Text gerendert.-- draw a text message
local pos = vmath.vector3(500, 500, 0)
msg.post("@render:", "draw_text", { text = "Hello world!", position = pos })
Der visuelle Profiler, den du über die Nachricht "toggle_profile" an den Socket @system aufrufen kannst, ist nicht Teil des per Skript steuerbaren Renderers. Er wird getrennt von deinem Render-Skript gezeichnet.
Ein Zeichenaufruf bezeichnet den Vorgang, die GPU so einzurichten, dass sie ein Objekt mit einer Textur und einem Material sowie optionalen zusätzlichen Einstellungen auf den Bildschirm zeichnet. Dieser Vorgang ist normalerweise ressourcenintensiv, daher wird empfohlen, die Anzahl der Zeichenaufrufe möglichst gering zu halten. Mit dem integrierten Profiler kannst du die Anzahl der Zeichenaufrufe und die zum Rendern benötigte Zeit messen.
Defold versucht, Renderoperationen nach den unten beschriebenen Regeln zu bündeln (Batching), um die Anzahl der Zeichenaufrufe zu verringern. Die Regeln unterscheiden sich zwischen GUI-Komponenten und allen anderen Komponententypen.
Jeder Aufruf von render.draw() steuert, wie passende Einträge mit Sortierung im Weltkoordinatensystem geordnet werden. Der Standard ist render.SORT_BACK_TO_FRONT; verwende render.SORT_FRONT_TO_BACK zum Rendern von nah nach fern oder render.SORT_NONE, um die Einfügereihenfolge beizubehalten:
render.draw(self.opaque_predicate, {
sort_order = render.SORT_FRONT_TO_BACK
})
render.draw(self.transparent_predicate, {
sort_order = render.SORT_BACK_TO_FRONT
})
Die gewählte Reihenfolge bestimmt, welche Einträge nebeneinander liegen, und kann dadurch die Bündelung beeinflussen. In dieser sortierten Liste wird jedes Objekt mit dem vorherigen Objekt zu demselben Zeichenaufruf zusammengefasst, wenn die folgenden Bedingungen erfüllt sind:
Wenn also zwei Sprite-Komponenten im selben Sammlungs-Proxy nach der gewählten Sortierung nebeneinander liegen und dieselbe Textur, dasselbe Material und dieselben Konstanten verwenden, werden sie zu demselben Zeichenaufruf zusammengefasst.
Die Knoten einer GUI-Komponente werden in der Knotenliste von oben nach unten gerendert. Jeder Knoten in der Liste wird mit dem vorherigen Knoten zu demselben Zeichenaufruf zusammengefasst, wenn die folgenden Bedingungen erfüllt sind:
Knoten werden pro Komponente gerendert. Das bedeutet, dass Knoten aus verschiedenen GUI-Komponenten nicht gebündelt werden.
Die Möglichkeit, Knoten in Hierarchien anzuordnen, erleichtert es, sie zu überschaubaren Einheiten zusammenzufassen. Hierarchien können jedoch das gebündelte Rendern unterbrechen, wenn du unterschiedliche Knotentypen mischst. Mit GUI-Ebenen kannst du GUI-Knoten wirkungsvoller bündeln und dabei die Knotenhierarchien beibehalten. Mehr über GUI-Ebenen und ihren Einfluss auf Zeichenaufrufe erfährst du im GUI-Handbuch.