Pygame für 3D Ansicht?

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
gaston70
User
Beiträge: 8
Registriert: Donnerstag 21. Dezember 2017, 10:04

Hallo,

ich befasse mich erst seit ein paar Monaten mit Python.
Habe mich durch ein paar Anfängertutorials gearbeitet und nun mein erstes kleines Spiel programmiert.
Zur Orientierung. Ich habe mich mit den verschiedenen Listen, Dictionarys usw. befasst. Was ich noch überhaupt nicht genutzt habe, sind diese Funktionen für Klassen und Objekte. Diese habe ich im Programm gar nicht genutzt.
Das Spielfeld basiert auf einer Liste.

Liste = [[stein1],[stein2],.... usw.]
Eine weitere Liste beinhaltet die Koordinaten der einzelnen Spielfelder
In der jetzigen Form gibt man ein Feld ein und Spieler Rot zieht seinen Stein1 auf ein angrenzendes Feld.
Soweit funktioniert alles. Man kann zu zweit Spielen.

Nächster Schritt war nun mein erstes Programm mit PyGame.
Spielfeld 6x6
18 Rote Steine
18 Blaue Steine
Geht mit PyGame ja sehr einfach.
Und jedes Mal wenn ich ziehe, wird das Spielfeld aktualisiert. Die Information erhält die PyGame - Funktion auch über eine Liste, mit der ich sozusagen eine Draufsicht des aktuellen Spielfelds erstelle.

Was ich nun aber benötige ist eine Art 3D - Ansicht. Ich denke auch dran, dass Spielfeld drehen zu können.
Wenn man sich zu diesem Thema umschaut, kommt als jede 2. Antwort Blender.

Wie könnte man eine 3D Ansicht lösen?
Die Spielsteine sollen aussehen wie bei Dame.
Im späteren Verlauf will ich natürlich weg von der Konsoleneingabe und die einzelnen Felder mit der Maus anklicken.
Der Zug soll evtl. animiert dargestellt werden.
Auch das machen von Gefangenen soll animiert werden. Im Grunde muss das gesamte Spielfeld gezeichnet werden.
Aber ich weiß nicht, wie ich mit PyGame etwas in dieser Art darstellen soll.

https://goo.gl/images/WNbEnJ
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@gaston70: Du solltest Dir die andere Hälfte der Antworten anschauen. Eine (zwar etwas ältere) Übersicht über verschiedene Engines ist hier: http://codeboje.de/2d-and-3d-game-and-r ... es-python/.
gaston70
User
Beiträge: 8
Registriert: Donnerstag 21. Dezember 2017, 10:04

Danke für den Link.

Aber ich muss meine Frage wiederholen. Mit welchem Tool würdet ihr arbeiten?

Wie sieht es denn mit VPython aus?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn es um isometrie geht (das reicht für die Darstellung eines Spielplatzes aus),dann reicht pygame vollkommen aus.

Wenn du ernsthaft 3-D Programmierung betreiben möchtest, würde ich zu unity raten. Damit hast du dann aber C# oder JavaScript als Sprachen, und kein Python mehr.
gaston70
User
Beiträge: 8
Registriert: Donnerstag 21. Dezember 2017, 10:04

Code: Alles auswählen

import pygame, sys
from pygame.locals import *

Felder = [['rot'],
         ['rot'],
         ['blau'],
         ['blau'],
         ['rot'],
         ['rot'],
         ['blau'],
         ['blau',],
         ['rot'],
         ['rot'],
         ['blau'],
         ['blau'],
         ['rot'],
         ['rot'],
         ['blau'],
         ['blau'],
         ['rot'],
         ['rot'],
         ['blau'],
         ['blau'],
         ['rot'],
         ['rot'],
         ['blau'],
         ['blau'],
         ['rot'],
         ['rot'],
         ['blau'],
         ['blau'],
         ['rot'],
         ['rot'],
         ['blau'],
         ['blau'],
         ['rot'],
         ['rot'],
         ['blau'],
         ['blau']]

def Liste_Spielfeld():
    Spielfeld = []
    for i in range(len(Felder)):
        if len(Felder[i]) == 0:
            Spielfeld.append([])
        else:
            Spielfeld.append(Felder[i][-1])
    return Spielfeld

x = 50
y = 50
i_spielfeld = 0

s = Liste_Spielfeld()

pygame.init()
DISPLAYSURF = pygame.display.set_mode((800,600))
pygame.display.set_caption("Focus")
DISPLAYSURF.fill([0,0,0])
radius = (40)
pos = (x,y)
while i_spielfeld < 36:

    for y in range(50, 540, 90):
        for x in range(50, 540, 90):

            DISPLAYSURF.lock()
            if "rot" in s[i_spielfeld]:
                farbe=(255, 0, 0)
            else:
                farbe=(0, 255, 0)
            pos=(x, y)
            pygame.draw.circle(DISPLAYSURF, farbe, pos, radius)

            i_spielfeld=i_spielfeld + 1

DISPLAYSURF.unlock()
pygame.display.flip()

aktiv = True
while aktiv:
    for event in pygame.event.get():
        if event.type == QUIT:
            aktiv = False
pygame.quit()
sys.exit()
Hier, was ich auf die schnelle als erstes mit pygame gemacht habe. Nach jedem Zug wird die Liste des Spielfeldes neu erstellt.
In dieser Liste bilde ich eine Draufsicht des aktuellen Spielfelds ab. Nun ziehen wir z.B. von Index 0 auf Index 1 (roter Stein).
Dadurch (ist in einer anderen Funktion gelöst, taucht in diesem Beispiel nicht auf) ändert sich die Liste "Felder".

Felder = [[],[rot.rot],[blau] usw.

D. h., für die Iso-Ansicht werde ich die Funktion Liste_Spielfeld nicht mehr benötigen. Dafür werde ich die Liste Felder zum Aufbau der einzelnen Ebenen nutzen. Wobei es eine max. Höhe von 5 geben kann.

Nur ich finde nicht so wirklich den Ansatz, um aus den Kreisen eine Iso-Ansicht zu machen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich wuerde mir das gehummse mit Liste_Spielfeld sparen. Das ist eh eine schlecht geschriebene Funktion, weil

- sie globalen Zustand (Felder) braucht, statt das uebergeben zu bekommen
- umstaendlich auf Eintraege in Felder zugreift, statt einfach

Code: Alles auswählen

for eintrag in Felder:
zu machen
- deine Darstellung genauso gut auch mit Felder klarkommen kann.

Ganz generell ist deine Felder-Datenstruktur schlecht gewaehlt. Dein Spielfeld ist offensichtlich zweidimensional. Das sollte dann auch die Datenstruktur sein. Damit kannst du dann deutlich einfacher arbeiten. Und fuer die Isometrie ist nur notwendig, das du mit Vektoren statt eindimensionalen Schritten arbeitest.

Ein durcharbeiten von PEP8 zu coding-conventions in Python waere auch gut, dein Code ist so *sehr* unuebersichlich.

Code: Alles auswählen

# outer entries are rows, inner columns
play_field = [ 
    ["rot", "blau"], # first row
    ["blau", "rot"],
]

# CAPTIAL names are CONSTANTS! Not variables!
ROW_STEP = (20, 10) 
COL_STEP = (20, -10)

def render_play_field(play_field, start_pos):
      sx, sy = start_pos
      for row_index, row in enumerate(play_field):
            for col_index, color in enumerate(row):
                 x = sx + row_index * ROW_STEP[0] + col_index * COL_STEP[0]
                 y = sy  + row_index * ROW_STEP[1]  + col_index * COL_STEP[1]
                 draw_tile(x, y, color) # homework
gaston70
User
Beiträge: 8
Registriert: Donnerstag 21. Dezember 2017, 10:04

Habe das mal getestet.

Code: Alles auswählen

import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((800,600))
pygame.display.set_caption("Focus")
DISPLAYSURF.fill([0,0,0])
radius = (40)
# outer entries are rows, inner columns
play_field=[["rot"],["rot"],["blau"],["blau"],["rot"],["rot"],
            ["blau"],["blau"],["rot"],["rot"],["blau"],["blau"],
            ["rot"],["rot"],["blau"],["blau"],["rot"],["rot"],
            ["blau"],["blau"],["rot"],["rot"],["blau"],["blau"],
            ["rot"],["rot"],["blau"],["blau"],["rot"],["rot"],
            ["blau"],["blau"],["rot"],["rot"],["blau"],["blau"]]

# CAPTIAL names are CONSTANTS! Not variables!
ROW_STEP=(50, 20)
COL_STEP=(50, -20)
i_spielfeld = 0

def render_play_field(play_field, sx, sy):

    for row_index, row in enumerate(play_field):
        for col_index, color in enumerate(row):
            x=sx + row_index * ROW_STEP[0] + col_index * COL_STEP[0]
            y=sy + row_index * ROW_STEP[1] + col_index * COL_STEP[1]
            if "rot" in color:
                farbe=(255, 0, 0)
            else:
                farbe=(0, 255, 0)
            pos=(x, y)
            pygame.draw.circle(DISPLAYSURF, farbe, pos, radius)  # homework

while i_spielfeld < 36:
    render_play_field(play_field,50,100)
    i_spielfeld=i_spielfeld + 1

DISPLAYSURF.unlock()
pygame.display.flip()

aktiv = True
while aktiv:
    for event in pygame.event.get():
        if event.type == QUIT:
            aktiv = False
pygame.quit()
sys.exit()
Bin natürlich begeistert von enumerate :D !!.
Aber irgendwie stimmt etwas noch nicht.
Vielleicht nochmal ein Wort zu der Liste Felder oder wie in deinem Programm play_field.
Das stellt Ebene 0 dar. Beim Spielen kann es in z also bis zu 5 Ebenen geben.
Stellt sich dann z.B. wie folgt dar.

play_field = [["rot","blau","rot"],["rot"],[],["rot","rot","rot","blau","blau"]]
Feld 0, Turm 3 hoch,Feld 1 Turm 1 hoch, Feld 2 leer, Feld 3 Turm 5 hoch. Usw.

Im obigen Beispiel ist auf Feld 0 der Spieler rot im Besitz des Turms. Auf Feld 3 ist es Spieler blau.
Wenn nun rot am Zug ist, kann er auf Feld 1,2 oder 3 ziehen.
Im Falle von Feld 3 sieht play_field danach so aus.

play_field = [[],["rot"],[],["blau","blau","rot","blau","rot"]]

Spieler rot hat ["rot","rot","rot"] als Reservesteine gewonnnen.
Bin mir nicht sicher, ob dein Lösungsansatz das berücksichtigt.
Ich denke, da muss in jedem Fall noch in z eine weitere Schleife eingebunden werden.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich kannte ja bis gerade nicht die Spielregeln. Natuerlich kann da mit einem Feld noch anders vorgegangen werden. Wenn das ein Stapel ist, dann malst du pro Kachel eben einen Stapel aus den notwendigen Dingen - Bodenplatte, und von unten nach oben die Spielsteine. Das ist alles relativ zur Position der gesamten Kachel zu tun, aber wirklich kein Hexenwerk.
gaston70
User
Beiträge: 8
Registriert: Donnerstag 21. Dezember 2017, 10:04

Zwar noch ohne enumerate 8) .

Code: Alles auswählen

import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((800,600))
pygame.display.set_caption("Focus")
DISPLAYSURF.fill([0,0,0])
radius = (40)
# outer entries are rows, inner columns
play_field=[["rot"],["rot"],["blau"],["blau"],["rot"],["rot"],
            ["blau"],["blau"],["rot"],["rot"],["blau"],["blau"],
            ["rot"],["rot"],["blau"],["blau"],["rot"],["rot"],
            ["blau"],["blau"],["rot"],["rot"],["blau"],["blau"],
            ["rot"],["rot"],["blau"],["blau"],["rot"],["rot"],
            ["blau"],["blau"],["rot"],["rot"],["blau"],["blau"]]

# CAPTIAL names are CONSTANTS! Not variables!
ROW_STEP=(50, 20)
COL_STEP=(50, -20)
i_spielfeld = 0

def render_play_field(play_field, sx, sy):

    for row_index, row in enumerate(play_field):
        for col_index, color in enumerate(row):
            x=sx + row_index * ROW_STEP[0] + col_index * COL_STEP[0]
            y=sy + row_index * ROW_STEP[1] + col_index * COL_STEP[1]
            if "rot" in color:
                farbe=(255, 0, 0)
            else:
                farbe=(0, 255, 0)
            pos=(x, y)
            pygame.draw.circle(DISPLAYSURF, farbe, pos, radius)  # homework

while i_spielfeld < 36:
    render_play_field(play_field,50,100)
    i_spielfeld=i_spielfeld + 1

DISPLAYSURF.unlock()
pygame.display.flip()

aktiv = True
while aktiv:
    for event in pygame.event.get():
        if event.type == QUIT:
            aktiv = False
pygame.quit()
sys.exit()
Schleife für Z-Achse hinzugefügt.

Wie schon geschrieben entfällt dadurch natürlich die Funktion Liste_Spielfeld. Ist natürlich ein Vorteil weil ich nur noch die Liste play_field nutzen muss. Aber mit meiner eigentlichen Frage, wie ich den nun aus der 2D Ansicht möglichst simpel eine ISO-Ansicht oder halt eine3D Ansicht zeichne,
läßt sich damit leider nicht beantworten.
Ich habe überlegt, ob ich einzelne Spielsteine als Image einlese und damit sozusagen das Spielfeld male.
Mit Blender scheint es ja weitreichende Möglichkeiten zu geben. Aber ich bin noch auf der Suche nach einem Tip, wie ich aus meinem Script ein 3D Spielfeld mit beliebigen Kameraeinstellungen und den dazugehörigen Spielsteinen zaubern kann.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also das sieht nicht richtig aus, du hast ja wieder eine eindimensionale Struktur. Und ja, zum malen von solchen Feldern benutzt man ueblicherweise Bilder.

Aber wenn du komplette 3D-Freiheitsgrade willst, dann reicht das eh nicht. Dann muss es halt etwas wie Blender sein. Oder eben Unity, mir persoenlich gefaellt das fuer Spiele deutlich besser - Blender habe ich mich nie so zu recht gefunden. So oder so ist da viel lernen angesagt.
gaston70
User
Beiträge: 8
Registriert: Donnerstag 21. Dezember 2017, 10:04

Der Code macht genau das was er soll.
Wie würdest du eine mehrdimensionale Liste anlegen?
Ich hatte das ursprünglich mal versucht.

Ebenen = [[rot,rot,blau,blau,rot,rot], # Ebene 0, im Spiel natürlich als 6x6 Matrix
[1,1,1,1,1,1], # Ebene 1
[Ebene 2 usw. bis 5

Hat sich aber für meine Zwecke als viel zu aufwendig dargestellt.
So bilde ich einfach mit einer Liste alle Spielfelder ab und nutze die Länge des einzelnen Index in der Liste um die Turmhöhe zu ermitteln.

Aber wenn hier jemand zufällig ein Damespiel oder etwas ähnliches programmiert hat und dazu evtl. eine passende isometrische Ansicht verwendet hat kann er mir vielleicht ein paar Tips geben.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na, dein Spielfeld ist zweidimensional (3, wenn wir die gestapelten Steine mitberücksichtigen). Und das ist auch die dafür geeignetste Datenstruktur, mit der zB die Bewegung einfach ausdrückbar ist.

Wenn du dir das nicht zutraust das selbst hinzubekommen, schau dir tile engines für pygame an.
Antworten