Beim Schreiben von Code denken viele an nichts anderes als an die Logik des Programms selbst. Weniger Menschen denken darĂŒber nach, den Code im Laufe der Zeit aus dem Speicher heraus zu optimieren. Aber nur wenige erreichen die letzte Stufe - das Programm wird auf eine kleine RekordgröĂe komprimiert.
Schauen Sie sich zum Beispiel das Ergebnis von
nur 251 Byte JavaScript an:
Nun, lassen Sie uns herausfinden, wie es funktioniert!
Woher kommt es?Dieser Code sowie vieles, worauf ich in diesem Artikel
eingegangen bin, befinden sich auf der Website
p01.org des groĂartigen Mathieu 'p01' Henri, einem JavaScript-Entwickler, der nicht nur hĂ€ufig daran beteiligt ist, den Code auf unmögliche GröĂen zu komprimieren. Das Quellmaterial dieses Artikels finden Sie
hier .
Also, bevor Sie die 251 Bytes des Quellcodes sind.
<body onload=E=c.getContext("2d"),setInterval(F="t+=.2,Q=Math.cos;c.height=300;for(x=h;x--;)for(y=h;y--;E.fillRect(x*4,y*4,bd?4:D/2,D/2))for(D=0;'.'<F[D*y/hD/2|0?1:(d=t+D*Q(T=x/h-.5+Q(t)/8)&7)|(3.5+D*Q(T-8))<<3]&&D<8;b=d)D+=.1",t=h=75)><canvas id=c>
Es ist klar, dass nichts klar ist.
Den Code lesbar machen
ZunÀchst habe ich den gesamten JavaScript-Code der Einfachheit halber in einem separaten Tag ausgegeben.
Es ist ersichtlich, dass die Variablen
E
,
h
,
Q
,
F
und andere Konstanten sind, die durch ihre Werte / Objekte selbst ersetzt werden können, sowie die Namen Àndern können.
var context = c.getContext("2d") var F="t+=.2,Q=Math.cos;c.height=300;for(x=h;x--;)for(y=h;y--;E.fillRect(x*4,y*4,bd?4:D/2,D/2))for(D=0;'.'<F[D*y/hD/2|0?1:(d=t+D*Q(T=x/h-.5+Q(t)/8)&7)|(3.5+D*Q(T-8))<<3]&&D<8;b=d)D+=.1" var t = 75 var size = 75 function render(){ t += 0.2; c.height=300; for(let x = size; x--;) for(let y = size; y--; context.fillRect(x * 4,y * 4,b - d? 4 : D / 2, D / 2)) for(var D = 0; '.' < F[D * y / size - D / 2 | 0 ? 1 : (d = t + D * Math.cos(T = x / size - 0.5 + Math.cos(t) / 8) & 7) | (3.5 + D * Math.cos(T - 8)) << 3] && D < 8; b = d) D += 0.1 } setInterval(render, 75);
Hier wurde der Code aus der Zeichenfolge bereits in die Funktion ĂŒbernommen, und die Zeichenfolge selbst bleibt unberĂŒhrt. Wir werden ihn in Zukunft benötigen.
Konvertieren Sie nun die beiden Ă€uĂeren Schleifen in
while
.
function render(){ t += 0.2; c.height=300; let x = size; while(x > 0){ let y = size; while(y > 0){ for(var D = 0; '.' < F[D * y / size - D / 2 | 0 ? 1 : (d = t + D * Math.cos(T = x / size - 0.5 + Math.cos(t) / 8) & 7) | (3.5 + D * Math.cos(T - 8)) << 3] && D < 8; b = d) D += 0.1 context.fillRect(x * 4,y * 4,b - d? 4 : D / 2, D / 2); y--; } x--; } }
Wie sehen wir das?
Lassen Sie uns verstehen, warum wir es ĂŒberhaupt sehen. Wenn Sie sich das Bild noch einmal ansehen, können Sie viel verstehen.
Klickbares BildFolgendes sehen wir:
- Je weiter das Motiv entfernt ist, desto dunkler ist es
- Der schrĂ€ge Teil der angetroffenen Hindernisse ist unterschiedlich mit Linien und nicht mit Punkten ĂŒberflutet.
Im Code wird die Zeichnung folgendermaĂen wiedergegeben:
Warum sehen wir in dieser Flut schwarzer Punkte volumetrische Objekte? SchlieĂlich mĂŒssen wir uns nur mit verschiedenen Schwarztönen zufrieden geben - der GröĂe der schwarzen Punkte (wir können die Farbe nicht Ă€ndern,
E.fillStyle
ist zu lang!). TatsÀchlich funktioniert es einfach, weil in einem zweidimensionalen Bild unser Auge hauptsÀchlich auf den Schatten und die Helligkeit des Lichts angewiesen ist.
Stellen Sie sich vor, Sie gehen mit nur einer Taschenlampe in den HĂ€nden einen dunklen Korridor entlang. Sie leuchten vor sich und sehen, dass einige Objekte nĂ€her und heller sind (eine Taschenlampe leuchtet, ein Hindernis ist hell, es gibt keine Schatten), wĂ€hrend andere weiter und dunkler sind (Licht ist gestreut, schwach und wir sehen Dunkelheit - und wir spĂŒren die Entfernung). Also hier - je weiter das Objekt (gröĂer
D ), desto gröĂer wird ein schwarzes Quadrat auf dem Bildschirm.
Aber woher wissen wir, was hell sein muss und was nicht?
ZĂ€hle das Pixel
Nun beschÀftigen wir uns mit diesem Monster:
for(var D = 0; '.' < F[D * y / size - D / 2 | 0 ? 1 : (d = t + D * Math.cos(T = x / size - 0.5 + Math.cos(t) / 8) & 7) | (3.5 + D * Math.cos(T - 8)) << 3] && D < 8; b = d) D += 0.1
Also. All dieser Ausdruck ist ein
Raymarching-Algorithmus mit
festem Schritt , mit dem Sie den Schnittpunkt des Strahls mit den Blöcken finden können. FĂŒr jedes Pixel des Bildschirms starten wir einen Strahl und folgen ihm mit einem festen Schritt von
0.1
. Sobald wir auf ein Hindernis stoĂen, beenden wir den Algorithmus und zeichnen ein Pixel auf den Bildschirm, wobei wir den Abstand zum Hindernis kennen.
Beginnen wir mit dem Lesen dieses Codes in Teilen.
Bedingung
D * y / size - D / 2 | 0
D * y / size - D / 2 | 0
kann dargestellt werden als
Dâ( fracyGröĂeâ frac12)=Dâ( fracyâGröĂe/2GröĂe)<1 Dann zeigt der Ausdruck in Klammern die âAbweichungâ
y
von der Bildschirmmitte (in Bruchteilen des Bildschirms). Wir versuchen also zu verstehen, ob sich der Balken zwischen Boden und Decke befindet oder nicht. Wenn wir also den Boden (oder die Decke) berĂŒhren, verlassen wir die Schleife weiter, um ein Pixel zu zeichnen und zu zeichnen.
Und wenn wir uns nicht berĂŒhren, setzen wir die Berechnungen fort: Wir suchen nach den aktuellen Koordinaten des Strahls.
var T = x / size - .5 + Math.cos(t) / 8;
Warum cos (T - 8)?Es stellt sich also heraus, dass
cos(xâ8) ca.sin(x) mit einer Genauigkeit von 0,15 Radiant. Alles weil
frac5 pi2 ca.8,15 ca.8
und dann
cos( alphaâ8) ca.cos( alphaâ frac5 pi2)=cos( alphaâ frac pi2)=sin( alpha)
Es lohnt sich darĂŒber zu sprechen, wie ein Punkt im Raum im Allgemeinen auf einen Block ĂŒberprĂŒft wird. Die Karte selbst stammt aus dem Quellcode (
F
) und sieht folgendermaĂen aus:
t+=.2,Q= ----> ââââââââ Math.cos ----> ââââââââ ;c.heigh ----> ââââââââ - t=300;fo ----> ââââââââ <---- , r(x=h;x- ----> ââââââââ -;)for(y ----> ââââââââ =h;y--;E ----> ââââââââ .fillRec ----> ââââââââ
Es sieht also wie in Bewegung aus, hier wird das Sichtfeld der Kamera angezeigt.

Die Zellen, deren Symbolcode kleiner als der Punktcode -
"."
Ist, sind dunkel markiert
"."
- das heiĂt, die Zeichen
!"#$%&'()*+,-.
Nun runden wir die Koordinaten des Strahls und versuchen herauszufinden, ob der Buchstabe im angegebenen" Koordinaten "-Index dunkel ist (Hindernis) oder nicht (den Strahl weiter fliegen).
Da der Index eins ist und die Koordinaten zwei sind, verwenden wir den Hack:
var boxIndex = xcoord & 7 | ycoord << 3;
Als Ergebnis erhalten wir eine Nummer, die die Blocknummer widerspiegelt (gut oder HohlrÀume).
Kehren wir zum Code zurĂŒck. Jetzt sieht er anstĂ€ndig aus.
Der Code ist etwas fett function render(){ t += 0.2; c.height=300; let x = size; while(x > 0){ let y = size; while(y > 0){ var depth = 0 while(depth < 8){ depth += 0.1 var T = x / size - .5 + Math.cos(t) / 8;
ZurĂŒck zum Zeichnen
Warum brauchten wir das alles? Nachdem wir diesen Algorithmus ausgefĂŒhrt haben, kennen wir die Entfernung zum Objekt und können sie zeichnen. Eine Frage blieb jedoch unbeantwortet: Wie kann die Decke von einer separaten Einheit unterschieden werden? Immerhin sind der Abstand zur Decke und zum Block Zahlen, die nicht anders sind! TatsĂ€chlich haben wir diese Frage bereits beantwortet.
Es gibt eine Bedingung im Code, die sich auf die Variable
b
bezieht und die Breite des "groĂen schwarzen Pixels" beeinflusst:
b - xcoord ? 4 : depth / 2
b - xcoord ? 4 : depth / 2
. Lassen Sie uns diesen Zustand entfernen und sehen, was ohne ihn passiert:
Es gibt keine Grenzen zwischen den Blöcken und der Decke! (anklickbar)Bedingung
b - xcoord
gibt uns eine konstante Breite, wenn die KoordinatenÀnderung 0 ist. Und wann kann dies
nicht passieren? Dies geschieht nicht nur, wenn wir nicht zur
(2) -Zeile im Code gelangen:
Dies bedeutet, dass das Programm den Zyklus in Zeile
(3) frĂŒher verlĂ€sst, wenn der Strahl in einer Richtung fast senkrecht zu seiner Wand in einen undurchsichtigen Block geht, dh in die "FlĂ€che" des Blocks fĂ€llt. Somit unterscheiden sich alle Blöcke vom Boden und der Decke.
So entsteht dieses wunderschöne 3D-Bild, das nicht nur das Auge erfreut, sondern Sie auch darĂŒber nachdenken lĂ€sst, wie und warum es funktioniert. Sie können diesen Code hier in Aktion
sehen (off. Site des Entwicklers dieses Wunders).