3D-Spiele auf Instagram Javascript oder Wurstflugbahn



Nach dem ersten Artikel über das Programmieren von Spielen in Masken auf Instagram bat mich ein Kunde, ein Spiel auf Instagram für seine Pizzeria zu erstellen. Dieses Spiel sollte für Werbezwecke verwendet werden. Natürlich verstehe ich, dass das Instagram-Thema nach der Anzahl der Aufrufe des ersten Artikels für die Habr-Community nicht besonders interessant ist. Anscheinend wird dieser Service immer noch als eine Art frivole Unterhaltung für Blondinen angesehen, und ein Artikel darüber eignet sich nur als Freitagslesung für einen Abendcocktail. Heute jedoch nur Freitag. Hol dir einen Cocktail. Und vergessen Sie nicht, Blondinen einzuladen. Es ist jedoch wahrscheinlich, dass die Technologie in Zukunft solche Höhen erreichen wird, dass wir beginnen werden, in GTA-5 auf Instagram zu spielen. Oder 10.

In diesem Artikel werden zwei Spiele gleichzeitig behandelt. Ich habe einen auf Bestellung angefertigt und dann den zweiten für mich. Mit jedem neuen Spiel stand ich vor neuen Aufgaben, bei der Suche nach Lösungen, mit denen ich mich mit den neuen Nuancen der Entwicklung vertraut machte. Vielleicht ist diese Geschichte für andere Entwickler von Elementen der Augmented Reality nützlich. Die Spiele sind in reinem Javascript ohne die Verwendung zusätzlicher Bibliotheken geschrieben. Zur Anzeige von 3D-Grafiken werden die integrierten Instagram-Tools verwendet.

Spiel 1. Pizza


Also, was ist die Essenz des ersten Spiels. Pizza drehen. Komponenten fliegen von seiner Oberfläche ab - Wurststücke, Tomaten und so weiter. Zwei Spieler müssen ihren Mund fangen. Der Gewinner ist derjenige, der, wie Sie sich vorstellen können, mehr fängt. Mit der Zeit nimmt die Drehzahl zu. Das Lustigste in diesem Spiel war für mich, die Flugwege dieser essbaren Elemente zu programmieren. Stellen Sie sich vor, Sie programmieren einen Wurstflug. Nein, definitiv, ich hatte noch nie so viel Spaß beim Programmieren. Ich lache schon jetzt und schreibe diesen Artikel.

Als der Kunde den funktionierenden Prototyp sah, schickte er mir Folgendes: ":))". Beim Testen des Endergebnisses ist ein Problem aufgetreten. Es bestand darin, dass die Probanden das Spiel nicht spielen konnten: Sie platzten einfach vor Lachen - es machte so viel Spaß.

Ich möchte Sie daran erinnern, dass die Entwicklung von Masken und Spielen in Spark AR Studio erfolgt. Und von dort aus können Sie Ihre Arbeit auf Instagram hochladen, damit jeder sie sehen kann. Es ist bemerkenswert, dass Webtechnologien die Grenzen zwischen Betriebssystemen für einen Entwickler verwischen. Sie schreiben einen Code, der dann in der Instagram-Anwendung für iOS und Android ohne Änderungen oder Modifikationen funktioniert. Natürlich wird die Zahlung dafür zu einer grundsätzlich niedrigen Skriptgeschwindigkeit.

Der Zeitplan für das Spiel wurde vom Kunden zur Verfügung gestellt. Es gab einige Schwierigkeiten mit 3D-Modellen. Insbesondere bei Verwendung der integrierten Animations-Engine Spark AR Studio stellte sich heraus, dass bei einer Skalierung des sich bewegenden Objekts dessen Koordinaten im Spiel falsch bestimmt werden. Dieser störende Effekt wird jedoch nicht beobachtet, wenn Sie nicht das gesamte Objekt vollständig skalieren, sondern jedes seiner Netze separat. Ich musste den Maßstab der Pizza 1: 1 verlassen und für jedes Element einen bestimmten Koeffizienten vorschreiben. Es gab auch Probleme mit dem FBX-Format, in das Sie Modelle für ein Instagram-Spiel exportieren müssen. Der Grafikdesigner schickte Modelle mit eingebauten Texturen, die außerdem entlang eines relativen Pfades platziert wurden. Der 3D-Editor hat sie gesehen, Spark AR jedoch nicht. Ich musste die Modelle neu verpacken, damit die Texturdateien getrennt von den Modellen und entlang eines Pfades mit ihnen lagen.

Ein weiteres kleines Problem, auf das ich gestoßen bin, war, dass das API-Saprk-AR-Objekt, das für die Anzeige von Text auf dem Bildschirm verantwortlich ist, sich weigerte, den Wert als Wert zu akzeptieren - beispielsweise als Spielstand. Ich konnte lange nicht verstehen, warum nichts angezeigt wird. Was mache ich falsch? Es stellte sich heraus, dass Sie zuerst die Zahlen in Zeichenfolgen konvertieren müssen (.toString ()). Dies ist für andere Sprachen selbstverständlich, für Javascript jedoch eher untypisch, was dies immer selbst getan hat.

Einfache Animation


Eines der neuen Dinge in diesem Spiel war für mich die Programmierung der Animation von 3D-Objekten. (In meinem vorherigen Instagram-Spiel "Tic-Tac-Toe" gab es überhaupt keine Animation.) Die Animations-Engine in Spark AR Studio ist sehr spezifisch. Es benötigt einige Eingabeparameter und verbindet die Variable dann reaktiv mit dem Objekt, in dem Sie etwas ändern möchten. Dies ändert beispielsweise die y-Koordinate (startValue, endValue - ihre Anfangs- und Endwerte) eines 3D-Objekts über die Zeit t:

var driverParameters = {
    durationMilliseconds: t,
    loopCount: Infinity,
    mirror: false
};
var driver = Animation.timeDriver(driverParameters);
var sampler = Animation.samplers.linear(startValue, endValue);
sceneObject.transform.y = Animation.animate(driver, sampler);
driver.start();

Um die im Weltraum abfliegenden Pizza-Zutaten zu bewegen, habe ich beschlossen, einfach drei solcher Animationen für jede Koordinate parallel auszuführen. Es genügte, die Anfangskoordinate (startValue) und die Endkoordinate anzugeben, die durch den Drehwinkel der Pizza (endValue) berechnet wurden, so dass sie irgendwo weit entfernt waren, falls der Spieler diese „Muschel“ nicht mit dem Mund auffing. Wenn gefangen - dann stoppt die Bewegung. Das Ereignis zum Öffnen des Mundes, das ich bereits in einem früheren Artikel beschrieben habe. Nur hier gibt es ein Spiel für zwei und dementsprechend wird es bereits zwei Gesichter und zwei Münder geben:

FaceTracking.face(0).mouth.openness.monitor().subscribe(function(event) {
    if (event.newValue > 0.2) {
        ...
    };
});

FaceTracking.face(1).mouth.openness.monitor().subscribe(function(event) {
    if (event.newValue > 0.2) {
        ...
    };
});

Der entscheidende Punkt in diesem Spiel ist das Fangen fliegender Zutaten mit dem Mund, dh das Finden eines Flugobjekts in einem bestimmten kleinen Bereich um die Mitte des Mundes einer Person. Anfangs wollte diese Berechnung für mich nicht richtig durchgeführt werden, aber nach der Einführung eines versteckten Objekts, das an die Koordinaten des Mundes gebunden ist, wurde eine Lösung für das Problem gefunden, und es funktionierte. Aus irgendeinem Grund kehrten die direkten Koordinaten des Mundes nicht zurück. Hier ist cameraTransform.applyTo die Reduktion der Koordinaten von Gesichtspunkten auf die Koordinaten in der 3D-Welt (die Methode ist der Dokumentation entnommen).

move: function() {
    //   (   )

    //    
    var object = Scene.root.find('pizzafly');
    var olast = {
        x: object.transform.x.pinLastValue(),
        y: object.transform.y.pinLastValue(),
        z: object.transform.z.pinLastValue()
    };

    //   ,       
    var objectHidden = Scene.root.find('nullObject');
    objectHidden.transform.x = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).x;
    objectHidden.transform.y = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).y;
    objectHidden.transform.z = FaceTracking.face(face).cameraTransform.applyTo(FaceTracking.face(face).mouth.center).z;

    //   
    var mouth = {
        x: objectHidden.transform.x.pinLastValue(),
        y: objectHidden.transform.y.pinLastValue(),
        z: objectHidden.transform.z.pinLastValue()
    };

    //    
    var d = {
        x: Math.abs(olast.x - mouth.x),
        y: Math.abs(olast.y - mouth.y),
        z: Math.abs(olast.z - mouth.z)
    };

    //  
    if ((d.x > 0.03) || (d.y > 0.03) || (d.z > 0.03)) {
        // 
        ...
    } else {
        // 
        ...
    };

},

Anschließend wurde mir klar, dass Sie die Tiefenprüfung (z-Koordinate) entfernen sollten, da es in diesem speziellen Spiel visuell schwierig ist, die Tiefe abzuschätzen. Das heißt, jetzt wurde es möglich, die Zutat zu fangen, indem Sie jederzeit während des Fluges den Mund öffnen. Die Hauptsache ist die Kombination von x und y.


Das letzte Problem, auf das ich beim Schreiben dieses Spiels gestoßen bin, war die Begrenzung der Größe des endgültigen Builds auf 4 MB. Darüber hinaus ist es laut der Empfehlung von Facebook wünschenswert, dass sogar 2 MB vorhanden sind, damit das Spiel auf möglichst vielen Mobilgeräten angezeigt werden kann. Und 3D-Modellierer sind kreative Menschen und möchten schwere Modelle mit einem dichten Raster und riesigen Texturen erstellen, ohne sich um uns, Programmierer oder vielmehr um die endgültige Leistung in Spielen zu kümmern. Ich habe auch irgendwie die Texturen verkleinert und in JPG (anstelle von PNG) komprimiert, aber das Pizzamodell selbst musste zur Überarbeitung gesendet werden (Retopologie). Infolgedessen war es dennoch möglich, mit allen Modellen, Texturen und einem Skript in das 2-MB-Volume zu passen. Und das Spiel ging in Maßen weiter.

Und nach einiger Zeit kehrte sie mit der Formulierung zurück, dass "der Effekt zu viel statischen Text enthält". Ich musste die Countdown-Nummern entfernen und stattdessen die Animation auf den Pfeil auf der Stoppuhr setzen, der nun begann, die Zeit anstelle von ihnen zu zählen. Danach wurde das Spiel genehmigt.

Spiel 2. Über den Schmetterling (ButterFlap)



Jetzt werde ich über das Erstellen eines zweiten Spiels sprechen. Ich kann nicht anders, als Spiele zu machen, das ist mein Hobby. Einige Tage vor dem 8. März habe ich beschlossen, ein Instagram-Spiel für diesen Feiertag zu erstellen. Etwas über Blumen, Schmetterlinge und Süßigkeiten. Aber ich konnte mir nicht vorstellen, was das Wesentliche des Spiels sein könnte. Der Gedanke drehte sich in meinem Kopf. Vielleicht sammelt der Schmetterling Süßigkeiten und legt sie in einen Korb? Oder nehmen Schmetterlinge vielleicht Süßigkeiten in die Luft und werfen sie, und der Spieler muss sie mit dem Mund fangen? Im Allgemeinen habe ich ein paar Tage lang betrogen und nachdem ich keine Lösung gefunden hatte, wurde mir klar, dass mein Spiel bis zum 8. März immer noch keine Zeit hatte, die Moderation zu durchlaufen, da letzteres 3-4 Tage dauert. Ich wollte dieses Unternehmen bereits verlassen, als plötzlich die Idee von selbst kam, plötzlich. Ich habe das Spiel nicht mehr an den Frauentag gebunden, jetzt war es nur noch ein Spiel.

Scroller. Von links nach rechts bewegen sich Landschaft und verschiedene Hindernisse. Der Spieler muss einen Schmetterling kontrollieren, der sich frei auf dem Bildschirm bewegen und in der Luft hängende Diamanten sammeln kann. Außerdem bewegen sich von rechts nach links etwas schneller als der Rest des Bildes Wolken, aus denen Regen strömt. Sie müssen sich vor dem Regen unter den Blumen verstecken. Wenn ein Schmetterling unter Wasser fällt, werden seine Flügel nass und er fällt. Dies ist das Ende des Spiels. Ich habe das Spiel einfach genannt: ButterFlap.

Ja, das hört sich alles gut an. Ich wollte mich direkt spielen. Aber ich erinnerte mich, dass ich überhaupt kein Künstler war. Ich kann jedoch 3D-Modelle erstellen, was einfacher ist als das Zeichnen. Also wurde beschlossen, dass es einen 3D-Scroller geben sollte.

Grafik


Ich fand lizenzfreie Online-Modelle, die ich verfeinern musste. Ich habe Texturen auf jene ohne Texturen gezogen, nachdem ich diese zuvor in den Texturatlas eingefügt hatte: Artefakte in Form von „Leitern“ sind auf nicht texturierten Modellen sichtbar, und Texturen im Spiel können geglättet werden, wodurch das Erscheinungsbild von Objekten präsentabler und nicht so flach wird. Die Modelle, die Texturen enthielten, hatten auch ihre Fehler, die behoben werden mussten. Erstens war die Größe der Texturen kein Vielfaches von zwei. Das heißt, es könnte zum Beispiel 1200x1200 sein. Ich musste auf 1024x1024 komprimieren. Videoprozessoren können unangemessene Texturen mit leerem Raum skalieren oder ausfüllen, d. H. Solche, die nicht 1024 x 1024, 512 x 512, 256 x 256 usw. sind. In jedem Fall sind solche Aktionen eine zusätzliche Belastung während der Arbeit des Spiels und ein bedeutungsloser Speicherverbrauch.Daher ist es besser, die richtigen Bilder zuerst manuell vorzubereiten. Die Situation wurde auch durch die Tatsache gerettet, dass ich alle Texturen nach Texturatlanten verteilt habe. Wenn also beispielsweise die ursprüngliche Textur 400 x 200 Pixel groß war, konnte ich sie einfach neben anderen ähnlichen in den Atlas 1024 x 1024 einfügen. Danach ist es natürlich notwendig, auch den UV-Scan zu skalieren, dies geschieht jedoch in wenigen Sekunden. Es gab immer noch Modellvarianten, bei denen die Texturen aus irgendeinem Grund entlang eines absoluten Pfades wie „C: \ Work \ Vasya \ Map.jpg“ gebunden waren. Nun, es gibt keinen solchen Ordner auf meinem Computer! Ich musste die Pfade zu den Texturen manuell angeben ... Aber warum kamen Sie auf die Idee, dass ich alle meine Projekte auf dem Laufwerk "C:" behalten sollte! Oh, diese Modellierer, freie Künstler ... Übrigens, auf diese Weise können Sie durch die Namen von Ordnern in zufällig linken Pfaden versehentlich mehr über die Identität des Modellierers erfahren.zum Beispiel sein Name. Freut mich, dich kennenzulernen!

Am schwierigsten war es, ein geeignetes Schmetterlingsmodell mit Animation zu finden. Spark AR verwendet Animationen, die aus jedem 3D-Editor in das FBX-Format exportiert werden können. Die Flügel eines Paares von Schmetterlingsmodellen, die ich heruntergeladen habe, waren irgendwie seltsam gespiegelt - ein Flügel wurde modelliert, und der zweite wurde auf eine Weise reflektiert, die ich nicht verstand, und so stellten sich zwei heraus. Mit dieser Herangehensweise an die Modellierung wollte am Ende im Spiel einer der Flügel (kopiert) kein Licht von der Quelle empfangen und blieb immer dunkel. Ich habe nicht riskiert, das Modell drastisch zu ändern, da dann die Animation geflogen wäre. Und in der Animation bin ich ein noch größerer Neuling als in der 3D-Modellierung. Vielleicht war das Problem etwas anderes: Ich habe zum Beispiel versucht, die Normalen zu erweitern, aber das hat nicht geholfen. Kurz gesagt, kostenlose 3D-Modelle sind ein Schmerz. Infolgedessen, den ganzen Abend gewaschen,Durch Versuch und Irrtum fand ich ein geeignetes Modell, das nach einiger Feinabstimmung mit einer Datei zufriedenstellend aussah. Etwas, das mir nicht gefallen hat, aber es waren kleine Dinge: Ich habe die Textur überarbeitet, die Geometrie an einigen Stellen geändert und die Materialparameter angepasst. Schließlich startete der Schmetterling. Hurra. Aber jetzt bin ich erschöpft. Also beschloss ich, ins Bett zu gehen und am nächsten Tag fortzufahren. Ja, ich habe 7-8 Tage damit verbracht, das Spiel zu erstellen. Aber es war eine ziemlich gemächliche Arbeit am Abend, Dokumentation, Artikel zu lesen und Antworten auf Fragen zu finden.Also beschloss ich, ins Bett zu gehen und am nächsten Tag fortzufahren. Ja, ich habe 7-8 Tage damit verbracht, das Spiel zu erstellen. Aber es war eine ziemlich gemächliche Arbeit am Abend, Dokumentation, Artikel zu lesen und Antworten auf Fragen zu finden.Also beschloss ich, ins Bett zu gehen und am nächsten Tag fortzufahren. Ja, ich habe 7-8 Tage damit verbracht, das Spiel zu erstellen. Aber es war eine ziemlich gemächliche Arbeit am Abend, Dokumentation, Artikel zu lesen und Antworten auf Fragen zu finden.


Den ganzen Abend am nächsten Tag habe ich an Grafiken gearbeitet. Die Besonderheit von Spielmasken für Instagram ist, wie bereits erwähnt, dass es ratsam ist, das 2-MB-Volumen für das gesamte Spiel (maximal 4 MB) nicht zu überschreiten. Ich habe mir keine Sorgen um das Skript gemacht: Es ist unwahrscheinlich, dass seine Größe 50 KB überschreitet. Aber über 3D mussten Modelle von Pflanzen ziemlich beschworen werden. Zum Beispiel wurde der Teil der Sonnenblume, in dem sich die Samen befinden, durch Geometrie hergestellt. Hunderte von Polygonen ... Ersetzen Sie es durch ein Fragment einer Kugel aus ein paar Dutzend Dreiecken und dehnen Sie die heruntergeladene Textur dieses Teils. Die Anzahl der Blätter kann ebenfalls reduziert werden. Wir machen Gras am unteren Rand der Szene mit einer Ebene von zwei Dreiecken mit einer überlagerten Textur mit einem Alphakanal. Wir erreichen das Volumen mit Schatten und kopieren Fragmente des Bildes in die Textur selbst.

Im Allgemeinen ist es wünschenswert, die Anzahl der Texturen sowie deren Größe in Bytes zu minimieren. Es sind die Texturen, die den Großteil des Spiels ausmachen. Ich bin es gewohnt, Texturen, bei denen Transparenz erforderlich ist, in einem Texturatlas zu platzieren - in PNG 32-Bit - und Texturen, die den Alphakanal nicht verwenden, in einen anderen Atlas zu packen und ihn in JPG zu speichern. Jpg kann stärker als png geschrumpft werden. Gras- und UI-Elemente, die Transparenz benötigen, wurden in den PNG-Atlas und alles andere in JPG aufgenommen.

Das Gesamtvolumen. Insgesamt habe ich 4 Arten von Pflanzen, einen Stein, einen Schmetterling, den der Spieler kontrollieren wird, und eine Wolke. Die Texturen aller dieser Modelle in komprimierter Form nahmen 2 Atlanten 1024 x 1024 jpg und png mit einem Gesamtvolumen von 500 KB an. Die Modelle selbst nahmen etwa 200 Kb. Plus ein Skript. Sounds - 31 Kb. Gesamt: ca. 1 MB. In Grün wird nur die Größe des Builds angezeigt (dies sollte in 2 MB passen).


Plötzlich stieß ich auf ein völlig unerwartetes Problem. Ich habe mehrere nicht verwendete Modelle gelöscht, jedoch nicht über Spark AR Studio, sondern aus dem Dateisystem. Beim Zusammenstellen des Builds stellte ich anschließend fest, dass sie aus der Spark AR Studio-Szene verschwunden waren, aber dennoch in die Assembly aufgenommen wurden. Und das Projekt von ungenutzten Ressourcen zu befreien, ist unmöglich. Links zu ihnen aus der "Asset Summary" führen nirgendwo hin. Anscheinend ist dies ein Defekt in Spark AR Studio. Ich musste das Projekt erneut erstellen und von Anfang an alle erforderlichen Ressourcen hinzufügen.

Szene


Ich habe lange darüber nachgedacht, wie das Scrollen aller Objekte auf dem Bildschirm implementiert werden kann. Von den Tools hierfür gibt es in Spark AR Studio nur Javascript und die einfachste integrierte Animations-Engine, mit der die Koordinaten des Objekts in einer bestimmten Zeit einfach vom Anfangswert zum Endwert geändert werden können.

Ja, das Dilemma war noch vorher aufgetreten: ob das gesamte Level des Spiels, eine Szene in einem 3D-Editor, erstellt werden soll, indem die sich wiederholenden Pflanzen die erforderliche Anzahl von Malen dupliziert und an den richtigen Positionen platziert werden, um die gesamte Szene im Spiel zu scrollen oder nur eine Kopie jedes 3D zu laden Objekt und ersetzen Sie sie in Richtung des Spielers direkt außerhalb des Bildschirms. Die Antwort war offensichtlich. Unsere Wahl ist die zweite Option. Andernfalls ist es definitiv nicht möglich, in 2 MB zu passen: Höchstwahrscheinlich ist die Szene gemäß der ersten Option schwer. Aber dann brauchen Sie ein Layout von Objekten. Und ich habe mich ohne zu zögern entschieden, den 3D-Editor als Level-Editor zu verwenden. Ja, es stellt sich heraus, dass ich beides getan habe. Pflanzen für das Spiel habe ich jeweils in einer Kopie gespeichert. Und vom Editor brauchte ich nur die Koordinaten. Nach Abschluss der Arbeit,Ich schrieb die Koordinaten aller Objekte auf und erstellte ein Array mit den Daten für das Spiel.

lv:[
    {n:'flower1', x:8.0, y:-6.5},
    {n:'cloud', x:10.0, y:0.1},
    {n:'rain', x:10.0, y:6.6},
    {n:'flower1', x:14, y:-2.5},
    {n:'diamond_red', x:20, y:2.0},
	...
],

Und doch - ein separates assoziatives Array nach Objekttypen, in dem die Größe der Kollider und ihre Verschiebungen relativ zu den Zentren von 3D-Objekten gespeichert sind, sowie eine Flagge (Spalte), die bestimmt, ob das Objekt ein Hindernis ist (andernfalls passiert der Spieler es). Für einige Objekttypen wird ein (Zerstörungs-) Flag gesetzt, das bestimmt, ob das Objekt nach der Interaktion ausgeblendet werden soll, sowie ein (v) -Parameter, der einen bestimmten Interaktionsgrad bestimmt, z. B. die Anzahl der Punkte, die ein Spieler gewinnt oder verliert.

colld:{
    'flower1': {dx:0, dy:5, w:2.3, h:1.4, col:true},
    'diamond_green': {dx:0, dy:0, w:1, h:1, col:false, v:1, destroy:true},
    'diamond_red':{dx:0, dy:0, w:1, h:1, col:false, v:-2},

    ...
},

Eine kleine Nuance. Das Gras am unteren Rand der Szene sollte kontinuierlich scrollen. Sie müssen also zwei Kopien dieses Objekts verwenden und sie beim Verschieben nacheinander ersetzen. Blumen erscheinen auf einem Bildschirm nicht mehr als jeweils in einer Kopie.

Unter Berücksichtigung des Skalierungsproblems, auf das ich bei der Entwicklung des ersten Spiels gestoßen bin, habe ich die Skalierung aller Modelle im 3D-Editor zurückgesetzt. So werden bei Saprk AR-Modellen sofort normale Größen geladen. Zwar konnte es im Skript ohnehin nicht ohne die "magische Zahl", den globalen Koeffizienten, den universellen Code, der die Essenz des Universums enthält, auskommen. Natürlich ein virtuelles Universum. Und ich bin bereit, Ihnen diese Nummer mitzuteilen. Benutze es, Leute! Es ist mir egal! Diese Nummer ist 0.023423. Kurz gesagt, trotz des Zurücksetzens aller Skalen stellte sich heraus, dass ein Meter im 3D-Editor genau dieser Zahl in Spark AR Studio entspricht. Höchstwahrscheinlich verstehe ich jedoch nicht alle Feinheiten der Arbeit mit 3D-Grafiken, daher der Koeffizient. Die Koordinaten (aber nicht die Größe) aller Objekte, die aus dem Editor exportiert wurden, werden damit multipliziert, Sie haben es erraten.Wo ich den Maßstab der Szene in Spark AR anpassen konnte, habe ich nicht gefunden.

Das nächste Problem, auf das ich stieß, war der Verlust der Sortierreihenfolge von Objekten beim Exportieren aus einem 3D-Editor. Komplexe Objekte, die aus mehreren Maschen bestehen, können im Spiel unvorhersehbar auf der Szene erscheinen, so dass beispielsweise ein Netz, das sich hinter einem anderen befindet, plötzlich nach vorne springt. Wenn Sie sich die Reihenfolge der Objekte nach dem Export in Spark AR Studio ansehen, zeigt dies, dass dieses Netz aus irgendeinem Grund in der Liste höher ist, obwohl es im Editor niedriger war. Ich habe dieses Problem gelöst, indem ich die Szene in Ebenen unterteilt und in verschiedenen Dateien gespeichert habe.

Komplexe Animation


Für weitere drei Abende spielte ich mit dem Programmieren. Wenn ich die Standard-Spark AR Studio-Engine zum Animieren der Flügel eines Schmetterlings verwendet habe, war das Verschieben des Hintergrunds nicht so einfach. Ich verstand immer noch nicht, wie man nicht nur einen variablen Parameter an Iterationen des Bewegungszyklus anfügt, sondern eine vollwertige Rückruffunktion. Auf jeden Fall konnte ich es nicht tun, die aktuellen Animationsparameter wollten dort nicht übertragen werden. Und eine solche Funktion ist einfach notwendig, denn wenn ich im ersten Spiel (mit Pizza) die Kollision durch das Öffnen des Mundes durch Abonnieren überprüft habe, gab es hier kein solches Ereignis. Und es war nur notwendig, Kollisionen mit Umgebungsobjekten zu überprüfen, während sich der Charakter gemäß seinen aktuellen Koordinaten bewegt. Und dafür müssen die Koordinaten bei jeder Iteration verglichen werden. Und dann dachte ich. Immerhin habe ich schon Spiele in Javascript geschrieben.Und warum nicht Ihre Animations-Engine verwenden, die ich zuvor für diese Spiele geschrieben habe? Das Funktionsprinzip ist ungefähr dasselbe: In einer bestimmten Zeit ändert sich der Parameter (oder die Parameter) zwischen den angegebenen Anfangs- und Endwerten. Und der aktuelle Wert wird in der angegebenen Rückruffunktion als Parameter übergeben - Sie werden es nicht glauben -, in der Sie beispielsweise ein 3D-Objekt in der Szene an Koordinaten festlegen können, die diesen aktuellen Werten entsprechen, oder auf Kollisionen prüfen können. Danke, Cap. Ich musste meine Engine leicht an das lokale „Ökosystem“ anpassen: Entfernen Sie Verweise auf das Fensterobjekt von dort, da es nicht hier ist, und andere kleine Dinge.Und der aktuelle Wert wird in der angegebenen Rückruffunktion als Parameter übergeben - Sie werden es nicht glauben -, in der Sie beispielsweise ein 3D-Objekt in der Szene an Koordinaten festlegen können, die diesen aktuellen Werten entsprechen, oder auf Kollisionen prüfen können. Danke, Cap. Ich musste meine Engine leicht an das lokale „Ökosystem“ anpassen: Entfernen Sie Verweise auf das Fensterobjekt von dort, da es nicht hier ist, und andere kleine Dinge.Und der aktuelle Wert wird in der angegebenen Rückruffunktion als Parameter übergeben - Sie werden es nicht glauben -, in der Sie beispielsweise ein 3D-Objekt in der Szene an Koordinaten festlegen können, die diesen aktuellen Werten entsprechen, oder auf Kollisionen prüfen können. Danke, Cap. Ich musste meine Engine leicht an das lokale „Ökosystem“ anpassen: Entfernen Sie Verweise auf das Fensterobjekt von dort, da es nicht hier ist, und andere kleine Dinge.

Ja, noch - über das Scrollen der Landschaft und der Objekte der Umgebung. Ich beschloss, die ganze Welt in ein NullObject zu setzen, dh in ein leeres 3D-Objekt, einen Container, und es mit nur einem Parameter für die Animation zu verschieben - seiner x-Koordinate. Innerhalb des Containers haben alle Modelle die gleichen Koordinaten wie außerhalb, nur für sie ist das Referenzsystem jetzt an dieses leere Objekt gebunden. Steine ​​und Blumen werden wiederholt (außerdem in unterschiedlichen Höhen vom Boden gemäß dem Niveaudiagramm), sodass Sie diese Objekte während der Bewegung wiederverwenden können und sie in der gewünschten horizontalen und vertikalen Position innerhalb des "Containers" einstellen können. Ich habe ein Suchsystem für Objekte geschrieben, die in den Rahmen fallen (am aktuellen Container-Offset), das das Objekt, das den Rahmen verlässt, weiter an eine neue Position setzt, falls es dort erscheinen soll. Du kannst sehen,wie es am Beispiel von drei Objekten funktioniert. (Es werden mehr davon im Spiel sein, so dass Sie dort keinen solchen Effekt der "Neuanordnung" von Umgebungsobjekten mehr sehen.)



Die Funktion zum Aktualisieren der Koordinaten von Objekten sieht folgendermaßen aus:

oCoordSet: function(v) {
    //  :    
    //   
    var camx = -ap.oWorld.transform.x.pinLastValue();
    //  
    var x1 = camx - ap.game.scro.w2;
    var x2 = camx + ap.game.scro.w2;
    //   
    for (var i = 0; i < ap.d.lv.length; i++) {
        //    
        if ((ap.d.lv[i].x >= x1) & (ap.d.lv[i].x <= x2)) {
            //   
            ap.d.lv[i].o = Scene.root.find(ap.d.lv[i].n);
            //  -  
            ap.d.lv[i].o.transform.y = ap.d.lv[i].y;
            if ((ap.d.lv[i].n == 'cloud') || (ap.d.lv[i].n == 'rain')) {
                //    ,
                //  
                //   2.3,
                //     
                ap.d.lv[i].o.transform.x = ap.d.lv[i].x - (x2 - ap.d.lv[i].x) * 2.3 + 0.2;
            } else {
                //    ,
                //      
                ap.d.lv[i].o.transform.x = ap.d.lv[i].x;
            };
        };
    };
    //        
    //     
    if (camx > ap.game.grassPosLast) {
        ap.game.grassPosLast += ap.game.grassd;
        ap.game.grassi = 1 - ap.game.grassi;
        ap[ap.game.grassNm[ap.game.grassi]].transform.x = ap.game.grassPosLast;
    };
},

Protagonist


Die Hauptfigur im Spiel ist ein unsinkbarer Schmetterling, der mutig vorwärts fliegt und Hindernisse überwindet. Ich entschied mich für das Management, indem ich einen Marker oder Cursor (Lichtpunkt) setzte, indem ich meinen Kopf drehte und neigte. Und in Richtung dieses Punktes fliegt ein Schmetterling langsam (tatsächlich ist er kein Kämpfer und kann sich auch nicht teleportieren). Wenn Sie beispielsweise das Ereignis "Kopfneigung" abonnieren, können Sie die vertikale Steuerung folgendermaßen implementieren:

FaceTracking.face(0).cameraTransform.rotationX.monitor().subscribe(function(event) {
    var v = event.newValue;
    //  
    var scrH2 = ap.game.scr.h2;
    //
    var p = -v * 0.5;
    if (p < -scrH2) {
        p = -scrH2;
    } else if (p > scrH2) {
        p = scrH2;
    };
    //  
    var d = 0.006;
    //  
    var cur = ap.oPers.transform.y.pinLastValue();
    if (p < cur) {
        cur -= d;
        if (cur < p) {cur = p;};
    } else {
        cur += d;
        if (cur > p) {cur = p;};
    };
    //    ()
    ap.oPointer1.transform.y = p;
    //   ,
    // ,    
    ap.game.pers.dy + = cur - ap.game.pers.y;
});

Ähnliches gilt für die Horizontale. Nur dort ist es notwendig, das Ereignis nicht einer Neigung, sondern einer Kopfdrehung (Rotation Y) zu abonnieren und anstelle der Höhe des Bildschirms dessen Breite zu berücksichtigen.

Collider


Das alles ist wunderbar, die Welt bewegt sich und der Spielcharakter kann sich frei auf dem Bildschirm bewegen. Aber jetzt brauchst du einen Kollisionshandler, sonst funktioniert kein Spiel. Es gibt drei Ereignisse im Spiel, nach denen sich die Position des Charakters ändern kann. Dies ist die Drehung und Neigung des Kopfes sowie die Bewegung der Welt, bei der die horizontale Koordinate des Spielers (x) automatisch zunimmt.

Da ich nicht weiß, wie Iterationen des Face-Handlers in Spark AR funktionieren - ob sie mit einer bestimmten Frequenz aufgerufen oder auf die maximal mögliche Taktfrequenz eingestellt sind und ich diesen Parameter in meiner Animations-Engine steuern kann, habe ich beschlossen, Kollisionen in meiner Funktion zu definieren die Bewegung der Welt, die mit einer von mir festgelegten Frequenz (60 Bilder pro Sekunde) aufgerufen wird. Bei Gesichtsverarbeitungsereignissen werden wir nur Bewegungen „akkumulieren“.

Das Prinzip wird so sein. Bei Kopfneigungs- und Kopfdrehereignissen wird ein Versatz entlang der x- und y-Achse akkumuliert. Darüber hinaus wird beim Scrollen der Welt dem „Sparschwein“ eine horizontale Verschiebung hinzugefügt. Und dann wird geprüft, ob es zu einer Kollision mit einem der Objekte auf der Welt kommt, wenn die akkumulierte Verschiebung zu den ursprünglichen Koordinaten addiert wird. Wenn nicht, werden die ursprünglichen Koordinaten des Spielers plus der Versatz durch die aktuellen Koordinaten gebildet. Und dann aktualisieren wir die Quell-Offsets auf die aktuellen (Reset) und setzen die Offsets zurück. Wenn ja, rollen Sie die Koordinaten auf das Original zurück. Darüber hinaus müssen wir bestimmen, auf welcher Achse die Kollision stattfinden würde, da beide Koordinaten nicht zurückgesetzt werden können. Andernfalls „haftet“ der Spieler einfach an einem Punkt im Raum und kann sich nirgendwo mehr bewegen. Es ist notwendig, ihm Bewegungsfreiheit entlang der Achse zu geben, entlang der die Kollision nicht auftritt.So können Sie ihm die Chance geben, um ein Hindernis herumzufliegen.

setPersPos: function(camx, camdx) {
    //   
    //       (camx,camdx)

    //     
    var persx = ap.game.pers.x;
    var persy = ap.game.pers.y;
    var dx = ap.game.pers.dx;
    var dy = ap.game.pers.dy;

    // ,     
    var col = ap.collisionDetect(
        {x: persx, y: persy, dx: dx, dy: dy},
        {x: camx, dx: camdx, y: 0}
    );

    if (col.f == true) {
        // 

        if (col.colx == true) {
            //   
            //    ,
            //   
            //(    ,    )
            ap.game.pers.x = col.x;
        } else {
            //    ,
            //   
            ap.game.pers.x = persx + dx;
        };

        if (col.coly == true) {
            // -  
            ap.game.pers.y = col.y;
        } else {
            ap.game.pers.y = persy + dy;
        };

    } else {
        //  ,   
        ap.game.pers.x = persx + dx;
        ap.game.pers.y = persy + dy;
    };

    // 
    ap.game.pers.dx = 0;
    ap.game.pers.dy = 0;

    //     
    ap.oPers.transform.x = ap.game.pers.x;
    ap.oPers.transform.y = ap.game.pers.y;
},

Die Kollisionserkennungsfunktion selbst:

collisionDetect: function(opers, ow) {
    // , opers -   , ow -  

    var res = {f: false, colx: false, coly: false, x: 0, y: 0};

    var ocoll, x, y, w, h, persx, persy, persx0, persy0, od, colx1, coly1, colx2, coly2;
    var collw = false, collh = false;

    //  
    //(  ,  "" )
    persx0 = opers.x + ow.x - ow.dx;
    persy0 = opers.y + ow.y;

    //       
    persx = persx0 + opers.dx;
    persy = persy0 + opers.dy;

    //  
    for (var i = 0; i < ap.d.lv.length; i++) {
        od = ap.d.lv[i]; //obj data

        //    (   ),
        //     
        //        
        if (typeof ap.d.colld[od.n] !== "undefined") {

            //       
            ocoll = ap.d.colld[od.n];
            colx1 = od.x + ocoll.x1;
            colx2 = od.x + ocoll.x2;
            coly1 = od.y + ocoll.y1;
            coly2 = od.y + ocoll.y2;

            if ((persx < colx1) || (persx > colx2) || (persy < coly1) || (persy > coly2)) {} else {
                //   
                res.f = true;

                //        ,
                //,    
                if ((persx0 < colx1) || (persx0 > colx2)) {
                    collw = true;
                };
                //        ,
                //,    
                if ((persy0 < coly1) || (persy0 > coly2)) {
                    collh = true;
                };

            };
        };

    };

    //   

    //  ,     ,
    //  
    if (collw == true) {
        res.colx = true;
        res.x = persx0 - ow.x;
    } else {
        res.x = opers.x;
    };

    // -  
    if (collh == true) {
        res.coly = true;
        res.y = persy0 + ow.y;
    } else {
        res.y = opers.y;
    };

    return res;
},

Regen


Ich habe die Standard-Partikelmaschine Spark AR Studio verwendet, um Regen zu animieren. Hier gibt es nichts Besonderes. Wir fügen der Szene ein Emtter-Objekt hinzu und vergessen nicht, es nach Emitter -> Space -> Local (anstelle von World) zu fragen. Dies soll sicherstellen, dass die Tropfen während der Bewegung nicht dynamisch hinter den Wolken zurückbleiben, sondern immer direkt fallen. Diese Methode ist bequemer, um den Moment, in dem der Schmetterling in den Regen fällt, leichter bestimmen zu können - es ist keine Höhenkorrektur erforderlich. Für die Tropfen selbst habe ich die passende Textur vorbereitet. Nun, und natürlich bewegt sich das Regenobjekt zusammen mit dem Wolkenobjekt. Dann habe ich dem Kollisionsbehandlungscode eine Cloud-Trefferbedingung hinzugefügt. Und wenn sich in diesem Moment kein Hindernis über dem Spieler befindet, fällt der Schmetterling und das Spiel endet.

Mäßigung


Für eine erfolgreiche Moderation sollte das obligatorische Video aus dem Spiel keinen statischen Text enthalten, der nicht an Objekte gebunden ist. Andernfalls stoppt der Roboter sofort die Moderation. Danach überprüft eine Person das Spiel jedoch mehrere Tage lang. Und er mochte die Fülle an Texten vor und am Ende nicht. Ich musste die Textmenge reduzieren. Danach wurde das Spiel genehmigt.

Zusammenfassung


Nicht dass mein Spiel besonders aufregend gewesen wäre. Ihr fehlen wahrscheinlich Dynamik und zusätzliche Mechanik. Ich werde ein Update veröffentlichen. Aber mir wurde klar, dass es interessant ist, Spiele auf Instagram zu machen. Dies ist eine Art Herausforderung. Ich habe den Prozess des Programmierens und Lösens aller Arten von Aufgaben unter Minimalismus in Bezug auf Werkzeuge und die Menge des zugewiesenen Speichers wirklich genossen. Ich erinnere mich, dass jemand gesagt hat, dass 640 Kb für alle ausreichen. Versuchen Sie nun, ein 3D-Spiel mit 2 MB zu installieren. Ich werde vielleicht nicht argumentieren, dass sie für alle genug sind ... Aber probieren Sie es aus!


Abschließend möchte ich alle nicht offensichtlichen Momente, denen ich begegnet bin, in einer Liste zusammenfassen. Vielleicht ist dies für jemanden als Spickzettel nützlich, wenn er Spiele für Instagram erstellt.

  • Rahmen. Passen Sie den Maßstab aller 3D-Modelle im 3D-Editor so an, dass er sofort 1: 1 auf der Bühne des Spiels steht.
  • . . , .
  • . FBX, . -. , .
  • . JPG , , , -, JPG, PNG.
  • . , , . Spark AR , . , , , 3D-.
  • 3D , : , . . .
  • , , . javascript .
  • Im Kontext von Spark AR gibt es kein Fensterobjekt und alle Arten von browserspezifischen Objekten wie webkitRequestAnimationFrame, mozRequestAnimationFrame usw. Dies ist ein Schicksal beim Programmieren in Javascript-Animationen.

All Articles