3D-Bild in Python mit (fast) normaler Leistung

Dieser Artikel kann als Antwort auf diesen Artikel betrachtet werden , bei dem es darum geht, so etwas in C ++ zu schreiben, das sich an Anfänger richtet, dh mit Schwerpunkt auf einfach lesbarem Code anstelle von hoher Leistung.

Nachdem ich den Artikel gelesen hatte, kam mir die Idee, das vom Autor geschriebene Programm zu wiederholen. Ich bin mit C ++ vertraut, habe aber nie komplizierte Programme darauf geschrieben und Python bevorzugt. Hier wurde die Idee geboren, darauf zu schreiben. Ich war besonders an der Leistung interessiert - ich war mir fast sicher, dass ein paar Bilder pro Sekunde die Grenze für Python sind. Ich lag falsch.

Bild

Bild

Der erste Versuch , finden sich hier . Hier wird der Code vollständig geschrieben, ohne Berücksichtigung von Sprachunterschieden gemäß dem vonHaqreu. Und aus diesem Grund ist das Rendering O (n ^ 2) - im Wesentlichen verschachtelte Schleifen in Winkel und Abstand:

         # iterating alpha
        alpha = player.view - player.fov / 2
        mapFB.drawRectangle(player.y - 1, player.x - 1, player.y + 1, player.x + 1, Color(255, 0, 0))
        rayNum = 0
        while alpha < player.fov / 2 + player.view:
            # iterating distance
            dist = 0
            x = player.x
            y = player.y
            while 0 < x < mapFB.w - 1 and 0 < y < mapFB.h - 1:
                  ...

Aus diesem Grund ist der Code ziemlich langsam (ich habe es geschafft, auf dem Intel Core i5 der 8. Generation weniger als 3-4 Bilder pro Sekunde zu erhalten).

Eine naheliegende Möglichkeit, die Dinge zu beschleunigen und den Code nicht zu komplizieren, besteht darin, die innere Schleife durch Operationen mit linearer Komplexität zu ersetzen. Betrachten wir alles aus mathematischer Sicht: Wir müssen den Schnittpunkt des Strahls bestimmen, der durch die Koordinaten des Spielers und den Blickwinkel gegeben ist, und den Block, der durch die Koordinaten und die Größe angegeben wird (der Einfachheit halber konstant). Als nächstes müssen Sie die nächste Überweisung auswählen und zurücksenden. Unten ist der entsprechende Code (der vollständige Code ist hier ):

def block_cross(i, j, k, y_0, alpha, player):
    # cell coordinates
    x_cell = i * H
    y_cell = j * H
    # find collision points
    collisions = []

    if k != 0:
        x = (y_cell - y_0) / k
        y = y_cell
        if x_cell <= x <= x_cell + H and (x - player.x) / cos(alpha) < 0:
            collisions.append((x, y))

    if k != 0:
        x = (y_cell + H - y_0) / k
        y = y_cell + H
        if x_cell <= x <= x_cell + H and (x - player.x) / cos(alpha) < 0:
            collisions.append((x, y))

    x = x_cell
    y = y_0 + x * k
    if y_cell <= y <= y_cell + H and (x - player.x) / cos(alpha) < 0:
        collisions.append((x, y))

    x = x_cell + H
    y = y_0 + (x) * k
    if y_cell <= y <= y_cell + H and (x - player.x) / cos(alpha) < 0:
        collisions.append((x, y))

    # select the closest collision for the block
    dist = 1000 * H
    x = None
    y = None
    for collision in collisions:
        tmp = sqrt((collision[0] - player.x) ** 2 + (collision[1] - player.y) ** 2)
        if tmp < dist:
            dist = tmp;
            x = collision[0]
            y = collision[1]

    return x, y, dist

Eine solche triviale Änderung von 10 Zeilen ergibt eine Beschleunigung von mehr als dem Doppelten, dh etwa 5 bis 6 Bildern pro Sekunde. Das sind keine Idioten mehr, sondern ein bewegtes Bild, aber immer noch ziemlich langsam.
Auf der Suche nach Ideen zur Beschleunigung des Codes bin ich auf Cython gestoßen . Kurz gesagt, dies ist ein Projekt, mit dem Sie Python-Code erheblich beschleunigen können, ohne ihn ernsthaft zu ändern. Kurz über ihn - unter dem Spoiler.

tyts
 cpdef int fun(int num, float val):
    cdef int result
    # do some stuff
    return result

result int( int, , ). , . python-, cdef — python, cython. , python — , :

 cpdef int fun(int num, float val):
    cdef int result
    # do some stuff
    return result

 cdef int fun2(int *arr, float* arr_2):
    cdef int arr_3[10][10]
    # do some stuff
    return result

fun2 python, - fun — .

Cython gab eine gewisse Beschleunigung, wenn auch unbedeutend - nur nicht ein paar Bilder pro Sekunde. In relativen Zahlen ist dies jedoch nicht so klein - 8-9 Bilder pro Sekunde, das sind + 40% für die beste Option in Python und + 200% für die Option mit einem naiven Algorithmus. Dies ist immer noch ein sehr nervöses Bild, aber für Bildungszwecke normal. Am Ende ist es unser Ziel, es selbst zu schreiben und den Prozess zu genießen, aber für ein echtes Spiel ist es einfacher, eine Bibliothek wie Pygame zu nehmen oder Python generell zu verschieben und etwas passenderes zu nehmen.

PS Es wäre interessant, andere Optionen zur Optimierung des Codes in den Kommentaren zu sehen.

Source: https://habr.com/ru/post/undefined/


All Articles