MetalKit: Erste Schritte mit Apples Grafik-Framework
HeimHeim > Nachricht > MetalKit: Erste Schritte mit Apples Grafik-Framework

MetalKit: Erste Schritte mit Apples Grafik-Framework

Aug 20, 2023

MetalKit ist ein High-Level-Framework, das den Umgang mit Metal erleichtert. Hier erfahren Sie, wie Sie mit der Verwendung des 3D-Frameworks von Apple beginnen.

Metal ist Apples 3D-Grafik- und Spiele-Pipeline zum Rendern von 3D-Objekten auf Apple-Geräten. Metal wurde als Ersatz für OpenGL und andere 3D-Frameworks entwickelt und bietet den Vorteil, dass es für Apple-Hardware optimiert ist, um maximale Leistung zu erzielen.

Apple bietet auf Apple-Geräten eine butterweiche 3D-Wiedergabe auf einem Leistungsniveau, das mit anderen 3D-Frameworks nicht möglich ist.

Sie haben wahrscheinlich ein Beispiel für Metal-Rendering auf einem iOS- oder macOS-Gerät gesehen, wenn Sie die Arcade-Spiele-App von Apple abonniert und ausgeführt haben. Die kurze Einführungsanimation in Arcade wird mit Metal gerendert:

Im Jahr 2015 stellte Apple auf der WWDC ein weiteres, übergeordnetes Framework für Metal namens MetalKit vor. Dieses Framework erleichtert die Verwendung von Metal, indem es einige übergeordnete Funktionen bereitstellt, die die Entwicklung von 3D-Apps einfacher machen.

Konkret stellt MetalKit zusätzliche Metal-APIs in den folgenden Bereichen bereit:

Mit MetalKit ist das Laden von Assets und Texturen durch die Verwendung der MTKTextureLoader-Klasse einfacher. Diese Klasse bietet eine einfache Möglichkeit, Assets und Texturen zu laden sowie Texturoptionen festzulegen.

Zu diesen Optionen gehören Mipmap-Nutzung und -Laden, Cache- und Speichermodi, Texturkoordinatentransformation, Würfeltexturnutzung und RGB-Farboptionen.

Eine Mipmap (oder MIP-Karte) ist einfach ein mehrschichtiges Bild, wobei jede Schicht eine zunehmend niedrigere Auflösung als die vorherige Schicht hat. Mipmaps werden verwendet, um die Bildwiedergabe zu beschleunigen und Aliasing-Artefakte wie Moiré-Muster zu entfernen.

Ein Moiré-Muster ist ein störendes Streifen- oder Farbartefaktmuster, das manchmal in Computergrafiken auftritt und aus Linien oder regelmäßigen Pixelmustern wie abwechselnden Pixelgittern besteht:

Die vollständige Dokumentation für MTKTextureLoader ist auf der Entwickler-Website von Apple in der Metal-Framework-Dokumentation unter Documentation/MetalKit/MTKTextureLoader verfügbar.

Model I/O ist Apples Entwickler-Framework zur Verwaltung von 3D- und 2D-Assets. Die Model-I/O-Integration von MetalKit umfasst APIs zum schnellen Laden von Texturen in Metal-Puffer und zur Verwendung von Mesh-Daten mithilfe von Containern.

Derzeit gibt es in MetalKit etwa ein halbes Dutzend Modell-I/O-bezogene Klassen, die sich hauptsächlich mit Netzen befassen. (Zu den Klassen und der objektorientierten Programmierung kommen wir gleich).

Die meisten iOS- und macOS-Apps verwenden Ansichten – Standardklassen, die visuelle Informationen und UI-Elemente auf dem Bildschirm darstellen. Verschiedene Ansichtsunterklassen stellen unterschiedliche Arten von Ansichten bereit.

Beispielsweise ist in iOS ein UIView die Basisklasse der Ansicht, aber UIButton ist eine von UIView abgeleitete Schaltflächenansichtsklasse. Durch die Verwendung objektorientierter Ansichtsklassen in iOS oder macOS können Sie zusätzliche Funktionen erstellen, die auf Standardfunktionen basieren, die bereits von Apple-Klassen definiert wurden.

Dies wird als Objektvererbung bezeichnet. Stellen Sie sich ein Objekt in einer App als ein Codebündel vor, das sowohl Code als auch Daten kapselt, mit denen der Code arbeitet. Durch die Bündelung beider Elemente zu Objekten kann Code problemlos wiederverwendet und für weitere Objekte verwendet werden.

Insbesondere in MetalKit wird eine neue Klasse – MTKView – bereitgestellt, die es Entwicklern ermöglicht, vollwertige Metal-Ansichten in Apps zu erstellen. Durch eine dedizierte Metal-Ansichtsklasse kann die Ansicht von Metal ohne zusätzlichen Code optimal gezeichnet und verwaltet werden.

Die Dokumentation von Apple für MTKView finden Sie auf der Entwickler-Website unter Documentation/MetalKit/MTKView. MTKView erfordert außerdem, dass Sie zunächst ein MTLDevice in einer seiner Eigenschaften festlegen, um ihm mitzuteilen, auf welchem ​​Gerät und auf welchem ​​Bildschirm Metallobjekte gerendert werden sollen.

MTKView stellt auf Anfrage auch einen MTLRenderPassDescriptor bereit, in den Sie Ihre Texturen rendern können. Schauen Sie sich den Abschnitt „Dokumentation/Metal/Render Passes“ auf der Entwicklerseite von Apple an.

Bei der objektorientierten Programmierung (OOP) werden Objekte durch Klassen definiert. Eine Klasse ist eine Definition in einer Quellcodedatei, die definiert, was ein Objekt enthält, und in Swift die tatsächliche Implementierung eines Objekts.

Eine Klasse definiert Methoden (Funktionen), die von anderen Objekten an sie gesendete Nachrichten empfangen können, um eine Funktion auszuführen. Jede Methode enthält Code zum Ausführen einiger Arbeiten.

Eine Klasse definiert auch Eigenschaften oder Variablen, die Daten enthalten können. Normalerweise führen die Methoden einer Klasse einige Arbeiten an den Eigenschaften der Klasse durch. Die meisten, aber nicht alle Methoden können die meisten (aber nicht alle) Eigenschaften lesen, die in der Klasse oder in einer ihrer Superklassen (übergeordneten Klassen) enthalten sind.

All dies wird als Objektkapselung bezeichnet. Objekte kapseln sowohl Daten als auch Methoden, um alles ordentlich und organisiert zu halten. Es ist einfacher, Objekte mit den zugehörigen Daten an einem Ort zu transportieren, zu referenzieren, zu kopieren und zu verwenden, als die Daten separat verfolgen zu müssen.

Vererbung ist eine OOP-Funktion, mit der neue Klassen aus einer anderen Klasse definiert werden können. Das abgeleitete Objekt wird als Unterklasse und die übergeordnete Klasse als Oberklasse bezeichnet.

Lange Objektketten können durch Unterklassenbildung definiert werden. Vererbung ist leistungsstark, da Sie vorhandenen Code nahezu ohne Aufwand wiederverwenden können.

Unterklassen erben fast ohne zusätzlichen Aufwand das gesamte Verhalten und die Eigenschaften ihrer übergeordneten Klassen. Unterklassen können zusätzliche Methoden hinzufügen, die nur ihnen (oder ihren Unterklassen) bekannt sind.

Noch besser: Wenn Sie eine Instanz (eine Kopie) eines Objekts in einem Programm instanziieren (erstellen), instanziiert es automatisch auch eine Kopie aller Objekte seiner Oberklasse.

Mit einer Codezeile können Sie durch die Erstellung einer einzigen Instanz einer Klasse umfangreiche Programmfunktionalitäten erreichen.

Bei der Instanziierung wird einfach ein Objekt erstellt, ihm Speicher im RAM zugewiesen und es einem Programm zur Verfügung gestellt.

All dies wird normalerweise in einer oder, im Fall von Objective-C, zwei Quellcodedateien definiert – normalerweise eine oder zwei Dateien pro Klasse.

In unserer Diskussion oben wird ein MTKView also als Klasse definiert (von Apple) und instanziiert, wenn er im Code erstellt wird (von Ihnen). Das Ergebnis ist ein MTKView-Objekt im Speicher, das zur Verwendung bereit ist. Wenn das MTKView-Objekt nicht mehr benötigt wird, wird seine Zuordnung aufgehoben, wodurch es aus dem Speicher entfernt und zerstört wird.

Bei den meisten Apps handelt es sich um Programme, die Hunderte solcher Objekte erstellen, verwenden, verwalten und zerstören.

Das OOP-Programmierparadigma ist leistungsstark, da es die benötigte Codemenge durch Unterklassenbildung und Wiederverwendung erheblich reduziert und Programme durch die Kapselung von Code und Daten modularer und wiederverwendbarer hält.

Sobald Sie eine Klasse geschrieben haben, um eine bestimmte Arbeit zu erledigen, können Sie die Klasse einfach wiederverwenden oder in einem anderen Programm eine Unterklasse bilden, um schnell eine weitere App zu erstellen.

Wie viele iOS- oder macOS-Standardansichten verfügt auch MTKView über eine Kernanimationsebene. Core Animation ist Apples leistungsstarkes 2D-Animationsframework.

Die meisten Ansichten verfügen über einen CALayer – ein Kernanimationsebenenobjekt, das 2D-Grafiken zeichnen und animieren kann. CALayers können gruppiert und kombiniert werden, um komplexe Animationen zu erstellen.

MTKView verfügt über eine eigene CALayer-Unterklasse namens CAMetalLayer, in die Metal rendern kann. Sie können CAMetalLayer mit anderen CA-Ebenen kombinieren, um kombinierte 2D- und 3D-Animationen zu erstellen.

In den meisten Fällen ist das Zeichnen sowohl bei 2D- als auch bei 3D-CALayern viel schneller und effizienter als das Zeichnen in UIViews. Sie können auch die Deckkraft oder Alpha von CA-Ebenen festlegen, um Teile davon transparent zu machen.

MTKView unterstützt drei Zeichenmodi:

Beim zeitgesteuerten Zeichnen wird die Ansicht in regelmäßigen, intern im Objekt festgelegten Intervallen aktualisiert. Die meisten Spiele verwenden diesen Modus, wenn eine Spielszene mit einer bestimmten Rate gerendert oder gezeichnet wird, die in Bildern pro Sekunde (fps) angegeben wird.

Im zeitgesteuerten Modus können Sie auch die Eigenschaft isPaused festlegen oder löschen, um die Ansichtsanimation zu starten und zu stoppen.

Im Benachrichtigungsmodus erfolgt eine Neuzeichnung, wenn ein Teil der gesamten Ansicht ungültig wird. Dadurch können Sie nur einen Teil der Ansicht oder Ebene neu zeichnen – was weniger Zeit in Anspruch nimmt und die Spielleistung verbessert.

Um eine Neuzeichnung im Benachrichtigungsmodus zu erzwingen, senden Sie dem Ansichtsobjekt einfach eine setNeedsDisplay()-Nachricht, um eine Neuzeichnung zu erzwingen. Dadurch wird die Ansicht gezwungen, alle ihre Unteransichten neu zu zeichnen, indem ihnen jeweils auch eine setNeedsDisplay()-Nachricht gesendet wird.

Beim expliziten Zeichnen zeichnen Sie den Ansichtsinhalt neu, indem Sie dem Ansichtsobjekt direkt eine draw()-Nachricht senden. Davon wird im Allgemeinen abgeraten, es sei denn, Sie verwenden einen benutzerdefinierten Zeichnungsworkflow, der etwas außerhalb der Standardansichts-/Unteransichtshierarchie tut.

Sie können auch nur Teile einer Ansicht neu zeichnen, indem Sie auch deren Unteransichten die setNeedsDisplay()-Nachricht senden und so die Neuzeichnung der Ansicht auf oberster Ebene umgehen. Generell gilt: Je weniger Objekte neu gezeichnet werden, desto besser ist die Leistung.

Im Fall eines MTKView oder einer Unterklasse davon erhalten Sie in Ihrer Zeichenmethode einen MTLRenderPassDescriptor aus der Ansicht, rendern darin und präsentieren dann das resultierende Zeichenobjekt zur Anzeige.

Ein Zeichenelement ist jedes Metallobjekt, das codiert wurde und zur Anzeige bereit ist.

In den Programmiersprachen Swift und Objective-C von Apple ist ein Delegat ein Objekt, das im Namen eines anderen Objekts eine Arbeit ausführt.

Normalerweise deklariert ein Objekt ein Delegatobjekt als eine seiner Eigenschaften, und der Delegat deklariert, welche Methoden (Funktionen) es bereitstellt.

Delegaten sind leistungsstark, da Sie das Verhalten eines Objekts einfach durch Ändern seiner Delegateneigenschaft ändern können. Delegaten werden auch verwendet, um Objekten zusätzliche Funktionalität bereitzustellen, ohne dass ein Objekt in eine Unterklasse umgewandelt werden muss, um ein anderes Objekt zu erstellen.

MTKView verfügt über ein eigenes Delegate-Objekt namens MTKViewDelegate-Klasse, das ebenfalls in der Dokumentation von Apple beschrieben wird. MTKViewDelegate reagiert hauptsächlich auf Ereignisse zum Neuzeichnen und Ändern der Größe von Ansichten.

MTKViewDelegate erbt außerdem von einem standardmäßigen Objective-C-Protokoll namens NSObjectProtocol, das allen Apple-Objekten gemeinsam ist.

Stellen Sie sich Delegierte und Protokolle als zusätzliche Objekte und Methoden vor, die an andere Objekte angehängt oder auf diese „geklebt“ werden können.

In Objective-C und Swift ist ein Protokoll einfach eine Liste zusätzlicher Methoden, die eine Klasse implementieren muss. Der Inhalt jeder Methode in einem Protokoll muss von jeder Klasse definiert werden.

Das MTKViewDelegate befasst sich hauptsächlich mit der Änderung des Layouts einer Ansicht (z. B. bei der Gerätedrehung) und dem Zeichnen.

Sie könnten beispielsweise mehrere MTKViewDelegate-Objekte mit jeweils unterschiedlichem Verhalten definieren und dann das Zeichen- oder Rotationsverhalten Ihres MTKView ändern, indem Sie einfach die Delegate-Eigenschaft auf eines der Delegate-Objekte nach Belieben zurücksetzen und neu zeichnen.

Wenn Sie MTKView verwenden, implementieren Sie die Methoden von MTKViewDelegate in Ihrem Renderer. Dadurch kann Ihr Renderer mit MTKView interagieren und Zeichnungs- und Layoutänderungen vornehmen.

Sie können Informationen darüber erhalten, wann es Zeit ist, jeden Frame zu rendern, indem Sie die Eigenschaft currentRenderPassDescriptor von MTKView verwenden. Dadurch können Sie mit jedem zu rendernden Frame interagieren.

Zum Beispiel in Swift:

wenn let onscreenDescriptor = view.currentRenderPassDescriptor

Dadurch wird der aktuelle Render-Pass-Deskriptor von MTKView abgerufen und in einer Variablen namens onscreenDescriptor gespeichert.

Nach dem Rendern müssen Sie das Drawable verwenden, um den Inhalt der Ansicht zu aktualisieren. Rufen Sie dazu die Methode present(_:) für das MTLCommandBuffer-Objekt auf und senden Sie dann die commit()-Nachricht und den Befehlspuffer zur Anzeige an die GPU.

Eine ausführlichere Beschreibung dieses Prozesses finden Sie in der Dokumentation von MTKView.

Apple verfügt außerdem über ein mathematisches Framework namens SIMD, das sich bei der Bearbeitung von 3D- und 2D-Objekten und der Durchführung von Berechnungen an ihnen und Matrizen als nützlich erweist. Die meisten dieser Funktionen werden verwendet, um schnelle und effiziente Gleitkommaberechnungen durchzuführen, die häufig in 3D-Berechnungen vorkommen.

SIMD kann nützlich sein, wenn Sie 3D-Objekte und Scheitelpunkte auf Objekten transformieren müssen. Die gebräuchlichste und nützlichste Datenstruktur in SIMD ist simd_float4x4, eine Vier-mal-Vier-Matrix aus Gleitkommawerten mit einfacher Genauigkeit.

Ausgestattet mit all diesem Hintergrundwissen sind Sie nun bereit, eine MetalKit-App in Xcode zu erstellen. Im folgenden Beispiel gehen wir davon aus, dass Sie eine einfache 3D-App erstellen, die eine einzelne Szene enthält, die ein einzelnes 3D-Metallobjekt enthält.

Um eine Xcode MetalKit-App zu schreiben, müssen Sie mit den Programmiersprachen Swift und Objective-C von Apple sowie ein wenig mit ANSI-C vertraut sein – einer früheren reinen C-Sprache, die 1972 bei der Entwicklung von UNIX in den Bell Labs erfunden wurde.

Um zu beginnen, öffnen Sie Xcode und wählen Sie ausDatei->Neues Projekt aus dem Menü Datei. Wählen Sie in der Projektvorlagenauswahl die Option ausiOSoderMac OSoben, dann wählen SieSpielWählen Sie aus den Symbolen unten aus und klicken Sie aufNächste:

Geben Sie im nächsten Bereich einen App-Namen, eine Bundle-ID und Organisationsinformationen ein und wählen Sie ausSchnellUndMetallaus den beiden unteren Popup-Menüs:

KlickenNächsteund speichern Sie Ihr neues Xcode-Projekt auf der Festplatte.

Sie müssen außerdem ein Texturbild für Ihr 3D-Objekt als PNG-Datei definieren und es Ihrem Xcode-Projekt hinzufügen. Diese Texturdatei wird beim Rendern auf Ihr 3D-Objekt „umhüllt“.

Die Metal-Spielvorlagen-App von Xcode stellt die minimalen Standard-Vorlagenquelldateien bereit, die Sie für Ihre App benötigen. Zuerst müssen Sie jedoch die Metal-Frameworks hinzufügen, um Xcode anzuweisen, diese Frameworks zur Laufzeit mit Ihrer App zu verknüpfen.

Wählen Sie dazu im Xcode-Projekteditor den Namen Ihres Projekts aus, indem Sie auf das Projektsymbol in der oberen linken Ecke des Projektfensters klicken, und wählen Sie dann rechts davon im Abschnitt „Ziele“ den Zielnamen aus:

Scrollen Sie zum unteren Rand des Fensters und klicken Sie im Abschnitt „Frameworks, Bibliotheken und eingebettete Inhalte“ auf„+“ Taste. Der Rahmenauswahlbereich wird angezeigt.

Geben Sie „Metal“ in das Suchfeld oben ein und klicken Sie bei gedrückter Befehlstaste auf sechs der sieben aufgelisteten Frameworks, mit Ausnahme von „MetalFX.framework“. Es sind Hunderte von Xcode-Frameworks verfügbar.

Sie möchten außerdem die Bibliothek libswiftsimd.tbd, Core Services-Frameworks und optional das Accelerate-Framework hinzufügen.

„tbd“ ist ein Platzhalter für „Noch festzulegen“, da sich die Versionsnummern der tatsächlichen Codebibliotheken ändern können. Durch das Einbinden einer .tbd-Bibliothek in Xcode wird Xcode angewiesen, die neueste Version dieser Bibliothek zu verwenden.

Wenn Sie Model I/O zum Verwalten von Assets verwenden möchten, fügen Sie außerdem „libswiftModelIO.tbd“ und „ModelIO.framework“ hinzu.

Wenn Sie in der Vorlagenauswahl eine iOS-App erstellt haben, fügen Sie auch UIKit.framework hinzu. Wenn Sie in der Vorlagenauswahl eine macOS-App erstellt haben, fügen Sie auch Cocoa.framework hinzu.

Fügen Sie abschließend Foundation.framework und CoreFoundation.framework hinzu. Foundation ist ein zentrales C-Sprach-Framework, das die meisten iOS- und macOS-Apps verwenden. Alle Foundation-API-Aufrufe erfolgen in einfachem C.

Der vollständige Code für eine Metal-Spiel-App würde den Rahmen dieses Artikels sprengen, daher werden wir hier für unser Ein-Objekt-Beispiel kurz nur die Grundlagen behandeln. Die Beispielprojektvorlage von Apple erstellt einen einzelnen 3D-Würfel, der sich im Raum dreht.

Xcode erstellt eine App-Delegatendatei, die die allgemeine Ereignisschleife der App selbst steuert, und eine ShaderTypes.h-Datei, bei der es sich um eine Header-Datei handelt, die die Netz- und Scheitelpunktinformationen des Shaders definiert, sowie eine C-Struktur, die die Projektionsmatrix und die Modellansichtsmatrix definiert.

Diese werden vom Shader beim Zeichnen verwendet.

Die Datei „Shaders.metal“ importiert die oben definierte Header-Datei „ShaderTypes.h“, die vom Renderer und der Datei GameViewController.swift gemeinsam genutzt wird, die wir gleich erhalten. Sie importieren Header-Dateien mit der Import-Präprozessoranweisung in andere Swift- oder Objective-C-Quellcodedateien:

#import „ShaderTypes.h“

Präprozessoranweisungen sind Compileranweisungen, die vor der eigentlichen Kompilierung ausgeführt werden und normalerweise mit einem „#“-Zeichen beginnen.

„Shaders.metal“ importiert außerdem zwei weitere Dateien, metal_stdlib und simd.h, mithilfe der früheren ANSI-C-Importanweisung #include. Sowohl #import als auch #include sind ähnlich und wir werden hier nicht auf die detaillierten Unterschiede zwischen den beiden eingehen.

Darunter sehen Sie diese Zeile:

Verwendung von Namespace-Metall;

Namespaces sind eine C++-Sprache, die es ermöglicht, ähnliche oder identische Codeabschnitte zu definieren und zu isolieren, indem sie unter einem Namespace definiert werden. Jeder Namespace hat seinen eigenen Namen, in diesem Fall Metal.

In Shaders.metal definieren Sie eine Vertex- und ColorInOut-Struktur sowie mehrere Funktionen, die die Shader definieren – in diesem Fall nur einen Vertex-Shader und einen Fragment-Shader. Der Fragment-Shader enthält außerdem eine Sampler-Variable, mit der Sie bei Bedarf Mipmaps definieren und verwenden können.

Die fragmentShader-Funktion verwendet als Argumente Farbinformationen, eine in SharderTypes.h definierte Uniforms-Struktur und eine Texture2d, wie im Metal-Bibliotheksheader „metal_texture“ definiert.

Der Uniforms-Parameter enthält, wie bereits erläutert, die Projektionsmatrix und die Modellansichtsmatrix.

Die nächste Datei, Renderer.swift, definiert die Renderer-Klasse des Objekts, die von der Basis-Objective-C-Klasse NSObject erbt und dem MTKViewDelegate-Protokoll entspricht.

Als kleine historische Anmerkung erinnert NSObject an die Tage von NeXT Computer – Steve Jobs zweites Unternehmen, nachdem er 1985 von Apple entlassen wurde. NeXT erfand Objective-C und hatte ein Betriebssystem und Framework namens NeXTStep. Das „NS“ in NSObject steht für „NeXTStep“.

Die meisten frühen NeXTStep-Objekte hatten das Präfix „NS“, um sie von Objekten Dritter zu unterscheiden. Als Apple 1997 NeXT Computer Inc. kaufte, erwarb das Unternehmen die gesamte NeXT-Technologie, einschließlich NeXTStep.

Bis heute basieren macOS und iOS auf NeXTStep.

Zu den Eigenschaften der Renderer-Klasse gehören MTLDevice, MTLCommandQueue, MTLBuffer, MTLRenderPipelineState, MTLDepthStencilState und MTLTexture sowie Eigenschaften für Matrizen, Rotation, Netz und ein Semaphor.

Ein Semaphor ist ein Thread (Ausführungspfad), der sich auf ein Flag verlässt, um ihm mitzuteilen, wann er ausgeführt werden kann und wann nicht.

Wenn Sie ein Render-Objekt instanziieren, übergeben Sie ihm in seiner Init-Methode ein MTKView, worauf wir gleich noch eingehen werden.

Sobald das Objekt erstellt wird, wird seine Init-Methode ausgeführt, und der gesamte Code in dieser Methode wird ausgeführt.

Die Init-Methode richtet alle ihre Eigenschaften oben in der Methode ein und weist sie zu und erstellt dann über die Zeile self.device.makeBuffer() einen Renderpuffer.

Anschließend werden einige Eigenschaften für das an die Init-Methode übergebene MetalKitView festgelegt, über Renderer.buildMetalVertexDescriptor() ein Vertexdeskriptor erstellt und anschließend über Renderer.buildRenderPipelineWithDevice() die Renderpipeline erstellt.

Als Nächstes erstellt der Code Tiefen- und Schabloneninformationen und erstellt dann über Renderer.buildMesh ein Netz.

Schließlich werden über Renderer.loadTexture() eine Farbkarte und eine Textur erstellt.

Sie müssen die Texturlademethode des Renderers „loadTexture:device:textureName:“ verwenden, um Ihre Textur aus der oben erstellten PNG-Datei zu laden. Dabei übergeben Sie der Methode den Dateinamen Ihrer Textur – in diesem Beispiel „ColorMap“.

Das Swift-Konstrukt do/catch dient der Fehlerbehandlung. Der in do enthaltene Code wird versucht, und wenn er fehlschlägt, wird der Catch-Block ausgeführt, andernfalls wird die Programmausführung normal fortgesetzt.

Schließlich wird am Ende der Init-Methode des Renderers die Init-Methode der Oberklasse ausgeführt:

super.init()

Durch das Senden der super.init()-Nachricht an die Superklasse am Ende der Init-Methode einer Swift-Klasse wird sichergestellt, dass die gesamte Objektkette in der Klassenhierarchie erstellt wird. Dies ist ein Standardmuster in Swift und Objective-C.

Wenn Sie die Init-Methode einer Superklasse nicht aufrufen, ist es sehr wahrscheinlich, dass das Objekt abstürzt oder bestenfalls nicht richtig funktioniert – oder Ihre App selbst stürzt ab.

Da Unterklassen während der Ausführung auf Methoden der Oberklasse angewiesen sind, wird der Methodenaufruf einer Unterklasse möglicherweise in einen zufälligen Speicherbereich gesendet, wenn das Oberklassenobjekt nicht vorhanden ist, wo der erwartete Code nicht vorhanden ist.

Wenn das passiert und der Prozessor versucht, den Code an diesem Speicherort auszuführen, ist ein Absturz sicher – es gibt dort keinen Code zum Ausführen.

super.init() wird in Swift und Objective-C normalerweise zuletzt aufgerufen, um Ihrem Objekt Zeit zu geben, alle erforderlichen Einstellungen vorzunehmen, bevor das Oberklassenobjekt eingerichtet wird.

Schließlich endet die Init-Methode des Renderers mit der schließenden Klammer „}“.

Unmittelbar nach der init()-Methode in Renderer.swift befinden sich die tatsächlichen Implementierungen der anderen Methoden in der Renderer-Klasse. Jeder Swift-Funktion wird die Klasse func vorangestellt, gefolgt vom Funktionsnamen und etwaigen Funktionsparametern in Klammern.

Wenn eine Swift-Methode nach Abschluss einen Wert zurückgibt, wird dieser Rückgabewerttyp durch das ->-Konstrukt definiert. Zum Beispiel:

class func buildMetalVertexDescriptor() -> MTLVertexDescriptor definiert eine Methode (Funktion) namens buildMetalVertexDescriptor, die bei erfolgreichem Abschluss einen MTLVertexDescriptor zurückgibt. Dies wird als Rückgabewert oder Rückgabetyp bezeichnet.

Wie wir zuvor gesehen haben, wird die Methode buildMetalVertexDescriptor intern bei der Objektinstanziierung über die Methode init() aufgerufen. Viele Objekte funktionieren auf diese Weise.

Die meisten Methoden können aber auch von externen Objekten aufgerufen werden, es sei denn, eine Klassendefinition verbietet dies ausdrücklich.

Die Renderer-Spielschleife steuert die meisten Metal-Spiele zusammen mit den Zeichenmethoden von Renderer und MTKView. In Kombination mit der im Anwendungsdelegatenobjekt überwachten Hauptereignisschleife steuert dies die App, während sie auf einem Gerät ausgeführt wird.

In der Datei Render.swift finden Sie eine Methode mit dem Namen private func updateGameState(). Diese Methode kann regelmäßig ausgeführt werden, um jeden im Spiel gespeicherten Status zu aktualisieren, z. B. Objektpositionen, Maus-, Tastatur- oder Gamecontroller-Eingaben, Position, Timing, Partituren usw.

Das Swift-Schlüsselwort private bedeutet, dass diese Methode privat für dieses Objekt ist und nur von diesem Objekt und allen in dieser Quelldatei definierten Erweiterungen aufgerufen werden kann – externe Objekte können diese Nachricht nicht an das Renderer-Objekt senden.

Diese zusätzliche Zugriffskontrolle stellt die korrekte Programmausführung nur innerhalb von bestimmten Objekten sicher. Da in diesem Fall der Renderer für die allgemeine Ausführung und Steuerung des Spiels verantwortlich ist, möchten Sie nicht, dass ein externes Objekt das Spiel stört.

Apple verfügt über einen vollständigen Abschnitt zur Objektzugriffskontrolle in der Swift-Entwicklerdokumentation auf der Swift-Dokumentationswebsite.

Als nächstes sehen wir in Renderer.swift die Methode draw():

Funktion zeichnen (in Ansicht: MTKView)

Sie übergeben die MTKView, in der die Zeichnung ausgeführt werden soll. Beachten Sie, dass diese Funktion keinen Rückgabewert hat. Solche Funktionen werden in Swift und Objective-C als Void-Funktionen bezeichnet.

In der Methode draw(), die einmal pro Frame aufgerufen wird, wird dem Semaphor mitgeteilt, eine bestimmte Zeit zu warten:

inFlightSemaphore.wait()

Anschließend wird der Befehlspuffer erstellt und zum Rendern an das Semaphor gesendet, wobei ein Abschlusshandler hinzugefügt wird.

Ein Abschlusshandler ist eine Funktion, die automatisch ausgeführt wird, wenn andere Aufgaben oder Threads abgeschlossen sind. Vervollständigungshandler sind eine Möglichkeit, Code sequentiell auszuführen, jedoch nur, wenn ein anderer Codeabschnitt abgeschlossen ist.

Completion-Handler sorgen für eine garantierte Codeausführung, ohne dass Code zur Verwaltung komplexer Timer-Algorithmen und Wartebedingungen geschrieben werden muss.

Als nächstes werden die 3D-Objektpuffer und der Spielstatus in dieser Reihenfolge aktualisiert:

self.updateDynamicBufferState()

self.updateGameState()

Als Nächstes wird ein Render-Pass-Deskriptor aus MTKView abgerufen und die Eigenschaften des Render-Pass-Encoders werden aktualisiert:

let renderPassDescriptor = view.currentRenderPassDescriptor

Dann wird eine kurze Schleife ausgeführt, um die Mesh-Scheitelpunkt-Deskriptor-Layouts und Puffer abzurufen und sie im Render-Encoder zu speichern. Anschließend werden die Fragmenttexturinformationen des Render-Encoders festgelegt:

renderEncoder.setFragmentTexture(colorMap, Index: TextureIndex.color.rawValue)

Als nächstes wird für jedes Mesh (Objekt) im .submeshes-Array renderEncoder.drawIndexedPrimitives() aufgerufen. Hier wird jedes Objekt in der Szene codiert.

Um die Kodierungsphase zu beenden, wird renderEncoder.endEncoding() aufgerufen. Die Objekte sind nun alle zum Zeichnen bereit.

Das Drawable der Ansicht wird dann wie folgt abgerufen:

wenn let drawable = view.currentDrawable

und bei Erfolg wird dann der gesamte Befehlspuffer gezeichnet mit:

commandBuffer.commit()

Der Commit-Aufruf sendet tatsächlich den Frame der Szene zur Anzeige auf dem Bildschirm an die GPU.

All dies geschieht mit dreißig, sechzig oder einhundertzwanzig fps.

Die Datei Renderer.swift endet mit einigen allgemeinen mathematischen 3D-Transformationen und Rotationsfunktionen.

Sie werden zwei zusätzliche Dateien im Xcode-Projekt bemerken: GameViewController.swift und Main.storyboard. Dies sind typische Dateien, die in den meisten iOS-Apps zu finden sind.

Eine typische iOS-App enthält eine zentrale UIViewController-Klasse der obersten Ebene, die im UIKIt-Framework definiert ist. Ein UIViewController ist eine Klasse, die eine andere UIKIt-Klasse verwaltet und steuert – UIView.

Eine UIView-Klasse ist eine Klasse, die andere UIView-Unterklassen wie Schaltflächen, Bilder, Text und andere UIKIt-Objekte enthält. Mit UIView wird die Benutzeroberfläche einer iOS-App auf dem Bildschirm dargestellt.

Jede UIViewController-Klasse verfügt über eine Eigenschaft namens view, die eine Instanz von UIView ist. Der View Controller verwaltet die UIView.

Wenn Sie sich die Dokumentation von Apple für UIViewController ansehen, werden Sie ein halbes Dutzend Methoden zum Verwalten der Ansicht bemerken – nämlich Methoden zum Laden der Ansicht, zum Benachrichtigen, wenn die Ansicht geladen wird, und zum Entladen von Ansichten.

In den meisten iOS-Apps laden Sie die Ansicht der obersten Ebene nicht direkt, sondern durch Instanziieren einer von Ihnen definierten UIViewController-Unterklasse (in diesem Beispiel ein GameViewController). Der Benutzeroberflächenteil der Ansicht wird im Interface Builder-Editor von Xcode oder über eine Nur-Text-SwiftUI-Ansicht erstellt.

Normalerweise legen Sie beim Erstellen einer iOS-App das Layout jeder Ansicht im Interface Builder an, indem Sie visuelle Komponenten aus der Xcode-Bibliothek ziehen und sie in einem Ansichts-Controller-Objekt auf dem Bildschirm ablegen.

Sobald Ihre UI-Objekte alle auf dem Bildschirm angeordnet sind, verbinden Sie sie mit den Eigenschaften eines View-Controllers, indem Sie bei gedrückter Strg-Taste klicken und von jedem UI-Element zum ersten Responder des View-Controllers ziehen. Ein Ersthelfer ist das erste Objekt in einer View-Controller-Objekthierarchie, das auf die Nachrichten dieses Objekts antworten kann.

Wenn Sie die Maustaste von Ihrem Strg-Klick loslassen und darüber ziehen, zeigt Xcode eine Liste der verfügbaren Objekteigenschaften an, mit denen das Objekt verbunden werden kann.

Das ist alles – Sie müssen normalerweise nicht für jedes UI-Element programmieren – wenn der View Controller instanziiert und in den Speicher geladen wird, stellt die Swift- oder Objective-C-Laufzeit automatisch alle UI-Verbindungen für Sie her.

Dies vereinfacht die Anwendungsentwicklung erheblich.

Storyboard-Dateien wurden später zu Xcode hinzugefügt, um das UI-Layout noch weiter zu vereinfachen: Mit Storyboards definieren Sie Segues zwischen Ansichtsübergängen – wenn Benutzer zwischen den einzelnen Ansichten auf dem Gerät navigieren, wird die von Ihnen angegebene Segue-Funktion aufgerufen, wo Sie dann alle anfänglichen Ansichtseinstellungen vornehmen können Aufräumen.

Segmente eliminieren den größten Teil des View-Loading-Codes.

In jedem Fall wird die viewDidLoad()-Methode des Controllers aufgerufen, wenn ein View-Controller das Laden einer Ansicht beendet. In viewDidLoad() können Sie alle zusätzlichen Ansichtseinstellungen vornehmen, die Sie benötigen. Sobald viewDidLoad() beendet wird, ist die Ansicht einsatzbereit und wird dem Benutzer auf dem Bildschirm angezeigt.

Sie können sowohl UIViewController als auch UIView in Unterklassen unterteilen, um Ihre Ansichten hochgradig anpassbar zu machen. Beachten Sie, dass die meisten UI-Elemente in iOS als Eigenschaften in einer UIViewController-Unterklasse gespeichert werden.

Es ist möglich, Ansichten und View-Controller vollständig im Code ohne Storyboard oder Interface Builder zu erstellen, dies ist jedoch viel komplexer und zeitaufwändiger.

Werfen wir einen Blick auf GameViewController.swift

Die Klasse wird oben in der Datei definiert:

Klasse GameViewController: UIViewController

Dies bedeutet, dass GameViewController eine Unterklasse von UIViewController ist.

Die Klassendefinition ist in passenden offenen und geschlossenen Klammern („{“ und „}“) enthalten.

Beachten Sie, dass die GameViewController-Klasse sehr kurz ist – etwas mehr als eine Seite. Der Großteil der Spielverarbeitungsarbeit findet in den Shadern und Renderern statt.

Als nächstes sehen wir zwei Swift-Eigenschaften, die durch das Schlüsselwort var definiert werden:

var renderer: Renderer!

var mtkView: MTKView!

Als nächstes sehen wir, dass GameViewController die UIViewController-Methode viewDidLoad() mit dem Swift-Override-Schlüsselwort überschreibt:

Funktion viewDidLoad() überschreiben

Dies bedeutet, dass, wenn der View-Controller die Ansicht lädt und die viewDidLoad()-Nachricht sendet, die GameViewController-Version der Methode anstelle der UIViewController-Version ausgeführt wird. Dies ist ein perfektes Beispiel für Vererbung in Aktion: Sie können wählen, ob Sie die Methode einer Oberklasse ausführen lassen oder sie in einer Unterklasse überschreiben und stattdessen diese Methode verwenden möchten.

Beachten Sie, dass die Deklarationen beider Methoden in beiden Klassen identisch sein müssen, damit dies funktioniert.

Das erste, was die Überschreibungsfunktion viewDidLoad() tut, ist, der Superklasse (UIViewController) eine viewDidLoad()-Nachricht zu senden. Dadurch kann der UIViewController jede erforderliche Initialisierung des UI-Ansichtslayouts durchführen.

Ohne diesen „Super“-Aufruf funktioniert die Ansicht nicht richtig, da einige ihrer internen Teile nie initialisiert werden.

Als nächstes lädt das GameViewController-Objekt das MTKView und speichert es in seiner internen Eigenschaft mtkView:

Guard let mtkView = view as? MTKView sonst

Guard ist einfach ein Swift-Bedingungstest, um zu sehen, ob etwas erfolgreich war – ähnlich wie if.

GameViewController speichert dann auch einen Verweis auf das Metal-Gerät des Geräts in seiner internen Eigenschaft defaultDevice.

Guard let defaultDevice = MTLCreateSystemDefaultDevice() else

Hier ist es wichtig zu verstehen, dass die beiden internen Eigenschaften oder Variablen:

var renderer: Renderer!

var mtkView: MTKView!

Speichern Sie Verweise auf andere Objekte im Speicher – in diesem Fall den Renderer und die Metal-Ansicht. Nach der Speicherung kann das GameViewController-Objekt nach Belieben auf diese Objekte zugreifen. Nach diesem Muster funktionieren die meisten Objekte in Swift und Objective-C.

In Objective-C wären diese beiden Eigenschaften wie folgt deklariert worden:

Renderer *renderer = nil;

MTKView *mtkView = nil;

nil ist ein Objective-C-Platzhalter, der „nichts“ oder genauer gesagt keine Adresse im Speicher bedeutet. Null wird verwendet, um anzugeben, dass eine Objective-C-Eigenschaft oder -Variable nichts enthält.

Das „*“ ist ein Standardindikator für einen C- oder Objective-C-Zeiger – eine Variable, die eine Speicheradresse für ein Objekt anstelle eines Werts enthält. Hinweise sind ein komplexes Thema, daher gehen wir hier nicht näher darauf ein.

Beachten Sie außerdem, dass Objective-C- und C-Codezeilen mit einem „;“ enden müssen. (Semikolon). Dies ist nicht optional – ohne das Semikolon wird der Code nicht kompiliert und Sie erhalten eine Fehlermeldung.

Swift hat Semikolons weggelassen (aber Sie können sie tatsächlich immer noch verwenden, wenn Sie möchten).

Als nächstes speichert der GameViewController weitere Verweise auf andere Objekte, dieses Mal jedoch innerhalb des mtkView-Eigenschaftsobjekts:

mtkView.device = defaultDevice

mtkView.backgroundColor = UIColor.black

Dies bedeutet, dass Sie das Standard-Rendering-Gerät in der Eigenschaft mtkView.device und eine schwarze UIColor in der Eigenschaft tkView.backgroundColor speichern.

UIColor ist ein Standard-UIKit-Objekt zur Farbangabe – in diesem Fall auf Schwarz eingestellt, das als Hintergrundfarbe der Szene verwendet wird. Jedes UIColor-Objekt verfügt über eine .backgroundColor-Eigenschaft.

Beachten Sie, dass Sie hier eigentlich Verweise auf Objekte in Eigenschaften speichern, die selbst Eigenschaften der Eigenschaften dieser Klasse sind. Das ist zunächst verwirrend, aber sobald man den Dreh raus hat, ist es leicht zu verstehen.

Durch die Verkettung von Eigenschaften über Objekte hinweg verketten Sie eigentlich nur Objekte miteinander.

Sie können Eigenschaften haben, die auf Eigenschaften verweisen, die auf andere Objekte verweisen. Es gibt theoretisch keine Begrenzung, wie tief Immobilienverweise gehen können.

Bevor Sie ein Objekt freigeben (zerstören), sollten Sie alle seine Eigenschaften in der deinit()-Methode der Klasse auf Null setzen, um sicherzustellen, dass alle Verweise auf andere Objekte freigegeben werden. Andernfalls kann es zu Speicherverlusten und unerwünschten Aufbewahrungszyklen kommen.

In Objective-C heißt deinit() Dealloc.

Anschließend wird das Renderer-Objekt erstellt, das MTKView-Objekt übergeben und ein Verweis (Zeiger) auf den Renderer in der Renderer-Eigenschaft des View-Controllers gespeichert:

Guard let newRenderer = Renderer(metalKitView: mtkView) else

renderer = newRenderer

Zuerst erstellen Sie das Objekt und speichern dann einen Verweis darauf in einer Eigenschaft.

Anschließend wird dem Zeiger des Renderers auf MTKView die Nachricht „drawableSizeWillChange“ gesendet:

renderer.mtkView(mtkView, drawableSizeWillChange: mtkView.drawableSize)

Dadurch weiß der Renderer, wie groß die aktuelle Zeichengröße der Ansicht ist, sodass er weiß, wie und wo die Szene skaliert werden muss, wenn sie an die GPU gesendet wird. Beachten Sie, dass die Zeichengröße im MTKView bereits in der Eigenschaft .drawableSize gespeichert ist.

Dies zeigt, dass Sie die Eigenschaften eines Objekts als Parameter an Methoden übergeben können.

Schließlich wird der Delegat der Ansicht auf den Renderer selbst festgelegt:

mtkView.delegate = Renderer

Denken Sie daran, dass in der Datei Renderer.swft die Renderer-Klasse als konform zum MTKViewDelegate-Protokoll deklariert ist:

Klassenrenderer: NSObject, MTKViewDelegate

Dadurch kann die Eigenschaft mtkView.delegate auf ein Renderer-Objekt festgelegt werden. Ohne die MTKViewDelegate-Protokollkonformität in der Renderer-Klassendefinition würde die Zeile mtkView.delegate = renderer beim Kompilieren wahrscheinlich eine Warnung oder einen Fehler auslösen, die besagt, dass die Renderer-Eigenschaft nicht dem MTKViewDelegate-Protokoll entspricht.

Beachten Sie außerdem, dass ein kritisches Problem für Xcode-Neulinge darin besteht, dass Sie vor dem Zerstören eines View-Controller-Objekts zunächst dessen .delegate-Eigenschaft auf Null setzen müssen. Andernfalls wird Ihre App garantiert abstürzen.

Dies gilt tatsächlich für jedes Swift- oder Objective-C-Objekt, das Delegaten enthält – nicht nur für View-Controller.

Warum? Denn wenn Sie die in der Delegat-Eigenschaft gespeicherte Referenz nicht zuerst freigeben, hat zwischen dem tatsächlichen Verschwinden des enthaltenden Objekts aus dem Speicher und dem Zeitpunkt, an dem das System feststellt, dass das Objekt zerstört wurde, möglicherweise ein anderes Objekt dem Delegate-Objekt eine weitere Nachricht gesendet.

Da nicht erkannt wird, dass das Objekt, das die Delegateneigenschaft enthielt, nicht mehr existiert, wartet die an den Delegaten gesendete Nachricht möglicherweise immer noch auf die Verarbeitung – und wenn sie verarbeitet wird, ist der Delegat jetzt ungültig, da sein enthaltendes Objekt nicht mehr existiert.

Der Delegate bleibt im Speicher baumeln, aber sein enthaltendes Objekt ist schon lange nicht mehr vorhanden – und das System hat daher keine Möglichkeit, das Delegate-Objekt zu finden, für das die Nachricht bestimmt ist.

Boom – ein Absturz.

Das Senden einer Nachricht an nil in Swift und Objective-C hat keine schädlichen Auswirkungen und ist gültig, aber das Senden einer Nachricht an eine Adresse im Speicher, an der sich ein Objekt befinden sollte, sich aber nicht befindet, führt definitiv zu einem Absturz.

Jetzt sind Sie endlich bereit, die Metal-Beispiel-App auszuführen.

Drücke denSpielen Klicken Sie oben im Xcode-Fenster auf die Schaltfläche und der Code wird kompiliert. Wenn keine Fehler vorliegen und alles funktioniert, startet Xcode den iOS-Simulator und führt die App darin aus:

Beachten Sie, dass einige, aber nicht alle Metal-Codes nicht im Simulator ausgeführt werden können. Sie müssen diese Metal-Programme stattdessen auf einem echten iOS-Gerät ausführen.

Als letzten Blick auf das Beispielprojekt müssen wir einige Elemente im Interface Builder durchgehen.

Wenn Sie neu bei Xcode und Interface Builder sind, beachten Sie, dass ein kritischer Aspekt der iOS-Entwicklung, den die meisten Neulinge übersehen, die Klassennamen sind. Die Klassennamen, die jedes Element in Xcode hat, müssen genau mit jedem Klassennamen übereinstimmen, der in den Quellcodedateien definiert ist.

Wenn dies nicht der Fall ist, funktioniert Ihre App nicht.

Beispielsweise muss der Klassenname des Ansichtscontrollers im Feld „Benutzerdefinierte Klasse“ im Objektinformationsbereich von Xcode auf der rechten Seite festgelegt sein. Dazu müssen Sie auf die Storyboard- oder .nib-Datei (Interface Builder) klicken, dann auf den Klassennamen in der Szenen- oder Ansichtshierarchie klicken und dann den Klassennamen im Inspektor rechts überprüfen oder festlegen:

Das Gleiche gilt für Ansichten und ihre Klassennamen sowie für andere Objekte wie Delegateneigenschaften. Wenn auch nur ein Klassenname oder eine Eigenschaft nicht festgelegt wird, kann dies dazu führen, dass eine App nicht funktioniert.

Die meisten davon werden normalerweise in von Xcode erstellten Vorlagendateien festgelegt, aber es schadet nicht, dies zu überprüfen.

Eine Sache, die seltsamerweise in Xcode-Vorlagendateien nicht festgelegt wird, sind die Verbindungen zwischen Ansichtscontrollern und ihren Ansichtseigenschaften. Sie müssen diese Verbindungen manuell herstellen, sonst funktioniert Ihre App nicht.

Wenn Sie beispielsweise in unserem Beispielprojekt bei gedrückter Strg-Taste auf das Game View Controller-Objekt in der Ansichtshierarchie klicken, werden Sie feststellen, dass die View-Eigenschaft auf Null gesetzt ist. Sie müssen die Ansicht mit dem Game View Controller verbinden, indem Sie bei gedrückter Strg-Taste klicken und dann vom Game View Controller auf die Ansicht in der Hierarchie ziehen.

Wenn Sie dies tun, wird das Bedienfeld „Outlets“ angezeigt und Sie müssen die Eigenschaft „view“ manuell mit dem Game View Controller-Objekt verbinden:

Ohne diese Verbindung funktioniert die App nicht. Und die von Xcode erstellten Beispielvorlagendateien stellen diese Verbindung standardmäßig nicht für Sie her.

Beachten Sie, dass der kleine Punkt neben den Steckdosennamen im Steckdosenfeld anzeigt, ob eine bestimmte Steckdose angeschlossen ist oder nicht.

Möglicherweise ist Ihnen auch aufgefallen, dass die Datei AppDelegate.swift eine Unterklasse von AppDelegate enthält, die leeren Boilerplate-Code enthält, in der App-Delegate-Datei jedoch nirgendwo Verweise auf den GameViewController vorhanden sind.

Wie wird der GameViewController geladen, wenn die App ausgeführt wird?

Die Antwort ist, dass die Storyboard-Datei den anfänglichen View-Controller definiert und ihn automatisch für Sie lädt, wenn die App zum ersten Mal ausgeführt wird. Wenn Sie ältere Dateien und Code im .nib-Stil (Interface Builder) zum Laden des anfänglichen View-Controllers verwendet hätten, hätte Ihre App stattdessen manuell eine GameViewController-Objektinstanz erstellt und die Methode application:didFinishLaunchingWithOptions von AppDelegate geladen.

Sobald der View-Controller dann die Ansicht geladen hat, erhalten Sie die viewDidLoad()-Meldung auf dem App-Delegaten, wenn Sie AppDelegate als Delegaten des View-Controllers festlegen.

Zusätzlich zu Apples Online-MetalKit und der Metal-Dokumentation gibt es eine Reihe anderer guter Metal-Ressourcen, die Sie sich vielleicht ansehen möchten.

Schauen Sie sich unbedingt Metalkit.org und Metalbyexample.com an, die viele tolle Tutorials zu MetalKit und Metal selbst bieten.

Besorgen Sie sich außerdem unbedingt das ultimative Buch eines Drittanbieters über Metal, den Metal Programming Guide: Tutorial and Reference via Swift von Janie Clayton, der Ihnen so ziemlich alles beibringt, was Sie über Metal-Programmierung wissen müssen.

Dies war ein langes Tutorial, aber jetzt sollten Sie viel besser verstehen, wie Metal-Apps funktionieren und wie Sie MetalKit in Ihren Apps verwenden, um problemlos Texturen zu laden und Metal-Objekte in Ansichten in iOS-Apps zu rendern.

Chip ist seit 30 Jahren ein Veteran der Apple-Branche, Autor von 18 kommerziellen Mac-Softwareprodukten und ehemaliger Mitarbeiter von Apple und Sony.

Datei->Neues ProjektiOSMac OSSpielNächsteSchnellMetallNächste„+“Spielen